feat: return most backend API errors to frontend (#1836)

* feat: return most backend API errros to frontend

Signed-off-by: yehong <239859435@qq.com>

* refactor: reduce int type change

Signed-off-by: yehong <239859435@qq.com>

* feat: return err backend in token.go

Signed-off-by: yehong <239859435@qq.com>

---------

Signed-off-by: yehong <239859435@qq.com>
This commit is contained in:
yehong
2023-05-30 15:49:39 +08:00
committed by GitHub
parent 34151c0095
commit 02e692a300
105 changed files with 3788 additions and 1734 deletions

View File

@ -91,67 +91,54 @@ type IntrospectionResponse struct {
Jti string `json:"jti,omitempty"`
}
func GetTokenCount(owner, organization, field, value string) int {
func GetTokenCount(owner, organization, field, value string) (int64, error) {
session := GetSession(owner, -1, -1, field, value, "", "")
count, err := session.Count(&Token{Organization: organization})
if err != nil {
panic(err)
}
return int(count)
return session.Count(&Token{Organization: organization})
}
func GetTokens(owner string, organization string) []*Token {
func GetTokens(owner string, organization string) ([]*Token, error) {
tokens := []*Token{}
err := adapter.Engine.Desc("created_time").Find(&tokens, &Token{Owner: owner, Organization: organization})
if err != nil {
panic(err)
}
return tokens
return tokens, err
}
func GetPaginationTokens(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) []*Token {
func GetPaginationTokens(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) ([]*Token, error) {
tokens := []*Token{}
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
err := session.Find(&tokens, &Token{Organization: organization})
if err != nil {
panic(err)
}
return tokens
return tokens, err
}
func getToken(owner string, name string) *Token {
func getToken(owner string, name string) (*Token, error) {
if owner == "" || name == "" {
return nil
return nil, nil
}
token := Token{Owner: owner, Name: name}
existed, err := adapter.Engine.Get(&token)
if err != nil {
panic(err)
return nil, err
}
if existed {
return &token
return &token, nil
}
return nil
return nil, nil
}
func getTokenByCode(code string) *Token {
func getTokenByCode(code string) (*Token, error) {
token := Token{Code: code}
existed, err := adapter.Engine.Get(&token)
if err != nil {
panic(err)
return nil, err
}
if existed {
return &token
return &token, nil
}
return nil
return nil, nil
}
func updateUsedByCode(token *Token) bool {
@ -163,7 +150,7 @@ func updateUsedByCode(token *Token) bool {
return affected != 0
}
func GetToken(id string) *Token {
func GetToken(id string) (*Token, error) {
owner, name := util.GetOwnerAndNameFromId(id)
return getToken(owner, name)
}
@ -172,124 +159,155 @@ func (token *Token) GetId() string {
return fmt.Sprintf("%s/%s", token.Owner, token.Name)
}
func UpdateToken(id string, token *Token) bool {
func UpdateToken(id string, token *Token) (bool, error) {
owner, name := util.GetOwnerAndNameFromId(id)
if getToken(owner, name) == nil {
return false
if t, err := getToken(owner, name); err != nil {
return false, err
} else if t == nil {
return false, nil
}
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(token)
if err != nil {
panic(err)
return false, err
}
return affected != 0
return affected != 0, nil
}
func AddToken(token *Token) bool {
func AddToken(token *Token) (bool, error) {
affected, err := adapter.Engine.Insert(token)
if err != nil {
panic(err)
return false, err
}
return affected != 0
return affected != 0, nil
}
func DeleteToken(token *Token) bool {
func DeleteToken(token *Token) (bool, error) {
affected, err := adapter.Engine.ID(core.PK{token.Owner, token.Name}).Delete(&Token{})
if err != nil {
panic(err)
return false, err
}
return affected != 0
return affected != 0, nil
}
func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token) {
func ExpireTokenByAccessToken(accessToken string) (bool, *Application, *Token, error) {
token := Token{AccessToken: accessToken}
existed, err := adapter.Engine.Get(&token)
if err != nil {
panic(err)
return false, nil, nil, err
}
if !existed {
return false, nil, nil
return false, nil, nil, nil
}
token.ExpiresIn = 0
affected, err := adapter.Engine.ID(core.PK{token.Owner, token.Name}).Cols("expires_in").Update(&token)
if err != nil {
panic(err)
return false, nil, nil, err
}
application := getApplication(token.Owner, token.Application)
return affected != 0, application, &token
application, err := getApplication(token.Owner, token.Application)
if err != nil {
return false, nil, nil, err
}
return affected != 0, application, &token, nil
}
func GetTokenByAccessToken(accessToken string) *Token {
func GetTokenByAccessToken(accessToken string) (*Token, error) {
// Check if the accessToken is in the database
token := Token{AccessToken: accessToken}
existed, err := adapter.Engine.Get(&token)
if err != nil || !existed {
return nil
if err != nil {
return nil, err
}
return &token
if !existed {
return nil, nil
}
return &token, nil
}
func GetTokenByTokenAndApplication(token string, application string) *Token {
func GetTokenByTokenAndApplication(token string, application string) (*Token, error) {
tokenResult := Token{}
existed, err := adapter.Engine.Where("(refresh_token = ? or access_token = ? ) and application = ?", token, token, application).Get(&tokenResult)
if err != nil || !existed {
return nil
if err != nil {
return nil, err
}
return &tokenResult
if !existed {
return nil, nil
}
return &tokenResult, nil
}
func CheckOAuthLogin(clientId string, responseType string, redirectUri string, scope string, state string, lang string) (string, *Application) {
func CheckOAuthLogin(clientId string, responseType string, redirectUri string, scope string, state string, lang string) (string, *Application, error) {
if responseType != "code" && responseType != "token" && responseType != "id_token" {
return fmt.Sprintf(i18n.Translate(lang, "token:Grant_type: %s is not supported in this application"), responseType), nil
return fmt.Sprintf(i18n.Translate(lang, "token:Grant_type: %s is not supported in this application"), responseType), nil, nil
}
application, err := GetApplicationByClientId(clientId)
if err != nil {
return "", nil, err
}
application := GetApplicationByClientId(clientId)
if application == nil {
return i18n.Translate(lang, "token:Invalid client_id"), nil
return i18n.Translate(lang, "token:Invalid client_id"), nil, nil
}
if !application.IsRedirectUriValid(redirectUri) {
return fmt.Sprintf(i18n.Translate(lang, "token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri), application
return fmt.Sprintf(i18n.Translate(lang, "token:Redirect URI: %s doesn't exist in the allowed Redirect URI list"), redirectUri), application, nil
}
// Mask application for /api/get-app-login
application.ClientSecret = ""
return "", application
return "", application, nil
}
func GetOAuthCode(userId string, clientId string, responseType string, redirectUri string, scope string, state string, nonce string, challenge string, host string, lang string) *Code {
user := GetUser(userId)
func GetOAuthCode(userId string, clientId string, responseType string, redirectUri string, scope string, state string, nonce string, challenge string, host string, lang string) (*Code, error) {
user, err := GetUser(userId)
if err != nil {
return nil, err
}
if user == nil {
return &Code{
Message: fmt.Sprintf("general:The user: %s doesn't exist", userId),
Code: "",
}
}, nil
}
if user.IsForbidden {
return &Code{
Message: "error: the user is forbidden to sign in, please contact the administrator",
Code: "",
}
}, nil
}
msg, application, err := CheckOAuthLogin(clientId, responseType, redirectUri, scope, state, lang)
if err != nil {
return nil, err
}
msg, application := CheckOAuthLogin(clientId, responseType, redirectUri, scope, state, lang)
if msg != "" {
return &Code{
Message: msg,
Code: "",
}
}, nil
}
ExtendUserWithRolesAndPermissions(user)
err = ExtendUserWithRolesAndPermissions(user)
if err != nil {
return nil, err
}
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, nonce, scope, host)
if err != nil {
panic(err)
return nil, err
}
if challenge == "null" {
@ -313,21 +331,28 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
CodeIsUsed: false,
CodeExpireIn: time.Now().Add(time.Minute * 5).Unix(),
}
AddToken(token)
_, err = AddToken(token)
if err != nil {
return nil, err
}
return &Code{
Message: "",
Code: token.Code,
}
}, nil
}
func GetOAuthToken(grantType string, clientId string, clientSecret string, code string, verifier string, scope string, username string, password string, host string, refreshToken string, tag string, avatar string, lang string) interface{} {
application := GetApplicationByClientId(clientId)
func GetOAuthToken(grantType string, clientId string, clientSecret string, code string, verifier string, scope string, username string, password string, host string, refreshToken string, tag string, avatar string, lang string) (interface{}, error) {
application, err := GetApplicationByClientId(clientId)
if err != nil {
return nil, err
}
if application == nil {
return &TokenError{
Error: InvalidClient,
ErrorDescription: "client_id is invalid",
}
}, nil
}
// Check if grantType is allowed in the current application
@ -336,32 +361,44 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
return &TokenError{
Error: UnsupportedGrantType,
ErrorDescription: fmt.Sprintf("grant_type: %s is not supported in this application", grantType),
}
}, nil
}
var token *Token
var tokenError *TokenError
switch grantType {
case "authorization_code": // Authorization Code Grant
token, tokenError = GetAuthorizationCodeToken(application, clientSecret, code, verifier)
token, tokenError, err = GetAuthorizationCodeToken(application, clientSecret, code, verifier)
case "password": // Resource Owner Password Credentials Grant
token, tokenError = GetPasswordToken(application, username, password, scope, host)
token, tokenError, err = GetPasswordToken(application, username, password, scope, host)
case "client_credentials": // Client Credentials Grant
token, tokenError = GetClientCredentialsToken(application, clientSecret, scope, host)
token, tokenError, err = GetClientCredentialsToken(application, clientSecret, scope, host)
case "refresh_token":
return RefreshToken(grantType, refreshToken, scope, clientId, clientSecret, host)
refreshToken2, err := RefreshToken(grantType, refreshToken, scope, clientId, clientSecret, host)
if err != nil {
return nil, err
}
return refreshToken2, nil
}
if err != nil {
return nil, err
}
if tag == "wechat_miniprogram" {
// Wechat Mini Program
token, tokenError = GetWechatMiniProgramToken(application, code, host, username, avatar, lang)
token, tokenError, err = GetWechatMiniProgramToken(application, code, host, username, avatar, lang)
if err != nil {
return nil, err
}
}
if tokenError != nil {
return tokenError
return tokenError, nil
}
token.CodeIsUsed = true
go updateUsedByCode(token)
tokenWrapper := &TokenWrapper{
@ -373,29 +410,33 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
Scope: token.Scope,
}
return tokenWrapper
return tokenWrapper, nil
}
func RefreshToken(grantType string, refreshToken string, scope string, clientId string, clientSecret string, host string) interface{} {
func RefreshToken(grantType string, refreshToken string, scope string, clientId string, clientSecret string, host string) (interface{}, error) {
// check parameters
if grantType != "refresh_token" {
return &TokenError{
Error: UnsupportedGrantType,
ErrorDescription: "grant_type should be refresh_token",
}
}, nil
}
application := GetApplicationByClientId(clientId)
application, err := GetApplicationByClientId(clientId)
if err != nil {
return nil, err
}
if application == nil {
return &TokenError{
Error: InvalidClient,
ErrorDescription: "client_id is invalid",
}
}, nil
}
if clientSecret != "" && application.ClientSecret != clientSecret {
return &TokenError{
Error: InvalidClient,
ErrorDescription: "client_secret is invalid",
}
}, nil
}
// check whether the refresh token is valid, and has not expired.
token := Token{RefreshToken: refreshToken}
@ -404,33 +445,44 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
return &TokenError{
Error: InvalidGrant,
ErrorDescription: "refresh token is invalid, expired or revoked",
}
}, nil
}
cert, err := getCertByApplication(application)
if err != nil {
return nil, err
}
cert := getCertByApplication(application)
_, err = ParseJwtToken(refreshToken, cert)
if err != nil {
return &TokenError{
Error: InvalidGrant,
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
}
}, nil
}
// generate a new token
user := getUser(application.Organization, token.User)
user, err := getUser(application.Organization, token.User)
if err != nil {
return nil, err
}
if user.IsForbidden {
return &TokenError{
Error: InvalidGrant,
ErrorDescription: "the user is forbidden to sign in, please contact the administrator",
}
}, nil
}
ExtendUserWithRolesAndPermissions(user)
err = ExtendUserWithRolesAndPermissions(user)
if err != nil {
return nil, err
}
newAccessToken, newRefreshToken, tokenName, err := generateJwtToken(application, user, "", scope, host)
if err != nil {
return &TokenError{
Error: EndpointError,
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
}
}, nil
}
newToken := &Token{
@ -447,8 +499,15 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
Scope: scope,
TokenType: "Bearer",
}
AddToken(newToken)
DeleteToken(&token)
_, err = AddToken(newToken)
if err != nil {
return nil, err
}
_, err = DeleteToken(&token)
if err != nil {
return nil, err
}
tokenWrapper := &TokenWrapper{
AccessToken: newToken.AccessToken,
@ -459,7 +518,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
Scope: newToken.Scope,
}
return tokenWrapper
return tokenWrapper, nil
}
// PkceChallenge: base64-URL-encoded SHA256 hash of verifier, per rfc 7636
@ -486,34 +545,38 @@ func IsGrantTypeValid(method string, grantTypes []string) bool {
// GetAuthorizationCodeToken
// Authorization code flow
func GetAuthorizationCodeToken(application *Application, clientSecret string, code string, verifier string) (*Token, *TokenError) {
func GetAuthorizationCodeToken(application *Application, clientSecret string, code string, verifier string) (*Token, *TokenError, error) {
if code == "" {
return nil, &TokenError{
Error: InvalidRequest,
ErrorDescription: "authorization code should not be empty",
}
}, nil
}
token, err := getTokenByCode(code)
if err != nil {
return nil, nil, err
}
token := getTokenByCode(code)
if token == nil {
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: "authorization code is invalid",
}
}, nil
}
if token.CodeIsUsed {
// anti replay attacks
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: "authorization code has been used",
}
}, nil
}
if token.CodeChallenge != "" && pkceChallenge(verifier) != token.CodeChallenge {
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: "verifier is invalid",
}
}, nil
}
if application.ClientSecret != clientSecret {
@ -523,13 +586,13 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
return nil, &TokenError{
Error: InvalidClient,
ErrorDescription: "client_secret is invalid",
}
}, nil
} else {
if clientSecret != "" {
return nil, &TokenError{
Error: InvalidClient,
ErrorDescription: "client_secret is invalid",
}
}, nil
}
}
}
@ -538,7 +601,7 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: "the token is for wrong application (client_id)",
}
}, nil
}
if time.Now().Unix() > token.CodeExpireIn {
@ -546,42 +609,50 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: "authorization code has expired",
}
}, nil
}
return token, nil
return token, nil, nil
}
// GetPasswordToken
// Resource Owner Password Credentials flow
func GetPasswordToken(application *Application, username string, password string, scope string, host string) (*Token, *TokenError) {
user := getUser(application.Organization, username)
func GetPasswordToken(application *Application, username string, password string, scope string, host string) (*Token, *TokenError, error) {
user, err := getUser(application.Organization, username)
if err != nil {
return nil, nil, err
}
if user == nil {
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: "the user does not exist",
}
}, nil
}
msg := CheckPassword(user, password, "en")
if msg != "" {
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: "invalid username or password",
}
}, nil
}
if user.IsForbidden {
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: "the user is forbidden to sign in, please contact the administrator",
}
}, nil
}
err = ExtendUserWithRolesAndPermissions(user)
if err != nil {
return nil, nil, err
}
ExtendUserWithRolesAndPermissions(user)
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", scope, host)
if err != nil {
return nil, &TokenError{
Error: EndpointError,
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
}
}, nil
}
token := &Token{
Owner: application.Owner,
@ -598,18 +669,22 @@ func GetPasswordToken(application *Application, username string, password string
TokenType: "Bearer",
CodeIsUsed: true,
}
AddToken(token)
return token, nil
_, err = AddToken(token)
if err != nil {
return nil, nil, err
}
return token, nil, nil
}
// GetClientCredentialsToken
// Client Credentials flow
func GetClientCredentialsToken(application *Application, clientSecret string, scope string, host string) (*Token, *TokenError) {
func GetClientCredentialsToken(application *Application, clientSecret string, scope string, host string) (*Token, *TokenError, error) {
if application.ClientSecret != clientSecret {
return nil, &TokenError{
Error: InvalidClient,
ErrorDescription: "client_secret is invalid",
}
}, nil
}
nullUser := &User{
Owner: application.Owner,
@ -623,7 +698,7 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
return nil, &TokenError{
Error: EndpointError,
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
}
}, nil
}
token := &Token{
Owner: application.Owner,
@ -639,18 +714,27 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
TokenType: "Bearer",
CodeIsUsed: true,
}
AddToken(token)
return token, nil
_, err = AddToken(token)
if err != nil {
return nil, nil, err
}
return token, nil, nil
}
// GetTokenByUser
// Implicit flow
func GetTokenByUser(application *Application, user *User, scope string, host string) (*Token, error) {
ExtendUserWithRolesAndPermissions(user)
err := ExtendUserWithRolesAndPermissions(user)
if err != nil {
return nil, err
}
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", scope, host)
if err != nil {
return nil, err
}
token := &Token{
Owner: application.Owner,
Name: tokenName,
@ -666,43 +750,56 @@ func GetTokenByUser(application *Application, user *User, scope string, host str
TokenType: "Bearer",
CodeIsUsed: true,
}
AddToken(token)
_, err = AddToken(token)
if err != nil {
return nil, err
}
return token, nil
}
// GetWechatMiniProgramToken
// Wechat Mini Program flow
func GetWechatMiniProgramToken(application *Application, code string, host string, username string, avatar string, lang string) (*Token, *TokenError) {
func GetWechatMiniProgramToken(application *Application, code string, host string, username string, avatar string, lang string) (*Token, *TokenError, error) {
mpProvider := GetWechatMiniProgramProvider(application)
if mpProvider == nil {
return nil, &TokenError{
Error: InvalidClient,
ErrorDescription: "the application does not support wechat mini program",
}
}, nil
}
provider := GetProvider(util.GetId("admin", mpProvider.Name))
provider, err := GetProvider(util.GetId("admin", mpProvider.Name))
if err != nil {
return nil, nil, err
}
mpIdp := idp.NewWeChatMiniProgramIdProvider(provider.ClientId, provider.ClientSecret)
session, err := mpIdp.GetSessionByCode(code)
if err != nil {
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: fmt.Sprintf("get wechat mini program session error: %s", err.Error()),
}
}, nil
}
openId, unionId := session.Openid, session.Unionid
if openId == "" && unionId == "" {
return nil, &TokenError{
Error: InvalidRequest,
ErrorDescription: "the wechat mini program session is invalid",
}
}, nil
}
user := getUserByWechatId(openId, unionId)
user, err := getUserByWechatId(openId, unionId)
if err != nil {
return nil, nil, err
}
if user == nil {
if !application.EnableSignUp {
return nil, &TokenError{
Error: InvalidGrant,
ErrorDescription: "the application does not allow to sign up new account",
}
}, nil
}
// Add new user
var name string
@ -730,16 +827,23 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
UserPropertiesWechatUnionId: unionId,
},
}
AddUser(user)
_, err = AddUser(user)
if err != nil {
return nil, nil, err
}
}
err = ExtendUserWithRolesAndPermissions(user)
if err != nil {
return nil, nil, err
}
ExtendUserWithRolesAndPermissions(user)
accessToken, refreshToken, tokenName, err := generateJwtToken(application, user, "", "", host)
if err != nil {
return nil, &TokenError{
Error: EndpointError,
ErrorDescription: fmt.Sprintf("generate jwt token error: %s", err.Error()),
}
}, nil
}
token := &Token{
@ -757,6 +861,9 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
TokenType: "Bearer",
CodeIsUsed: true,
}
AddToken(token)
return token, nil
_, err = AddToken(token)
if err != nil {
return nil, nil, err
}
return token, nil, nil
}