2022-03-24 08:01:56 +00:00
|
|
|
package resolvers
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
|
2022-05-24 07:12:29 +00:00
|
|
|
"github.com/golang-jwt/jwt"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
|
2022-06-11 18:57:21 +00:00
|
|
|
"github.com/authorizerdev/authorizer/server/constants"
|
2022-03-24 08:01:56 +00:00
|
|
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
2022-05-27 17:50:38 +00:00
|
|
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
2022-05-30 06:24:16 +00:00
|
|
|
"github.com/authorizerdev/authorizer/server/parsers"
|
2022-03-24 08:01:56 +00:00
|
|
|
"github.com/authorizerdev/authorizer/server/token"
|
|
|
|
"github.com/authorizerdev/authorizer/server/utils"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ValidateJwtTokenResolver is used to validate a jwt token without its rotation
|
|
|
|
// this can be used at API level (backend)
|
|
|
|
// it can validate:
|
|
|
|
// access_token
|
|
|
|
// id_token
|
|
|
|
// refresh_token
|
|
|
|
func ValidateJwtTokenResolver(ctx context.Context, params model.ValidateJWTTokenInput) (*model.ValidateJWTTokenResponse, error) {
|
|
|
|
gc, err := utils.GinContextFromContext(ctx)
|
|
|
|
if err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Failed to get GinContext: ", err)
|
2022-03-24 08:01:56 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
tokenType := params.TokenType
|
2022-06-11 18:57:21 +00:00
|
|
|
if tokenType != constants.TokenTypeAccessToken && tokenType != constants.TokenTypeRefreshToken && tokenType != constants.TokenTypeIdentityToken {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Invalid token type: ", tokenType)
|
2022-03-24 08:01:56 +00:00
|
|
|
return nil, errors.New("invalid token type")
|
|
|
|
}
|
|
|
|
|
2022-06-11 13:40:39 +00:00
|
|
|
var claimRoles []string
|
|
|
|
var claims jwt.MapClaims
|
2022-03-24 08:01:56 +00:00
|
|
|
userID := ""
|
|
|
|
nonce := ""
|
2022-06-11 18:57:21 +00:00
|
|
|
|
|
|
|
claims, err = token.ParseJWTToken(params.Token)
|
|
|
|
if err != nil {
|
|
|
|
log.Debug("Failed to parse JWT token: ", err)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
userID = claims["sub"].(string)
|
|
|
|
|
2022-03-24 08:01:56 +00:00
|
|
|
// access_token and refresh_token should be validated from session store as well
|
2022-06-11 18:57:21 +00:00
|
|
|
if tokenType == constants.TokenTypeAccessToken || tokenType == constants.TokenTypeRefreshToken {
|
|
|
|
nonce = claims["nonce"].(string)
|
2022-06-29 16:54:00 +00:00
|
|
|
loginMethod := claims["login_method"]
|
|
|
|
sessionKey := userID
|
|
|
|
if loginMethod != nil && loginMethod != "" {
|
|
|
|
sessionKey = loginMethod.(string) + ":" + userID
|
|
|
|
}
|
|
|
|
token, err := memorystore.Provider.GetUserSession(sessionKey, tokenType+"_"+claims["nonce"].(string))
|
2022-06-11 18:57:21 +00:00
|
|
|
if err != nil || token == "" {
|
2022-06-11 13:40:39 +00:00
|
|
|
log.Debug("Failed to get user session: ", err)
|
|
|
|
return nil, errors.New("invalid token")
|
|
|
|
}
|
2022-03-24 08:01:56 +00:00
|
|
|
}
|
|
|
|
|
2022-05-30 06:24:16 +00:00
|
|
|
hostname := parsers.GetHost(gc)
|
2022-03-24 08:01:56 +00:00
|
|
|
|
2022-06-11 18:57:21 +00:00
|
|
|
// we cannot validate nonce in case of id_token as that token is not persisted in session store
|
|
|
|
if nonce != "" {
|
2022-06-11 13:40:39 +00:00
|
|
|
if ok, err := token.ValidateJWTClaims(claims, hostname, nonce, userID); !ok || err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Failed to parse jwt token: ", err)
|
2022-06-11 13:40:39 +00:00
|
|
|
return nil, errors.New("invalid claims")
|
2022-03-24 08:01:56 +00:00
|
|
|
}
|
|
|
|
} else {
|
2022-06-11 18:57:21 +00:00
|
|
|
if ok, err := token.ValidateJWTTokenWithoutNonce(claims, hostname, userID); !ok || err != nil {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Failed to parse jwt token without nonce: ", err)
|
2022-06-11 13:40:39 +00:00
|
|
|
return nil, errors.New("invalid claims")
|
2022-03-24 08:01:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-23 16:33:08 +00:00
|
|
|
claimKey := "roles"
|
|
|
|
|
|
|
|
if tokenType == constants.TokenTypeIdentityToken {
|
|
|
|
claimKey, err = memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyJwtRoleClaim)
|
|
|
|
if err != nil {
|
|
|
|
claimKey = "roles"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
claimRolesInterface := claims[claimKey]
|
2022-03-24 08:01:56 +00:00
|
|
|
roleSlice := utils.ConvertInterfaceToSlice(claimRolesInterface)
|
|
|
|
for _, v := range roleSlice {
|
|
|
|
claimRoles = append(claimRoles, v.(string))
|
|
|
|
}
|
|
|
|
|
|
|
|
if params.Roles != nil && len(params.Roles) > 0 {
|
|
|
|
for _, v := range params.Roles {
|
|
|
|
if !utils.StringSliceContains(claimRoles, v) {
|
2022-05-25 07:00:22 +00:00
|
|
|
log.Debug("Token does not have required role: ", v)
|
2022-03-24 08:01:56 +00:00
|
|
|
return nil, fmt.Errorf(`unauthorized`)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &model.ValidateJWTTokenResponse{
|
|
|
|
IsValid: true,
|
2022-11-01 04:42:01 +00:00
|
|
|
Claims: claims,
|
2022-03-24 08:01:56 +00:00
|
|
|
}, nil
|
|
|
|
}
|