package core import ( "crypto/hmac" "crypto/sha1" "encoding/base64" "encoding/json" "io" "net/http" "net/url" "strconv" "strings" "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" ) func (mg *Maingate) platform_twitter_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(AuthPlatformTwitter, existid, browserinfo) if err == nil { if info.token != "" { params := url.Values{} params.Add("id", existid) params.Add("authtype", AuthPlatformTwitter) http.Redirect(w, r, "actionsquare://login?"+params.Encode(), http.StatusSeeOther) return } } } sessionkey := mg.GeneratePlatformLoginNonceKey() nonce := mg.GeneratePlatformLoginNonceKey() auth_token, auth_secret := parse_TwitterOAuthToken(mg.CallTwitterAPI_WithAPPKey("https://api.twitter.com/oauth/request_token", "POST", nonce)) if auth_token == "" || auth_secret == "" { w.WriteHeader(http.StatusBadRequest) } else { mg.mongoClient.Delete(CollectionPlatformLoginToken, bson.M{ "platform": AuthPlatformTwitter, "key": sessionkey, }) _, _, err := mg.mongoClient.Update(CollectionPlatformLoginToken, bson.M{ "_id": primitive.NewObjectID(), }, bson.M{ "$setOnInsert": bson.M{ "platform": AuthPlatformTwitter, "key": sessionkey, "token": auth_token, "secret": auth_secret, "nonce": nonce, "brinfo": browserinfo, }, }, options.Update().SetUpsert(true)) if err != nil { w.WriteHeader(http.StatusBadRequest) logger.Error(err) return } } // 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) params := url.Values{} params.Add("oauth_token", auth_token) http.Redirect(w, r, "https://api.twitter.com/oauth/authorize?"+params.Encode(), http.StatusSeeOther) } func (mg *Maingate) platform_twitter_authorize(w http.ResponseWriter, r *http.Request) { // defer r.Body.Close() // body, _ := io.ReadAll(r.Body) // bodyString := string(body) // oauth_token := r.URL.Query().Get("oauth_token") // oauth_verifier := r.URL.Query().Get("oauth_verifier") // fmt.Println("bodyString") // fmt.Println(bodyString) // fmt.Println("=======================") // fmt.Println("Req: %s %s", r.URL.Host, r.URL.Path) // fmt.Println(oauth_token, oauth_verifier) // fmt.Println("=======================") // set cookie for storing token cookie := http.Cookie{ Name: "LoginFlowContext_code", Value: r.URL.Query().Encode(), 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/"+AuthPlatformTwitter, http.StatusSeeOther) //-- 바로 받으니까 쿠키 안와서 한번 더 Redirect 시킨다. } func (mg *Maingate) platform_twitter_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": AuthPlatformTwitter, }) 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 } urlvalue, err := url.ParseQuery(code) if err != nil { logger.Println("Token parse error") w.WriteHeader(http.StatusBadRequest) return } //=== oauth_token := urlvalue.Get("oauth_token") oauth_verifier := urlvalue.Get("oauth_verifier") if oauth_token == "" || oauth_verifier == "" { w.WriteHeader(400) return } userid, token, secret := getTwitterAccessToken("https://api.twitter.com/oauth/access_token?oauth_token=" + oauth_token + "&oauth_verifier=" + oauth_verifier) if userid != "" && token != "" && secret != "" { var info usertokeninfo info.platform = AuthPlatformTwitter info.userid = userid info.token = token info.secret = secret info.brinfo = brinfo mg.setUserToken(info) params := url.Values{} params.Add("id", userid) params.Add("authtype", AuthPlatformTwitter) http.Redirect(w, r, "actionsquare://login?"+params.Encode(), http.StatusSeeOther) } else { http.Redirect(w, r, "actionsquare://error", http.StatusSeeOther) } } func (mg *Maingate) platform_twitter_getuserinfo(token, secret string) (bool, string, string) { result := mg.CallTwitterAPI("https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true", "GET", token, secret, mg.GeneratePlatformLoginNonceKey()) var TwitterUserInfo struct { Id string `json:"id_str"` Name string `json:"name"` Username string `json:"screen_name"` Email string `json:"email"` } err := json.Unmarshal([]byte(result), &TwitterUserInfo) if err != nil { logger.Error("twitter userinfo api unmarshal fail :", result) return false, "", "" } // fmt.Println("=====================") // fmt.Println(result) // fmt.Println(TwitterUserInfo.Id) // fmt.Println(TwitterUserInfo.Name) // fmt.Println(TwitterUserInfo.Username) // fmt.Println(TwitterUserInfo.Email) // fmt.Println("=====================") return true, TwitterUserInfo.Id, TwitterUserInfo.Email } func (mg *Maingate) CallTwitterAPI_WithAPPKey(requesturl, method, nonce string) string { return mg.CallTwitterAPI(requesturl, method, config.TwitterOAuthKey, config.TwitterOAuthSecret, nonce) } func (mg *Maingate) CallTwitterAPI(requesturl, method, oauth_token, oauth_secret, nonce string) string { vals := url.Values{} if method == "GET" { splited := strings.Split(requesturl, "?") if len(splited) > 1 { parameter := splited[1] args := strings.Split(parameter, "&") for _, arg := range args { tempsplited := strings.Split(arg, "=") if len(tempsplited) == 2 { vals.Add(tempsplited[0], tempsplited[1]) } } } } //vals.Add("oauth_callback", "actionclient://callback") //vals.Add("oauth_callback", "http://127.0.0.1:7770/auth") vals.Add("oauth_callback", config.RedirectBaseUrl+"/authorize/"+AuthPlatformTwitter) vals.Add("oauth_consumer_key", config.TwitterCustomerKey) vals.Add("oauth_token", oauth_token) vals.Add("oauth_signature_method", "HMAC-SHA1") vals.Add("oauth_timestamp", strconv.Itoa(int(time.Now().Unix()))) vals.Add("oauth_nonce", nonce) vals.Add("oauth_version", "1.0") parameterString := strings.Replace(vals.Encode(), "+", "%20", -1) signatureBase := strings.ToUpper(method) + "&" + url.QueryEscape(strings.Split(requesturl, "?")[0]) + "&" + url.QueryEscape(parameterString) signingKey := url.QueryEscape(config.TwitterCustomerSecret) + "&" + url.QueryEscape(oauth_secret) signature := calculateTwitterSignature(signatureBase, signingKey) headerString := "OAuth oauth_callback=\"" + url.QueryEscape(vals.Get("oauth_callback")) + "\", oauth_consumer_key=\"" + url.QueryEscape(vals.Get("oauth_consumer_key")) + "\", oauth_nonce=\"" + url.QueryEscape(vals.Get("oauth_nonce")) + "\", oauth_signature=\"" + url.QueryEscape(signature) + "\", oauth_signature_method=\"" + url.QueryEscape(vals.Get("oauth_signature_method")) + "\", oauth_timestamp=\"" + url.QueryEscape(vals.Get("oauth_timestamp")) + "\", oauth_token=\"" + url.QueryEscape(vals.Get("oauth_token")) + "\", oauth_version=\"" + url.QueryEscape(vals.Get("oauth_version")) + "\"" client := &http.Client{} req, err := http.NewRequest(method, requesturl, nil) if err != nil { panic(err) } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Content-Length", "0") req.Header.Add("Accept-Encoding", "application/json") req.Header.Add("Host", "api.twitter.com") req.Header.Add("Accept", "*/*") req.Header.Add("Authorization", headerString) resp, err := client.Do(req) if err != nil { logger.Error(err) } defer resp.Body.Close() strBody := "" respBody, err := io.ReadAll(resp.Body) if err == nil { strBody = string(respBody) } if resp.StatusCode != 200 { logger.Error("Error: ", resp.StatusCode, err, strBody) return "error" } return strBody } func getTwitterAccessToken(requesturl string) (string, string, string) { client := &http.Client{} req, err := http.NewRequest("POST", requesturl, nil) if err != nil { panic(err) } req.Header.Add("Content-Type", "application/x-www-form-urlencoded") req.Header.Add("Content-Length", "0") req.Header.Add("Accept-Encoding", "application/json") req.Header.Add("Host", "api.twitter.com") req.Header.Add("Accept", "*/*") resp, err := client.Do(req) if err != nil { logger.Error(err) return "", "", "" } defer resp.Body.Close() strBody := "" respBody, err := io.ReadAll(resp.Body) if err == nil { strBody = string(respBody) } if resp.StatusCode != 200 { logger.Error("Error: ", resp.StatusCode, err, strBody) return "", "", "" } params := strings.Split(strBody, "&") oauth_token := "" oauth_secret := "" user_id := "" //screen_name := "" for _, param := range params { parsedStr := strings.Split(param, "=") if len(parsedStr) == 2 { if parsedStr[0] == "oauth_token" { oauth_token = parsedStr[1] } if parsedStr[0] == "oauth_token_secret" { oauth_secret = parsedStr[1] } if parsedStr[0] == "user_id" { user_id = parsedStr[1] } // if parsedStr[0] == "screen_name" { // screen_name = parsedStr[1] // } } } // ret := "" // if oauth_token != "" && oauth_secret != "" && user_id != "" && screen_name != "" { // ret = "{ \"AuthToken\": \"" + oauth_token + "\", \"UserId\": \"" + user_id + "\", \"Screen_name\": \"" + screen_name + "\" }" // } // return ret, user_id, oauth_token, oauth_secret return user_id, oauth_token, oauth_secret } func calculateTwitterSignature(base, key string) string { hash := hmac.New(sha1.New, []byte(key)) hash.Write([]byte(base)) signature := hash.Sum(nil) return base64.StdEncoding.EncodeToString(signature) } func parse_TwitterOAuthToken(strBody string) (string, string) { params := strings.Split(strBody, "&") oauth_token := "" oauth_secret := "" for _, param := range params { parsedStr := strings.Split(param, "=") if len(parsedStr) == 2 { if parsedStr[0] == "oauth_token" { oauth_token = parsedStr[1] } if parsedStr[0] == "oauth_token_secret" { oauth_secret = parsedStr[1] } } } return oauth_token, oauth_secret }