diff --git a/utils/auth.go b/utils/auth.go index d025161..bd10ce6 100644 --- a/utils/auth.go +++ b/utils/auth.go @@ -1,6 +1,8 @@ package utils import ( + "strings" + "domain0/config" "encoding/json" "errors" @@ -16,6 +18,7 @@ type AccessTokenInfo struct { ExpiresIn int `json:"expires_in"` RefreshToken string `json:"refresh_token"` RefreshExpiresIn int `json:"refresh_expires_in"` + Scope string `json:"scope"` } type AuthInfo struct { @@ -36,24 +39,41 @@ type AuthInfo struct { // Mobile string `json:"mobile"` } -type FeishuAuthInfoResponse struct { - Code int `json:"code"` - Message string `json:"msg"` - Data AuthInfo `json:"data"` +// enterprise email +var AuthScope = strings.Join([]string{"mail:user_mailbox:readonly"}, " ") + +type FeishuGenericResponse[T any] struct { + Code int `json:"code"` + Message string `json:"msg"` + Data T `json:"data"` +} + +type FeishuAccessTokenInfoResponse = FeishuGenericResponse[AccessTokenInfo] + +type FeishuAuthInfoResponse = FeishuGenericResponse[AuthInfo] + +type FeishuAppAccessTokenInfoResponse struct { + AppAccessToken string `json:"app_access_token"` + Code int `json:"code"` + Expire int `json:"expire"` + Msg string `json:"msg"` + TenantAccessToken string `json:"tenant_access_token"` } func FeishuRedirectToCodeURL() string { - return "https://passport.feishu.cn/suite/passport/oauth/authorize?client_id=" + config.CONFIG.Feishu.AppID + + return "https://open.feishu.cn/open-apis/authen/v1/authorize?app_id=" + config.CONFIG.Feishu.AppID + "&redirect_uri=" + url.QueryEscape(config.CONFIG.Feishu.RedirectURL) + - "&response_type=code" + + "&scope=" + url.QueryEscape(AuthScope) + "&state=feishu" } +func feishuAppAccessTokenURL() string { + return "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal?app_id=" + config.CONFIG.Feishu.AppID + + "&app_secret=" + config.CONFIG.Feishu.AppSecret +} + func feishuRedirectToTokenURL(code string) string { - return "https://passport.feishu.cn/suite/passport/oauth/token?grant_type=authorization_code&client_id=" + config.CONFIG.Feishu.AppID + - "&client_secret=" + config.CONFIG.Feishu.AppSecret + - "&code=" + code + - "&redirect_uri=" + url.QueryEscape(config.CONFIG.Feishu.RedirectURL) + return "https://open.feishu.cn/open-apis/authen/v1/oidc/access_token?grant_type=authorization_code&code=" + code } func feishuGetUserInfoURL() string { @@ -61,6 +81,35 @@ func feishuGetUserInfoURL() string { } func FeishuGetUserInfo(code string) (AuthInfo, error) { + // Query App access token + t := fiber.AcquireAgent() + defer fiber.ReleaseAgent(t) + + app_req := t.Request() + app_req.Header.SetMethod("POST") + app_req.SetRequestURI(feishuAppAccessTokenURL()) + if err := t.Parse(); err != nil { + logrus.Error(err) + return AuthInfo{}, err + } + + hcode, body, errs := t.Bytes() + if len(errs) != 0 || hcode != 200 { + logrus.Error("fetch app token failed : ", string(body), errs) + return AuthInfo{}, errors.New("feishu auth failed") + } + + var feishuAppAccessTokenInfo FeishuAppAccessTokenInfoResponse + err := json.Unmarshal(body, &feishuAppAccessTokenInfo) + if err != nil { + logrus.Error(err) + return AuthInfo{}, err + } + if feishuAppAccessTokenInfo.Code != 0 { + logrus.Error("fetch app token failed : ", feishuAppAccessTokenInfo.Msg) + return AuthInfo{}, errors.New("feishu auth failed") + } + // Query Access token a := fiber.AcquireAgent() defer fiber.ReleaseAgent(a) @@ -68,24 +117,28 @@ func FeishuGetUserInfo(code string) (AuthInfo, error) { act_req := a.Request() act_req.Header.SetMethod("POST") act_req.SetRequestURI(feishuRedirectToTokenURL(code)) - + act_req.Header.Set("Authorization", "Bearer "+feishuAppAccessTokenInfo.AppAccessToken) if err := a.Parse(); err != nil { logrus.Error(err) return AuthInfo{}, err } - hcode, body, errs := a.Bytes() + hcode, body, errs = a.Bytes() if len(errs) != 0 || hcode != 200 { - logrus.Error(errs) + logrus.Error("fetch auth token failed : ", string(body), errs) return AuthInfo{}, errors.New("feishu auth failed") } - var accessTokenInfo AccessTokenInfo - err := json.Unmarshal(body, &accessTokenInfo) + var accessTokenInfo FeishuAccessTokenInfoResponse + err = json.Unmarshal(body, &accessTokenInfo) if err != nil { logrus.Error(err) return AuthInfo{}, err } + if accessTokenInfo.Code != 0 { + logrus.Error("fetch user info failed : ", accessTokenInfo.Message) + return AuthInfo{}, errors.New("feishu auth failed") + } // Query User info u := fiber.AcquireAgent() @@ -94,10 +147,10 @@ func FeishuGetUserInfo(code string) (AuthInfo, error) { user_req := u.Request() user_req.Header.SetMethod("GET") user_req.SetRequestURI(feishuGetUserInfoURL()) - user_req.Header.Set("Authorization", "Bearer "+accessTokenInfo.AccessToken) + user_req.Header.Set("Authorization", "Bearer "+accessTokenInfo.Data.AccessToken) if err := u.Parse(); err != nil { - logrus.Error(err) + logrus.Error("fetch user info failed : ", string(body), err) return AuthInfo{}, err } @@ -114,9 +167,8 @@ func FeishuGetUserInfo(code string) (AuthInfo, error) { return AuthInfo{}, err } if feishuInfoResponse.Code != 0 { - logrus.Error(feishuInfoResponse.Message) + logrus.Error("fetch user info failed : ", feishuInfoResponse.Message) return AuthInfo{}, errors.New("feishu auth failed") } - return feishuInfoResponse.Data, nil }