Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add third platform login and modify login flow #51

Merged
merged 9 commits into from
Oct 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions api/v1/oauth_client_github.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package v1

import (
"context"
"fmt"
"io"
"net/http"

"github.com/NJUPT-SAST/sast-link-backend/config"
"github.com/NJUPT-SAST/sast-link-backend/endpoints"
"github.com/NJUPT-SAST/sast-link-backend/model/result"
"github.com/NJUPT-SAST/sast-link-backend/service"
"github.com/gin-gonic/gin"
"github.com/tidwall/gjson"
"golang.org/x/oauth2"
)

const (
// GitHub user info url
GithubUserInfoURL = "https://api.github.com/user"
)

var (
githubConf = oauth2.Config{
ClientID: config.Config.GetString("oauth.client.github.id"),
ClientSecret: config.Config.GetString("oauth.client.github.secret"),
RedirectURL: config.Config.GetString("oauth.client.github.redirect_url"),
Scopes: []string{},
Endpoint: endpoints.GitHub,
}
)

func OauthGithubLogin(c *gin.Context) {

// Create oauthState cookie
oauthState := GenerateStateOauthCookie(c.Writer)
url := githubConf.AuthCodeURL(oauthState)

fmt.Println("------")
fmt.Printf("Visit the URL for the auth dialog: %v\n", url)
fmt.Println("------")

c.Redirect(http.StatusFound, url)
}

func OauthGithubCallback(c *gin.Context) {
oauthState, _ := c.Request.Cookie("oauthstate")

if c.Request.FormValue("state") != oauthState.Value {
fmt.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthState.Value, c.Request.FormValue("state"))
c.Redirect(http.StatusFound, "/")
return
}

code := c.Query("code")

githubId, err := getUserInfoFromGithub(c.Request.Context(), code)
if err != nil {
c.JSON(http.StatusOK, result.Failed(result.HandleError(err)))
return
}

if githubId == "" {
c.JSON(http.StatusOK, result.Failed(result.HandleError(result.RequestParamError)))
return
}

user, err := service.GetUserByGithubId(githubId)
if err != nil {
c.JSON(http.StatusOK, result.Failed(result.HandleError(err)))
return
}

// User not found, Need to register to bind the github id
if user == nil {
return
}

c.JSON(http.StatusOK, result.Success(githubId))
}

func getUserInfoFromGithub(ctx context.Context, code string) (string, error) {

token, err := githubConf.Exchange(ctx, code)
if err != nil {
return "", fmt.Errorf("exchange github code error: %s", err.Error())
}
client := &http.Client{}
req, err := http.NewRequest("GET", GithubUserInfoURL, nil)
if err != nil {
return "", fmt.Errorf("new request error: %s", err.Error())
}
req.Header.Set("Authorization", "Bearer "+token.AccessToken)
res, err := client.Do(req)
if err != nil {
return "", fmt.Errorf("failt to getting user info: %s", err.Error())
}
body, err := io.ReadAll(res.Body)
if err != nil {
return "", result.InternalErr
}

// Now just get the github id
githubId := gjson.Get(string(body), "id").String()
return githubId, nil
}
135 changes: 135 additions & 0 deletions api/v1/oauth_client_lark.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package v1

import (
"errors"
"fmt"
"io"
"net/http"
"net/url"
"time"

"github.com/NJUPT-SAST/sast-link-backend/config"
"github.com/NJUPT-SAST/sast-link-backend/endpoints"
"github.com/NJUPT-SAST/sast-link-backend/log"
"github.com/NJUPT-SAST/sast-link-backend/model"
"github.com/NJUPT-SAST/sast-link-backend/model/result"
"github.com/NJUPT-SAST/sast-link-backend/util"
"github.com/gin-gonic/gin"
"github.com/tidwall/gjson"
"golang.org/x/oauth2"
)

const (
AppAccessTokenURL = "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal"
UserAccessTokenURL = "https://open.feishu.cn/open-apis/authen/v1/oidc/access_token"
)

var (
larkConf = oauth2.Config{
ClientID: config.Config.GetString("oauth.client.lark.id"),
ClientSecret: config.Config.GetString("oauth.client.lark.secret"),
RedirectURL: "http://localhost:8080/api/v1/login/lark/callback",
Scopes: []string{},
Endpoint: endpoints.Lark,
}
)

// OauthLarkLogin redirect url to lark auth page.
func OauthLarkLogin(c *gin.Context) {
url := larkConf.AuthCodeURL("state")

log.Log.Warnf("Visit the URL for the auth dialog: %v\n", url)

c.Redirect(http.StatusPermanentRedirect, url)
}

