feat: add ability to get access token based on refresh token
This commit is contained in:
parent
674eeeea4e
commit
a69b8e290c
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// grant type required
|
||||||
func TokenHandler() gin.HandlerFunc {
|
func TokenHandler() gin.HandlerFunc {
|
||||||
return func(gc *gin.Context) {
|
return func(gc *gin.Context) {
|
||||||
var reqBody map[string]string
|
var reqBody map[string]string
|
||||||
|
@ -29,6 +30,22 @@ func TokenHandler() gin.HandlerFunc {
|
||||||
codeVerifier := strings.TrimSpace(reqBody["code_verifier"])
|
codeVerifier := strings.TrimSpace(reqBody["code_verifier"])
|
||||||
code := strings.TrimSpace(reqBody["code"])
|
code := strings.TrimSpace(reqBody["code"])
|
||||||
clientID := strings.TrimSpace(reqBody["client_id"])
|
clientID := strings.TrimSpace(reqBody["client_id"])
|
||||||
|
grantType := strings.TrimSpace(reqBody["grant_type"])
|
||||||
|
refreshToken := strings.TrimSpace(reqBody["refresh_token"])
|
||||||
|
|
||||||
|
if grantType == "" {
|
||||||
|
grantType = "authorization_code"
|
||||||
|
}
|
||||||
|
|
||||||
|
isRefreshTokenGrant := grantType == "refresh_token"
|
||||||
|
isAuthorizationCodeGrant := grantType == "authorization_code"
|
||||||
|
|
||||||
|
if !isRefreshTokenGrant && !isAuthorizationCodeGrant {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_grant_type",
|
||||||
|
"error_description": "grant_type is invalid",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if clientID == "" {
|
if clientID == "" {
|
||||||
gc.JSON(http.StatusBadRequest, gin.H{
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
@ -46,6 +63,10 @@ func TokenHandler() gin.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var userID string
|
||||||
|
var roles, scope []string
|
||||||
|
if isAuthorizationCodeGrant {
|
||||||
|
|
||||||
if codeVerifier == "" {
|
if codeVerifier == "" {
|
||||||
gc.JSON(http.StatusBadRequest, gin.H{
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
"error": "invalid_code_verifier",
|
"error": "invalid_code_verifier",
|
||||||
|
@ -88,6 +109,8 @@ func TokenHandler() gin.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rollover the session for security
|
||||||
|
sessionstore.RemoveState(sessionDataSplit[1])
|
||||||
// validate session
|
// validate session
|
||||||
claims, err := token.ValidateBrowserSession(gc, sessionDataSplit[1])
|
claims, err := token.ValidateBrowserSession(gc, sessionDataSplit[1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -97,7 +120,30 @@ func TokenHandler() gin.HandlerFunc {
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
userID := claims.Subject
|
userID = claims.Subject
|
||||||
|
roles = claims.Roles
|
||||||
|
scope = claims.Scope
|
||||||
|
} else {
|
||||||
|
// validate refresh token
|
||||||
|
if refreshToken == "" {
|
||||||
|
gc.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "invalid_refresh_token",
|
||||||
|
"error_description": "The refresh token is invalid",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
claims, err := token.ValidateRefreshToken(gc, refreshToken)
|
||||||
|
if err != nil {
|
||||||
|
gc.JSON(http.StatusUnauthorized, gin.H{
|
||||||
|
"error": "unauthorized",
|
||||||
|
"error_description": err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
userID = claims["sub"].(string)
|
||||||
|
roles = claims["roles"].([]string)
|
||||||
|
scope = claims["scope"].([]string)
|
||||||
|
}
|
||||||
|
|
||||||
user, err := db.Provider.GetUserByID(userID)
|
user, err := db.Provider.GetUserByID(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.JSON(http.StatusUnauthorized, gin.H{
|
gc.JSON(http.StatusUnauthorized, gin.H{
|
||||||
|
@ -106,9 +152,8 @@ func TokenHandler() gin.HandlerFunc {
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// rollover the session for security
|
|
||||||
sessionstore.RemoveState(sessionDataSplit[1])
|
authToken, err := token.CreateAuthToken(gc, user, roles, scope)
|
||||||
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, claims.Scope)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gc.JSON(http.StatusUnauthorized, gin.H{
|
gc.JSON(http.StatusUnauthorized, gin.H{
|
||||||
"error": "unauthorized",
|
"error": "unauthorized",
|
||||||
|
@ -124,7 +169,8 @@ func TokenHandler() gin.HandlerFunc {
|
||||||
res := map[string]interface{}{
|
res := map[string]interface{}{
|
||||||
"access_token": authToken.AccessToken.Token,
|
"access_token": authToken.AccessToken.Token,
|
||||||
"id_token": authToken.IDToken.Token,
|
"id_token": authToken.IDToken.Token,
|
||||||
"scope": claims.Scope,
|
"scope": scope,
|
||||||
|
"roles": roles,
|
||||||
"expires_in": expiresIn,
|
"expires_in": expiresIn,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string) (
|
||||||
}
|
}
|
||||||
|
|
||||||
if utils.StringSliceContains(scope, "offline_access") {
|
if utils.StringSliceContains(scope, "offline_access") {
|
||||||
refreshToken, refreshTokenExpiresAt, err := CreateRefreshToken(user, roles, hostname, nonce)
|
refreshToken, refreshTokenExpiresAt, err := CreateRefreshToken(user, roles, scope, hostname, nonce)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string) (
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateRefreshToken util to create JWT token
|
// CreateRefreshToken util to create JWT token
|
||||||
func CreateRefreshToken(user models.User, roles []string, hostname, nonce string) (string, int64, error) {
|
func CreateRefreshToken(user models.User, roles, scopes []string, hostname, nonce string) (string, int64, error) {
|
||||||
// expires in 1 year
|
// expires in 1 year
|
||||||
expiryBound := time.Hour * 8760
|
expiryBound := time.Hour * 8760
|
||||||
expiresAt := time.Now().Add(expiryBound).Unix()
|
expiresAt := time.Now().Add(expiryBound).Unix()
|
||||||
|
@ -115,6 +115,7 @@ func CreateRefreshToken(user models.User, roles []string, hostname, nonce string
|
||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"token_type": constants.TokenTypeRefreshToken,
|
"token_type": constants.TokenTypeRefreshToken,
|
||||||
"roles": roles,
|
"roles": roles,
|
||||||
|
"scope": scopes,
|
||||||
"nonce": nonce,
|
"nonce": nonce,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,6 +199,36 @@ func ValidateAccessToken(gc *gin.Context, accessToken string) (map[string]interf
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Function to validate refreshToken
|
||||||
|
func ValidateRefreshToken(gc *gin.Context, refreshToken string) (map[string]interface{}, error) {
|
||||||
|
var res map[string]interface{}
|
||||||
|
|
||||||
|
if refreshToken == "" {
|
||||||
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
|
}
|
||||||
|
|
||||||
|
savedSession := sessionstore.GetState(refreshToken)
|
||||||
|
if savedSession == "" {
|
||||||
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
|
}
|
||||||
|
|
||||||
|
savedSessionSplit := strings.Split(savedSession, "@")
|
||||||
|
nonce := savedSessionSplit[0]
|
||||||
|
userID := savedSessionSplit[1]
|
||||||
|
|
||||||
|
hostname := utils.GetHost(gc)
|
||||||
|
res, err := ParseJWTToken(refreshToken, hostname, nonce, userID)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if res["token_type"] != constants.TokenTypeRefreshToken {
|
||||||
|
return res, fmt.Errorf(`unauthorized: invalid token type`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionData, error) {
|
func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionData, error) {
|
||||||
if encryptedSession == "" {
|
if encryptedSession == "" {
|
||||||
return nil, fmt.Errorf(`unauthorized`)
|
return nil, fmt.Errorf(`unauthorized`)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user