Files
maingate/core/platformmicrosoft.go

341 lines
10 KiB
Go

package core
import (
"bytes"
"encoding/json"
"io"
"net/http"
"net/url"
"time"
"repositories.action2quare.com/ayo/gocommon/logger"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
type Microsoft_ValidationResponse struct {
IDToken string `json:"id_token"`
RefreshToken string `json:"refresh_token"`
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
}
type Microsoft_UserInfoResponse struct {
Sub string `json:"sub"`
Givenname string `json:"givenname"`
Familyname string `json:"familyname"`
Email string `json:"email"`
Locale string `json:"locale"`
}
func (mg *Maingate) platform_microsoft_get_login_url(w http.ResponseWriter, r *http.Request) {
browserinfo, err := mg.GetUserBrowserInfo(r)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
logger.Error(err)
return
}
existid := r.URL.Query().Get("existid")
//fmt.Println("existid =>", existid)
if existid != "" {
//기존 계정이 있는 경우에는 그 계정 부터 조회한다.
info, err := mg.getUserTokenWithCheck(AuthPlatformMicrosoft, existid, browserinfo)
if err == nil {
if info.token != "" {
params := url.Values{}
params.Add("id", existid)
params.Add("authtype", AuthPlatformMicrosoft)
http.Redirect(w, r, "actionsquare://login?"+params.Encode(), http.StatusSeeOther)
return
}
}
}
sessionkey := mg.GeneratePlatformLoginNonceKey()
nonce := mg.GeneratePlatformLoginNonceKey()
mg.mongoClient.Delete(CollectionPlatformLoginToken, bson.M{
"platform": AuthPlatformMicrosoft,
"key": sessionkey,
})
_, _, err = mg.mongoClient.Update(CollectionPlatformLoginToken, bson.M{
"_id": primitive.NewObjectID(),
}, bson.M{
"$setOnInsert": bson.M{
"platform": AuthPlatformMicrosoft,
"key": sessionkey,
"nonce": nonce,
"brinfo": browserinfo,
},
}, options.Update().SetUpsert(true))
if err != nil {
w.WriteHeader(http.StatusBadRequest)
logger.Error(err)
return
}
params := url.Values{}
params.Add("client_id", config.MicrosoftClientId)
params.Add("response_type", "code")
params.Add("redirect_uri", config.RedirectBaseUrl+"/authorize/"+AuthPlatformMicrosoft)
params.Add("response_mode", "query")
params.Add("scope", "openid offline_access https://graph.microsoft.com/mail.read")
params.Add("nonce", nonce)
// set cookie for storing token
cookie := http.Cookie{
Name: "LoginFlowContext_SessionKey",
Value: sessionkey,
Expires: time.Now().Add(1 * time.Hour),
//SameSite: http.SameSiteStrictMode,
SameSite: http.SameSiteLaxMode,
// HttpOnly: false,
Secure: true,
Path: "/",
}
http.SetCookie(w, &cookie)
//Set-Cookie
//fmt.Println(mg.authorizationEndpoints[AuthPlatformMicrosoft] + params.Encode())
//http.Redirect(w, r, "https://appleid.apple.com/auth/authorize?"+params.Encode(), http.StatusSeeOther)
http.Redirect(w, r, mg.authorizationEndpoints[AuthPlatformMicrosoft]+"?"+params.Encode(), http.StatusSeeOther)
}
func (mg *Maingate) platform_microsoft_authorize(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
code := r.URL.Query().Get("code")
// set cookie for storing token
cookie := http.Cookie{
Name: "LoginFlowContext_code",
Value: code,
Expires: time.Now().Add(1 * time.Minute),
SameSite: http.SameSiteLaxMode,
Secure: true,
Path: "/",
}
http.SetCookie(w, &cookie)
http.Redirect(w, r, config.RedirectBaseUrl+"/authorize_result/"+AuthPlatformMicrosoft, http.StatusSeeOther) //-- 바로 받으니까 쿠키 안와서 한번 더 Redirect 시킨다.
}
func (mg *Maingate) platform_microsoft_authorize_result(w http.ResponseWriter, r *http.Request) {
brinfo, err := mg.GetUserBrowserInfo(r)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
logger.Error(err)
return
}
cookie, err := r.Cookie("LoginFlowContext_SessionKey")
if err != nil {
logger.Println("Session not found", err)
w.WriteHeader(http.StatusBadRequest)
return
}
cookiecode, err := r.Cookie("LoginFlowContext_code")
if err != nil {
logger.Println("code not found", err)
w.WriteHeader(http.StatusBadRequest)
return
}
code := cookiecode.Value
found, err := mg.mongoClient.FindOne(CollectionPlatformLoginToken, bson.M{
"key": cookie.Value,
"platform": AuthPlatformMicrosoft,
})
if err != nil {
logger.Println("LoginFlowContext_SessionKey find key :", err)
w.WriteHeader(http.StatusBadRequest)
return
}
if found == nil {
logger.Println("LoginFlowContext_SessionKey not found")
w.WriteHeader(http.StatusBadRequest)
return
}
if cookie.Value != found["key"] {
logger.Println("LoginFlowContext_SessionKey key not match")
logger.Println(cookie.Value)
logger.Println(found["key"])
w.WriteHeader(http.StatusBadRequest)
return
}
if brinfo != found["brinfo"] { //-- 로그인 시작점과 인증점의 브라우저 혹은 접속지 정보가 다르다?
logger.Println("LoginFlowContext_SessionKey brinfo not match ")
logger.Println(brinfo)
logger.Println(found["brinfo"])
w.WriteHeader(http.StatusBadRequest)
return
}
//=================
params := url.Values{}
params.Add("client_id", config.MicrosoftClientId)
params.Add("redirect_uri", config.RedirectBaseUrl+"/authorize/"+AuthPlatformMicrosoft)
params.Add("code", code)
params.Add("scope", "openid offline_access https://graph.microsoft.com/mail.read")
params.Add("grant_type", "authorization_code")
params.Add("client_secret", config.MicrosoftClientSecret)
var respReferesh Microsoft_ValidationResponse
acceestoken_expire_time := time.Now().Unix()
content := params.Encode()
resp, _ := http.Post(mg.tokenEndpoints[AuthPlatformMicrosoft], "application/x-www-form-urlencoded", bytes.NewBuffer([]byte(content)))
if resp != nil {
body, _ := io.ReadAll(resp.Body)
// w.Write(body)
//json.NewDecoder(resp.Body).Decode(respReferesh)
json.Unmarshal(body, &respReferesh)
} else {
logger.Println("tokenEndpoints fail.")
w.WriteHeader(http.StatusBadRequest)
return
}
// fmt.Println("==============================")
// fmt.Println("IDToken:", respReferesh.IDToken)
// fmt.Println("AccessToken:", respReferesh.AccessToken)
// fmt.Println("ExpiresIn:", respReferesh.ExpiresIn)
// fmt.Println("RefreshToken:", respReferesh.RefreshToken)
// fmt.Println("TokenType:", respReferesh.TokenType)
// fmt.Println("==============================")
acceestoken_expire_time = acceestoken_expire_time + (int64(respReferesh.ExpiresIn) * 3 / 4) //--- 3/4 이상 지나면 업데이트 한다. ms는 보통 3600초 1시간
userid, _, nonce := JWTparseCode(mg.jwksUri[AuthPlatformMicrosoft], respReferesh.IDToken)
if nonce == "" || nonce != found["nonce"] {
logger.Errorf("nonce not match")
return
}
if userid != "" {
var info usertokeninfo
info.platform = AuthPlatformMicrosoft
info.userid = userid
info.token = respReferesh.RefreshToken
info.brinfo = brinfo
info.accesstoken = respReferesh.AccessToken
info.accesstoken_expire_time = acceestoken_expire_time
mg.setUserToken(info)
params := url.Values{}
params.Add("id", userid)
params.Add("authtype", AuthPlatformMicrosoft)
http.Redirect(w, r, "actionsquare://login?"+params.Encode(), http.StatusSeeOther)
} else {
http.Redirect(w, r, "actionsquare://error", http.StatusSeeOther)
}
}
func (mg *Maingate) platform_microsoft_getuserinfo(info usertokeninfo) (bool, string, string) {
// fmt.Println(info.platform)
// fmt.Println(info.userid)
// fmt.Println(info.accesstoken)
// fmt.Println(time.Now().Unix())
// fmt.Println(info.accesstoken_expire_time)
//-- access token 갱신이 필요하다. -- userinfoEndpoint
if time.Now().Unix() > info.accesstoken_expire_time {
params := url.Values{}
params.Add("client_id", config.MicrosoftClientId)
params.Add("redirect_uri", config.RedirectBaseUrl+"/authorize/"+AuthPlatformMicrosoft)
params.Add("refresh_token", info.token)
params.Add("scope", "openid offline_access https://graph.microsoft.com/mail.read")
params.Add("grant_type", "refresh_token")
params.Add("client_secret", config.MicrosoftClientSecret)
var respReferesh Microsoft_ValidationResponse
acceestoken_expire_time := time.Now().Unix()
content := params.Encode()
resp, _ := http.Post(mg.tokenEndpoints[AuthPlatformMicrosoft], "application/x-www-form-urlencoded", bytes.NewBuffer([]byte(content)))
if resp != nil {
body, _ := io.ReadAll(resp.Body)
json.Unmarshal(body, &respReferesh)
// fmt.Println("==== accesstoken 업데이트 =====")
// fmt.Println("IDToken:", respReferesh.IDToken)
// fmt.Println("AccessToken:", respReferesh.AccessToken)
// fmt.Println("ExpiresIn:", respReferesh.ExpiresIn)
// fmt.Println("RefreshToken:", respReferesh.RefreshToken)
// fmt.Println("TokenType:", respReferesh.TokenType)
// fmt.Println("==============================")
acceestoken_expire_time = acceestoken_expire_time + (int64(respReferesh.ExpiresIn) * 3 / 4) //--- 3/4 이상 지나면 업데이트 한다. ms는 보통 3600초 1시간
userid, _, _ := JWTparseCode(mg.jwksUri[AuthPlatformMicrosoft], respReferesh.IDToken)
if userid != "" && info.userid == userid {
info.token = respReferesh.RefreshToken
info.accesstoken = respReferesh.AccessToken
info.accesstoken_expire_time = acceestoken_expire_time
mg.setUserToken(info) //-- accesstoken 업데이트
} else {
logger.Println("JWTparseCode fail.")
return false, "", ""
}
} else {
logger.Println("tokenEndpoints fail.")
return false, "", ""
}
}
//=================
req, _ := http.NewRequest("GET", mg.userinfoEndpoint[AuthPlatformMicrosoft], nil)
req.Header.Add("Authorization", "Bearer "+info.accesstoken)
client := http.Client{}
resp, err := client.Do(req)
if err != nil {
logger.Error("authorize query failed :", err)
return false, "", ""
}
defer resp.Body.Close()
var respUserInfo Microsoft_UserInfoResponse
body, _ := io.ReadAll(resp.Body)
json.Unmarshal(body, &respUserInfo)
// fmt.Println(string(body))
// fmt.Println(respUserInfo.Sub)
// fmt.Println(respUserInfo.Email)
if respUserInfo.Sub != info.userid {
logger.Println("userinfoEndpoint fail.")
return false, "", ""
}
return true, info.userid, respUserInfo.Email //?
}