// OauthLarkCallback read url from lark callback,
// get `code`, request app_access_token,
// at last request lark url to get user_access_token.
func OauthLarkCallback(c *gin.Context) {
code := c.Query("code")
log.Log.Debugf("\ncode ::: %s\n", code)
accessToken, err := getLarkAppAccessToken()

if err != nil {
log.Log.Errorln("getLarkAppAccessToken ::: ", err)
c.JSON(http.StatusInternalServerError, result.Failed(result.HandleError(err)))
return
}

data := map[string]string {
"grant_type": "authorization_code",
"code": code,
}

header := map[string]string {
"Authorization": fmt.Sprintf("Bearer %s", accessToken),
"Content-Type": "application/json; charset=utf-8",
}

res, err := util.PostWithHeader(UserAccessTokenURL, header, data)
if err != nil {
log.Log.Errorln("util.PostWithHeader ::: ", err)
c.JSON(http.StatusOK, result.Failed(result.HandleError(result.AccessTokenErr)))
}

body, err := io.ReadAll(res.Body)
defer res.Body.Close()
if err != nil {
log.Log.Errorln("io.ReadAll ::: ", err)
c.JSON(http.StatusInternalServerError, result.Failed(result.HandleError(err)))
return
}
if resCode := gjson.Get(string(body), "code").Int(); resCode != 0 {
log.Log.Errorf("gjson.Get ::: response code: %d\n", resCode)
c.JSON(http.StatusOK, result.Failed(result.HandleError(errors.New(fmt.Sprintf("OauthLarkCallback resCode: %d", resCode)))))
return
}

userAccessToken := gjson.Get(string(body), "data.access_token").String()
expire := gjson.Get(string(body), "data.expire_in").Int()

model.Rdb.Set(model.RedisCtx, "lark_user_access_token", userAccessToken, time.Duration(expire))
c.JSON(http.StatusOK, data)

}

// Get Lark app_access_token
func getLarkAppAccessToken() (string, error) {
appId := larkConf.ClientID
appSecret := larkConf.ClientSecret

params := url.Values{}
params.Add("app_id", appId)
params.Add("app_secret", appSecret)

res, error := http.PostForm(AppAccessTokenURL, params)
if error != nil {
log.Log.Errorln("http.PostForm ::: ", error)
return "", error
}
log.LogRes(res)

body, error := io.ReadAll(res.Body)
defer res.Body.Close()
if error != nil {
log.Log.Errorln("io.ReadAll ::: ", error)
return "", error
}


if code := gjson.Get(string(body), "code").Int(); code != 0 {
log.Log.Errorln("gjson.Get ::: code:", code)
return "", result.InternalErr
}

acceToken := gjson.Get(string(body), "app_access_token").String()
expire := gjson.Get(string(body), "expire").Int()

model.Rdb.Set(model.RedisCtx, "lark_app_access_token", acceToken, time.Duration(expire))

return acceToken, nil
}


func getUserInfo(user_access_token string) {}
20 changes: 20 additions & 0 deletions api/v1/oauth_clients.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package v1

import (
"crypto/rand"
"encoding/base64"
"net/http"
"time"
)

func GenerateStateOauthCookie(w http.ResponseWriter) string {
var expiration = time.Now().Add(20 * time.Minute)

b := make([]byte, 16)
rand.Read(b)
state := base64.URLEncoding.EncodeToString(b)
cookie := http.Cookie{Name: "oauthstate", Value: state, Expires: expiration}
http.SetCookie(w, &cookie)

return state
}
32 changes: 16 additions & 16 deletions api/v1/oauth.go → api/v1/oauth_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (

var (
srv *server.Server
pgxConn, _ = pgx.Connect(context.TODO(), config.Config.Sub("oauth").GetString("db_uri"))
pgxConn, _ = pgx.Connect(context.TODO(), config.Config.Sub("oauth.server").GetString("db_uri"))
adapter = pgx4adapter.NewConn(pgxConn)
clientStore, _ = pg.NewClientStore(adapter)
)
Expand Down Expand Up @@ -184,21 +184,21 @@ func Authorize(c *gin.Context) {
}

// User decides whether to authorize
func UserAuth(c *gin.Context) {
w := c.Writer
r := c.Request

//token := r.Header.Get("TOKEN")
_ = r.ParseMultipartForm(0)
token := c.PostForm("token")
if token == "" {
w.Header().Set("Content-Type", "application/json")
response := result.Failed(result.TokenError)
json, _ := json.Marshal(response)
w.Write(json)
return
}
}
// func UserAuth(c *gin.Context) {
// w := c.Writer
// r := c.Request
//
// //token := r.Header.Get("TOKEN")
// _ = r.ParseMultipartForm(0)
// token := c.PostForm("token")
// if token == "" {
// w.Header().Set("Content-Type", "application/json")
// response := result.Failed(result.AuthError)
// json, _ := json.Marshal(response)
// w.Write(json)
// return
// }
// }

// Get AccessToken
func AccessToken(c *gin.Context) {
Expand Down
Loading