fix: add namespace to session token keys

This commit is contained in:
Lakhan Samani 2022-06-29 22:24:00 +05:30
parent e6a4670ba9
commit 2a5d5d43b0
24 changed files with 258 additions and 149 deletions

View File

@ -0,0 +1,18 @@
package constants
const (
// AuthRecipeMethodBasicAuth is the basic_auth auth method
AuthRecipeMethodBasicAuth = "basic_auth"
// AuthRecipeMethodMagicLinkLogin is the magic_link_login auth method
AuthRecipeMethodMagicLinkLogin = "magic_link_login"
// AuthRecipeMethodGoogle is the google auth method
AuthRecipeMethodGoogle = "google"
// AuthRecipeMethodGithub is the github auth method
AuthRecipeMethodGithub = "github"
// AuthRecipeMethodFacebook is the facebook auth method
AuthRecipeMethodFacebook = "facebook"
// AuthRecipeMethodLinkedin is the linkedin auth method
AuthRecipeMethodLinkedIn = "linkedin"
// AuthRecipeMethodApple is the apple auth method
AuthRecipeMethodApple = "apple"
)

View File

@ -1,18 +0,0 @@
package constants
const (
// SignupMethodBasicAuth is the basic_auth signup method
SignupMethodBasicAuth = "basic_auth"
// SignupMethodMagicLinkLogin is the magic_link_login signup method
SignupMethodMagicLinkLogin = "magic_link_login"
// SignupMethodGoogle is the google signup method
SignupMethodGoogle = "google"
// SignupMethodGithub is the github signup method
SignupMethodGithub = "github"
// SignupMethodFacebook is the facebook signup method
SignupMethodFacebook = "facebook"
// SignupMethodLinkedin is the linkedin signup method
SignupMethodLinkedIn = "linkedin"
// SignupMethodApple is the apple signup method
SignupMethodApple = "apple"
)

View File

@ -218,13 +218,18 @@ func AuthorizeHandler() gin.HandlerFunc {
return return
} }
sessionKey := user.ID
if claims.LoginMethod != "" {
sessionKey = claims.LoginMethod + ":" + user.ID
}
// if user is logged in // if user is logged in
// based on the response type code, generate the response // based on the response type code, generate the response
if isResponseTypeCode { if isResponseTypeCode {
// rollover the session for security // rollover the session for security
go memorystore.Provider.DeleteUserSession(user.ID, claims.Nonce) go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
nonce := uuid.New().String() nonce := uuid.New().String()
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope) newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope, claims.LoginMethod)
if err != nil { if err != nil {
if isQuery { if isQuery {
gc.Redirect(http.StatusFound, loginURL) gc.Redirect(http.StatusFound, loginURL)
@ -262,7 +267,7 @@ func AuthorizeHandler() gin.HandlerFunc {
if isResponseTypeToken { if isResponseTypeToken {
// rollover the session for security // rollover the session for security
authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope) authToken, err := token.CreateAuthToken(gc, user, claims.Roles, scope, claims.LoginMethod)
if err != nil { if err != nil {
if isQuery { if isQuery {
gc.Redirect(http.StatusFound, loginURL) gc.Redirect(http.StatusFound, loginURL)
@ -280,9 +285,10 @@ func AuthorizeHandler() gin.HandlerFunc {
} }
return return
} }
go memorystore.Provider.DeleteUserSession(user.ID, claims.Nonce)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
cookie.SetSession(gc, authToken.FingerPrintHash) cookie.SetSession(gc, authToken.FingerPrintHash)
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix() expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
@ -305,7 +311,7 @@ func AuthorizeHandler() gin.HandlerFunc {
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
res["refresh_token"] = authToken.RefreshToken.Token res["refresh_token"] = authToken.RefreshToken.Token
params += "&refresh_token=" + authToken.RefreshToken.Token params += "&refresh_token=" + authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
} }
if isQuery { if isQuery {

View File

@ -57,15 +57,15 @@ func OAuthCallbackHandler() gin.HandlerFunc {
user := models.User{} user := models.User{}
code := c.Request.FormValue("code") code := c.Request.FormValue("code")
switch provider { switch provider {
case constants.SignupMethodGoogle: case constants.AuthRecipeMethodGoogle:
user, err = processGoogleUserInfo(code) user, err = processGoogleUserInfo(code)
case constants.SignupMethodGithub: case constants.AuthRecipeMethodGithub:
user, err = processGithubUserInfo(code) user, err = processGithubUserInfo(code)
case constants.SignupMethodFacebook: case constants.AuthRecipeMethodFacebook:
user, err = processFacebookUserInfo(code) user, err = processFacebookUserInfo(code)
case constants.SignupMethodLinkedIn: case constants.AuthRecipeMethodLinkedIn:
user, err = processLinkedInUserInfo(code) user, err = processLinkedInUserInfo(code)
case constants.SignupMethodApple: case constants.AuthRecipeMethodApple:
user, err = processAppleUserInfo(code) user, err = processAppleUserInfo(code)
default: default:
log.Info("Invalid oauth provider") log.Info("Invalid oauth provider")
@ -192,7 +192,7 @@ func OAuthCallbackHandler() gin.HandlerFunc {
} }
} }
authToken, err := token.CreateAuthToken(c, user, inputRoles, scopes) authToken, err := token.CreateAuthToken(c, user, inputRoles, scopes, provider)
if err != nil { if err != nil {
log.Debug("Failed to create auth token: ", err) log.Debug("Failed to create auth token: ", err)
c.JSON(500, gin.H{"error": err.Error()}) c.JSON(500, gin.H{"error": err.Error()})
@ -205,13 +205,14 @@ func OAuthCallbackHandler() gin.HandlerFunc {
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token
sessionKey := provider + ":" + user.ID
cookie.SetSession(c, authToken.FingerPrintHash) cookie.SetSession(c, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
params = params + `&refresh_token=` + authToken.RefreshToken.Token params = params + `&refresh_token=` + authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
} }
go db.Provider.AddSession(models.Session{ go db.Provider.AddSession(models.Session{

View File

@ -100,13 +100,13 @@ func OAuthLoginHandler() gin.HandlerFunc {
provider := c.Param("oauth_provider") provider := c.Param("oauth_provider")
isProviderConfigured := true isProviderConfigured := true
switch provider { switch provider {
case constants.SignupMethodGoogle: case constants.AuthRecipeMethodGoogle:
if oauth.OAuthProviders.GoogleConfig == nil { if oauth.OAuthProviders.GoogleConfig == nil {
log.Debug("Google OAuth provider is not configured") log.Debug("Google OAuth provider is not configured")
isProviderConfigured = false isProviderConfigured = false
break break
} }
err := memorystore.Provider.SetState(oauthStateString, constants.SignupMethodGoogle) err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodGoogle)
if err != nil { if err != nil {
log.Debug("Error setting state: ", err) log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{ c.JSON(500, gin.H{
@ -115,16 +115,16 @@ func OAuthLoginHandler() gin.HandlerFunc {
return return
} }
// during the init of OAuthProvider authorizer url might be empty // during the init of OAuthProvider authorizer url might be empty
oauth.OAuthProviders.GoogleConfig.RedirectURL = hostname + "/oauth_callback/" + constants.SignupMethodGoogle oauth.OAuthProviders.GoogleConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodGoogle
url := oauth.OAuthProviders.GoogleConfig.AuthCodeURL(oauthStateString) url := oauth.OAuthProviders.GoogleConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url) c.Redirect(http.StatusTemporaryRedirect, url)
case constants.SignupMethodGithub: case constants.AuthRecipeMethodGithub:
if oauth.OAuthProviders.GithubConfig == nil { if oauth.OAuthProviders.GithubConfig == nil {
log.Debug("Github OAuth provider is not configured") log.Debug("Github OAuth provider is not configured")
isProviderConfigured = false isProviderConfigured = false
break break
} }
err := memorystore.Provider.SetState(oauthStateString, constants.SignupMethodGithub) err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodGithub)
if err != nil { if err != nil {
log.Debug("Error setting state: ", err) log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{ c.JSON(500, gin.H{
@ -132,16 +132,16 @@ func OAuthLoginHandler() gin.HandlerFunc {
}) })
return return
} }
oauth.OAuthProviders.GithubConfig.RedirectURL = hostname + "/oauth_callback/" + constants.SignupMethodGithub oauth.OAuthProviders.GithubConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodGithub
url := oauth.OAuthProviders.GithubConfig.AuthCodeURL(oauthStateString) url := oauth.OAuthProviders.GithubConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url) c.Redirect(http.StatusTemporaryRedirect, url)
case constants.SignupMethodFacebook: case constants.AuthRecipeMethodFacebook:
if oauth.OAuthProviders.FacebookConfig == nil { if oauth.OAuthProviders.FacebookConfig == nil {
log.Debug("Facebook OAuth provider is not configured") log.Debug("Facebook OAuth provider is not configured")
isProviderConfigured = false isProviderConfigured = false
break break
} }
err := memorystore.Provider.SetState(oauthStateString, constants.SignupMethodFacebook) err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodFacebook)
if err != nil { if err != nil {
log.Debug("Error setting state: ", err) log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{ c.JSON(500, gin.H{
@ -149,16 +149,16 @@ func OAuthLoginHandler() gin.HandlerFunc {
}) })
return return
} }
oauth.OAuthProviders.FacebookConfig.RedirectURL = hostname + "/oauth_callback/" + constants.SignupMethodFacebook oauth.OAuthProviders.FacebookConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodFacebook
url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString) url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url) c.Redirect(http.StatusTemporaryRedirect, url)
case constants.SignupMethodLinkedIn: case constants.AuthRecipeMethodLinkedIn:
if oauth.OAuthProviders.LinkedInConfig == nil { if oauth.OAuthProviders.LinkedInConfig == nil {
log.Debug("Linkedin OAuth provider is not configured") log.Debug("Linkedin OAuth provider is not configured")
isProviderConfigured = false isProviderConfigured = false
break break
} }
err := memorystore.Provider.SetState(oauthStateString, constants.SignupMethodLinkedIn) err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodLinkedIn)
if err != nil { if err != nil {
log.Debug("Error setting state: ", err) log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{ c.JSON(500, gin.H{
@ -166,16 +166,16 @@ func OAuthLoginHandler() gin.HandlerFunc {
}) })
return return
} }
oauth.OAuthProviders.LinkedInConfig.RedirectURL = hostname + "/oauth_callback/" + constants.SignupMethodLinkedIn oauth.OAuthProviders.LinkedInConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodLinkedIn
url := oauth.OAuthProviders.LinkedInConfig.AuthCodeURL(oauthStateString) url := oauth.OAuthProviders.LinkedInConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url) c.Redirect(http.StatusTemporaryRedirect, url)
case constants.SignupMethodApple: case constants.AuthRecipeMethodApple:
if oauth.OAuthProviders.AppleConfig == nil { if oauth.OAuthProviders.AppleConfig == nil {
log.Debug("Apple OAuth provider is not configured") log.Debug("Apple OAuth provider is not configured")
isProviderConfigured = false isProviderConfigured = false
break break
} }
err := memorystore.Provider.SetState(oauthStateString, constants.SignupMethodApple) err := memorystore.Provider.SetState(oauthStateString, constants.AuthRecipeMethodApple)
if err != nil { if err != nil {
log.Debug("Error setting state: ", err) log.Debug("Error setting state: ", err)
c.JSON(500, gin.H{ c.JSON(500, gin.H{
@ -183,7 +183,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
}) })
return return
} }
oauth.OAuthProviders.AppleConfig.RedirectURL = hostname + "/oauth_callback/" + constants.SignupMethodApple oauth.OAuthProviders.AppleConfig.RedirectURL = hostname + "/oauth_callback/" + constants.AuthRecipeMethodApple
// there is scope encoding issue with oauth2 and how apple expects, hence added scope manually // there is scope encoding issue with oauth2 and how apple expects, hence added scope manually
// check: https://github.com/golang/oauth2/issues/449 // check: https://github.com/golang/oauth2/issues/449
url := oauth.OAuthProviders.AppleConfig.AuthCodeURL(oauthStateString, oauth2.SetAuthURLParam("response_mode", "form_post")) + "&scope=name email" url := oauth.OAuthProviders.AppleConfig.AuthCodeURL(oauthStateString, oauth2.SetAuthURLParam("response_mode", "form_post")) + "&scope=name email"

View File

@ -56,7 +56,14 @@ func RevokeHandler() gin.HandlerFunc {
return return
} }
memorystore.Provider.DeleteUserSession(claims["sub"].(string), claims["nonce"].(string)) userID := claims["sub"].(string)
loginMethod := claims["login_method"]
sessionToken := userID
if loginMethod != nil && loginMethod != "" {
sessionToken = loginMethod.(string) + ":" + userID
}
memorystore.Provider.DeleteUserSession(sessionToken, claims["nonce"].(string))
gc.JSON(http.StatusOK, gin.H{ gc.JSON(http.StatusOK, gin.H{
"message": "Token revoked successfully", "message": "Token revoked successfully",

View File

@ -72,6 +72,9 @@ func TokenHandler() gin.HandlerFunc {
var userID string var userID string
var roles, scope []string var roles, scope []string
loginMethod := ""
sessionKey := ""
if isAuthorizationCodeGrant { if isAuthorizationCodeGrant {
if codeVerifier == "" { if codeVerifier == "" {
@ -134,8 +137,13 @@ func TokenHandler() gin.HandlerFunc {
userID = claims.Subject userID = claims.Subject
roles = claims.Roles roles = claims.Roles
scope = claims.Scope scope = claims.Scope
loginMethod = claims.LoginMethod
// rollover the session for security // rollover the session for security
go memorystore.Provider.DeleteUserSession(userID, claims.Nonce) sessionKey = userID
if loginMethod != "" {
sessionKey = loginMethod + ":" + userID
}
go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
} else { } else {
// validate refresh token // validate refresh token
if refreshToken == "" { if refreshToken == "" {
@ -155,6 +163,7 @@ func TokenHandler() gin.HandlerFunc {
}) })
} }
userID = claims["sub"].(string) userID = claims["sub"].(string)
loginMethod := claims["login_method"]
rolesInterface := claims["roles"].([]interface{}) rolesInterface := claims["roles"].([]interface{})
scopeInterface := claims["scope"].([]interface{}) scopeInterface := claims["scope"].([]interface{})
for _, v := range rolesInterface { for _, v := range rolesInterface {
@ -163,8 +172,22 @@ func TokenHandler() gin.HandlerFunc {
for _, v := range scopeInterface { for _, v := range scopeInterface {
scope = append(scope, v.(string)) scope = append(scope, v.(string))
} }
sessionKey = userID
if loginMethod != nil && loginMethod != "" {
sessionKey = loginMethod.(string) + ":" + sessionKey
}
// remove older refresh token and rotate it for security // remove older refresh token and rotate it for security
go memorystore.Provider.DeleteUserSession(userID, claims["nonce"].(string)) go memorystore.Provider.DeleteUserSession(sessionKey, claims["nonce"].(string))
}
if sessionKey == "" {
log.Debug("Error getting sessionKey: ", sessionKey, loginMethod)
gc.JSON(http.StatusUnauthorized, gin.H{
"error": "unauthorized",
"error_description": "User not found",
})
return
} }
user, err := db.Provider.GetUserByID(userID) user, err := db.Provider.GetUserByID(userID)
@ -177,7 +200,7 @@ func TokenHandler() gin.HandlerFunc {
return return
} }
authToken, err := token.CreateAuthToken(gc, user, roles, scope) authToken, err := token.CreateAuthToken(gc, user, roles, scope, loginMethod)
if err != nil { if err != nil {
log.Debug("Error creating auth token: ", err) log.Debug("Error creating auth token: ", err)
gc.JSON(http.StatusUnauthorized, gin.H{ gc.JSON(http.StatusUnauthorized, gin.H{
@ -186,8 +209,8 @@ func TokenHandler() gin.HandlerFunc {
}) })
return return
} }
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
cookie.SetSession(gc, authToken.FingerPrintHash) cookie.SetSession(gc, authToken.FingerPrintHash)
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix() expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
@ -205,7 +228,7 @@ func TokenHandler() gin.HandlerFunc {
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
res["refresh_token"] = authToken.RefreshToken.Token res["refresh_token"] = authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
} }
gc.JSON(http.StatusOK, res) gc.JSON(http.StatusOK, res)

View File

@ -92,7 +92,11 @@ func VerifyEmailHandler() gin.HandlerFunc {
} else { } else {
scope = strings.Split(scopeString, " ") scope = strings.Split(scopeString, " ")
} }
authToken, err := token.CreateAuthToken(c, user, roles, scope) loginMethod := constants.AuthRecipeMethodBasicAuth
if verificationRequest.Identifier == constants.VerificationTypeMagicLinkLogin {
loginMethod = constants.AuthRecipeMethodMagicLinkLogin
}
authToken, err := token.CreateAuthToken(c, user, roles, scope, loginMethod)
if err != nil { if err != nil {
log.Debug("Error creating auth token: ", err) log.Debug("Error creating auth token: ", err)
errorRes["error_description"] = err.Error() errorRes["error_description"] = err.Error()
@ -107,13 +111,14 @@ func VerifyEmailHandler() gin.HandlerFunc {
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token
sessionKey := loginMethod + ":" + user.ID
cookie.SetSession(c, authToken.FingerPrintHash) cookie.SetSession(c, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
params = params + `&refresh_token=` + authToken.RefreshToken.Token params = params + `&refresh_token=` + authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
} }
if redirectURL == "" { if redirectURL == "" {

View File

@ -26,11 +26,22 @@ func (c *provider) GetUserSession(userId, sessionToken string) (string, error) {
// DeleteAllUserSessions deletes all the user sessions from in-memory store. // DeleteAllUserSessions deletes all the user sessions from in-memory store.
func (c *provider) DeleteAllUserSessions(userId string) error { func (c *provider) DeleteAllUserSessions(userId string) error {
namespaces := []string{
constants.AuthRecipeMethodBasicAuth,
constants.AuthRecipeMethodMagicLinkLogin,
constants.AuthRecipeMethodApple,
constants.AuthRecipeMethodFacebook,
constants.AuthRecipeMethodGithub,
constants.AuthRecipeMethodGoogle,
constants.AuthRecipeMethodLinkedIn,
}
if os.Getenv("ENV") != constants.TestEnv { if os.Getenv("ENV") != constants.TestEnv {
c.mutex.Lock() c.mutex.Lock()
defer c.mutex.Unlock() defer c.mutex.Unlock()
} }
c.sessionStore.RemoveAll(userId) for _, namespace := range namespaces {
c.sessionStore.RemoveAll(namespace + ":" + userId)
}
return nil return nil
} }

View File

@ -63,11 +63,22 @@ func (c *provider) DeleteUserSession(userId, key string) error {
// DeleteAllUserSessions deletes all the user session from redis // DeleteAllUserSessions deletes all the user session from redis
func (c *provider) DeleteAllUserSessions(userID string) error { func (c *provider) DeleteAllUserSessions(userID string) error {
err := c.store.Del(c.ctx, userID).Err() namespaces := []string{
constants.AuthRecipeMethodBasicAuth,
constants.AuthRecipeMethodMagicLinkLogin,
constants.AuthRecipeMethodApple,
constants.AuthRecipeMethodFacebook,
constants.AuthRecipeMethodGithub,
constants.AuthRecipeMethodGoogle,
constants.AuthRecipeMethodLinkedIn,
}
for _, namespace := range namespaces {
err := c.store.Del(c.ctx, namespace+":"+userID).Err()
if err != nil { if err != nil {
log.Debug("Error deleting all user sessions from redis: ", err) log.Debug("Error deleting all user sessions from redis: ", err)
return err return err
} }
}
return nil return nil
} }

View File

@ -129,11 +129,11 @@ func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput)
// use magic link login if that option is on // use magic link login if that option is on
if !isMagicLinkLoginDisabled { if !isMagicLinkLoginDisabled {
user.SignupMethods = constants.SignupMethodMagicLinkLogin user.SignupMethods = constants.AuthRecipeMethodMagicLinkLogin
verificationRequest.Identifier = constants.VerificationTypeMagicLinkLogin verificationRequest.Identifier = constants.VerificationTypeMagicLinkLogin
} else { } else {
// use basic authentication if that option is on // use basic authentication if that option is on
user.SignupMethods = constants.SignupMethodBasicAuth user.SignupMethods = constants.AuthRecipeMethodBasicAuth
verificationRequest.Identifier = constants.VerificationTypeForgotPassword verificationRequest.Identifier = constants.VerificationTypeForgotPassword
verifyEmailURL = appURL + "/setup-password" verifyEmailURL = appURL + "/setup-password"

View File

@ -56,7 +56,7 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
return res, fmt.Errorf(`user access has been revoked`) return res, fmt.Errorf(`user access has been revoked`)
} }
if !strings.Contains(user.SignupMethods, constants.SignupMethodBasicAuth) { if !strings.Contains(user.SignupMethods, constants.AuthRecipeMethodBasicAuth) {
log.Debug("User signup method is not basic auth") log.Debug("User signup method is not basic auth")
return res, fmt.Errorf(`user has not signed up email & password`) return res, fmt.Errorf(`user has not signed up email & password`)
} }
@ -97,7 +97,7 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
scope = params.Scope scope = params.Scope
} }
authToken, err := token.CreateAuthToken(gc, user, roles, scope) authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth)
if err != nil { if err != nil {
log.Debug("Failed to create auth token", err) log.Debug("Failed to create auth token", err)
return res, err return res, err
@ -117,12 +117,13 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
} }
cookie.SetSession(gc, authToken.FingerPrintHash) cookie.SetSession(gc, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) sessionStoreKey := constants.AuthRecipeMethodBasicAuth + ":" + user.ID
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
} }
go db.Provider.AddSession(models.Session{ go db.Provider.AddSession(models.Session{

View File

@ -41,7 +41,12 @@ func LogoutResolver(ctx context.Context) (*model.Response, error) {
return nil, err return nil, err
} }
memorystore.Provider.DeleteUserSession(sessionData.Subject, sessionData.Nonce) sessionKey := sessionData.Subject
if sessionData.LoginMethod != "" {
sessionKey = sessionData.LoginMethod + ":" + sessionData.Subject
}
memorystore.Provider.DeleteUserSession(sessionKey, sessionData.Nonce)
cookie.DeleteSession(gc) cookie.DeleteSession(gc)
res := &model.Response{ res := &model.Response{

View File

@ -70,7 +70,7 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
return res, fmt.Errorf(`signup is disabled for this instance`) return res, fmt.Errorf(`signup is disabled for this instance`)
} }
user.SignupMethods = constants.SignupMethodMagicLinkLogin user.SignupMethods = constants.AuthRecipeMethodMagicLinkLogin
// define roles for new user // define roles for new user
if len(params.Roles) > 0 { if len(params.Roles) > 0 {
// check if roles exists // check if roles exists
@ -158,8 +158,8 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
} }
signupMethod := existingUser.SignupMethods signupMethod := existingUser.SignupMethods
if !strings.Contains(signupMethod, constants.SignupMethodMagicLinkLogin) { if !strings.Contains(signupMethod, constants.AuthRecipeMethodMagicLinkLogin) {
signupMethod = signupMethod + "," + constants.SignupMethodMagicLinkLogin signupMethod = signupMethod + "," + constants.AuthRecipeMethodMagicLinkLogin
} }
user.SignupMethods = signupMethod user.SignupMethods = signupMethod

View File

@ -82,8 +82,8 @@ func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput)
user.Password = &password user.Password = &password
signupMethod := user.SignupMethods signupMethod := user.SignupMethods
if !strings.Contains(signupMethod, constants.SignupMethodBasicAuth) { if !strings.Contains(signupMethod, constants.AuthRecipeMethodBasicAuth) {
signupMethod = signupMethod + "," + constants.SignupMethodBasicAuth signupMethod = signupMethod + "," + constants.AuthRecipeMethodBasicAuth
} }
user.SignupMethods = signupMethod user.SignupMethods = signupMethod

View File

@ -70,14 +70,18 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
scope = params.Scope scope = params.Scope
} }
authToken, err := token.CreateAuthToken(gc, user, claimRoles, scope) authToken, err := token.CreateAuthToken(gc, user, claimRoles, scope, claims.LoginMethod)
if err != nil { if err != nil {
log.Debug("Failed to create auth token: ", err) log.Debug("Failed to create auth token: ", err)
return res, err return res, err
} }
// rollover the session for security // rollover the session for security
go memorystore.Provider.DeleteUserSession(userID, claims.Nonce) sessionKey := userID
if claims.LoginMethod != "" {
sessionKey = claims.LoginMethod + ":" + userID
}
go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix() expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
if expiresIn <= 0 { if expiresIn <= 0 {
@ -93,12 +97,12 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
} }
cookie.SetSession(gc, authToken.FingerPrintHash) cookie.SetSession(gc, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
} }
return res, nil return res, nil
} }

View File

@ -157,7 +157,7 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
user.Picture = params.Picture user.Picture = params.Picture
} }
user.SignupMethods = constants.SignupMethodBasicAuth user.SignupMethods = constants.AuthRecipeMethodBasicAuth
isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification)
if err != nil { if err != nil {
log.Debug("Error getting email verification disabled: ", err) log.Debug("Error getting email verification disabled: ", err)
@ -219,7 +219,7 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
scope = params.Scope scope = params.Scope
} }
authToken, err := token.CreateAuthToken(gc, user, roles, scope) authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth)
if err != nil { if err != nil {
log.Debug("Failed to create auth token: ", err) log.Debug("Failed to create auth token: ", err)
return res, err return res, err
@ -243,13 +243,14 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
User: userToReturn, User: userToReturn,
} }
sessionKey := constants.AuthRecipeMethodBasicAuth + ":" + user.ID
cookie.SetSession(gc, authToken.FingerPrintHash) cookie.SetSession(gc, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
} }
} }

View File

@ -50,7 +50,12 @@ func ValidateJwtTokenResolver(ctx context.Context, params model.ValidateJWTToken
// access_token and refresh_token should be validated from session store as well // access_token and refresh_token should be validated from session store as well
if tokenType == constants.TokenTypeAccessToken || tokenType == constants.TokenTypeRefreshToken { if tokenType == constants.TokenTypeAccessToken || tokenType == constants.TokenTypeRefreshToken {
nonce = claims["nonce"].(string) nonce = claims["nonce"].(string)
token, err := memorystore.Provider.GetUserSession(userID, tokenType+"_"+claims["nonce"].(string)) loginMethod := claims["login_method"]
sessionKey := userID
if loginMethod != nil && loginMethod != "" {
sessionKey = loginMethod.(string) + ":" + userID
}
token, err := memorystore.Provider.GetUserSession(sessionKey, tokenType+"_"+claims["nonce"].(string))
if err != nil || token == "" { if err != nil || token == "" {
log.Debug("Failed to get user session: ", err) log.Debug("Failed to get user session: ", err)
return nil, errors.New("invalid token") return nil, errors.New("invalid token")

View File

@ -73,9 +73,14 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
return res, err return res, err
} }
loginMethod := constants.AuthRecipeMethodBasicAuth
if loginMethod == constants.VerificationTypeMagicLinkLogin {
loginMethod = constants.AuthRecipeMethodMagicLinkLogin
}
roles := strings.Split(user.Roles, ",") roles := strings.Split(user.Roles, ",")
scope := []string{"openid", "email", "profile"} scope := []string{"openid", "email", "profile"}
authToken, err := token.CreateAuthToken(gc, user, roles, scope) authToken, err := token.CreateAuthToken(gc, user, roles, scope, loginMethod)
if err != nil { if err != nil {
log.Debug("Failed to create auth token: ", err) log.Debug("Failed to create auth token: ", err)
return res, err return res, err
@ -100,13 +105,14 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
User: user.AsAPIUser(), User: user.AsAPIUser(),
} }
sessionKey := loginMethod + ":" + user.ID
cookie.SetSession(gc, authToken.FingerPrintHash) cookie.SetSession(gc, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
} }
return res, nil return res, nil
} }

View File

@ -36,7 +36,13 @@ func logoutTests(t *testing.T, s TestSetup) {
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEmpty(t, claims) assert.NotEmpty(t, claims)
sessionToken, err := memorystore.Provider.GetUserSession(verifyRes.User.ID, constants.TokenTypeSessionToken+"_"+claims["nonce"].(string)) loginMethod := claims["login_method"]
sessionKey := verifyRes.User.ID
if loginMethod != nil && loginMethod != "" {
sessionKey = loginMethod.(string) + ":" + verifyRes.User.ID
}
sessionToken, err := memorystore.Provider.GetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+claims["nonce"].(string))
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEmpty(t, sessionToken) assert.NotEmpty(t, sessionToken)

View File

@ -2,6 +2,7 @@ package test
import ( import (
"context" "context"
"fmt"
"testing" "testing"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
@ -36,10 +37,12 @@ func profileTests(t *testing.T, s TestSetup) {
s.GinContext.Request.Header.Set("Authorization", "Bearer "+*verifyRes.AccessToken) s.GinContext.Request.Header.Set("Authorization", "Bearer "+*verifyRes.AccessToken)
ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext) ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext)
profileRes, err := resolvers.ProfileResolver(ctx) profileRes, err := resolvers.ProfileResolver(ctx)
fmt.Println("=> err:", err)
assert.Nil(t, err) assert.Nil(t, err)
assert.NotNil(t, profileRes)
s.GinContext.Request.Header.Set("Authorization", "") s.GinContext.Request.Header.Set("Authorization", "")
fmt.Println("=> res:", profileRes.Email, email)
newEmail := *&profileRes.Email newEmail := profileRes.Email
assert.Equal(t, email, newEmail, "emails should be equal") assert.Equal(t, email, newEmail, "emails should be equal")
cleanData(email) cleanData(email)

View File

@ -41,7 +41,8 @@ func sessionTests(t *testing.T, s TestSetup) {
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEmpty(t, claims) assert.NotEmpty(t, claims)
sessionToken, err := memorystore.Provider.GetUserSession(verifyRes.User.ID, constants.TokenTypeSessionToken+"_"+claims["nonce"].(string)) sessionKey := constants.AuthRecipeMethodBasicAuth + ":" + verifyRes.User.ID
sessionToken, err := memorystore.Provider.GetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+claims["nonce"].(string))
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEmpty(t, sessionToken) assert.NotEmpty(t, sessionToken)

View File

@ -50,12 +50,13 @@ func validateJwtTokenTest(t *testing.T, s TestSetup) {
roles := []string{"user"} roles := []string{"user"}
gc, err := utils.GinContextFromContext(ctx) gc, err := utils.GinContextFromContext(ctx)
assert.NoError(t, err) assert.NoError(t, err)
authToken, err := token.CreateAuthToken(gc, user, roles, scope) sessionKey := constants.AuthRecipeMethodBasicAuth + ":" + user.ID
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth)
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
} }
t.Run(`should validate the access token`, func(t *testing.T) { t.Run(`should validate the access token`, func(t *testing.T) {

View File

@ -44,17 +44,17 @@ type SessionData struct {
Nonce string `json:"nonce"` Nonce string `json:"nonce"`
IssuedAt int64 `json:"iat"` IssuedAt int64 `json:"iat"`
ExpiresAt int64 `json:"exp"` ExpiresAt int64 `json:"exp"`
Provider string `json:"provider"` LoginMethod string `json:"login_method"`
} }
// CreateSessionToken creates a new session token // CreateSessionToken creates a new session token
func CreateSessionToken(user models.User, nonce string, roles, scope []string, provider string) (*SessionData, string, error) { func CreateSessionToken(user models.User, nonce string, roles, scope []string, loginMethod string) (*SessionData, string, error) {
fingerPrintMap := &SessionData{ fingerPrintMap := &SessionData{
Nonce: nonce, Nonce: nonce,
Roles: roles, Roles: roles,
Subject: user.ID, Subject: user.ID,
Scope: scope, Scope: scope,
Provider: provider, LoginMethod: loginMethod,
IssuedAt: time.Now().Unix(), IssuedAt: time.Now().Unix(),
ExpiresAt: time.Now().AddDate(1, 0, 0).Unix(), ExpiresAt: time.Now().AddDate(1, 0, 0).Unix(),
} }
@ -68,19 +68,19 @@ func CreateSessionToken(user models.User, nonce string, roles, scope []string, p
} }
// CreateAuthToken creates a new auth token when userlogs in // CreateAuthToken creates a new auth token when userlogs in
func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string, provider string) (*Token, error) { func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string, loginMethod string) (*Token, error) {
hostname := parsers.GetHost(gc) hostname := parsers.GetHost(gc)
nonce := uuid.New().String() nonce := uuid.New().String()
_, fingerPrintHash, err := CreateSessionToken(user, nonce, roles, scope, provider) _, fingerPrintHash, err := CreateSessionToken(user, nonce, roles, scope, loginMethod)
if err != nil { if err != nil {
return nil, err return nil, err
} }
accessToken, accessTokenExpiresAt, err := CreateAccessToken(user, roles, scope, hostname, nonce, provider) accessToken, accessTokenExpiresAt, err := CreateAccessToken(user, roles, scope, hostname, nonce, loginMethod)
if err != nil { if err != nil {
return nil, err return nil, err
} }
idToken, idTokenExpiresAt, err := CreateIDToken(user, roles, hostname, nonce, provider) idToken, idTokenExpiresAt, err := CreateIDToken(user, roles, hostname, nonce, loginMethod)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -93,7 +93,7 @@ func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string, p
} }
if utils.StringSliceContains(scope, "offline_access") { if utils.StringSliceContains(scope, "offline_access") {
refreshToken, refreshTokenExpiresAt, err := CreateRefreshToken(user, roles, scope, hostname, nonce, provider) refreshToken, refreshTokenExpiresAt, err := CreateRefreshToken(user, roles, scope, hostname, nonce, loginMethod)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -105,7 +105,7 @@ func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string, p
} }
// CreateRefreshToken util to create JWT token // CreateRefreshToken util to create JWT token
func CreateRefreshToken(user models.User, roles, scopes []string, hostname, nonce, provider string) (string, int64, error) { func CreateRefreshToken(user models.User, roles, scopes []string, hostname, nonce, loginMethod 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()
@ -123,7 +123,7 @@ func CreateRefreshToken(user models.User, roles, scopes []string, hostname, nonc
"roles": roles, "roles": roles,
"scope": scopes, "scope": scopes,
"nonce": nonce, "nonce": nonce,
"provider": provider, "login_method": loginMethod,
} }
token, err := SignJWTToken(customClaims) token, err := SignJWTToken(customClaims)
@ -136,7 +136,7 @@ func CreateRefreshToken(user models.User, roles, scopes []string, hostname, nonc
// CreateAccessToken util to create JWT token, based on // CreateAccessToken util to create JWT token, based on
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT // user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
func CreateAccessToken(user models.User, roles, scopes []string, hostName, nonce, provider string) (string, int64, error) { func CreateAccessToken(user models.User, roles, scopes []string, hostName, nonce, loginMethod string) (string, int64, error) {
expireTime, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyAccessTokenExpiryTime) expireTime, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyAccessTokenExpiryTime)
if err != nil { if err != nil {
return "", 0, err return "", 0, err
@ -162,7 +162,7 @@ func CreateAccessToken(user models.User, roles, scopes []string, hostName, nonce
"token_type": constants.TokenTypeAccessToken, "token_type": constants.TokenTypeAccessToken,
"scope": scopes, "scope": scopes,
"roles": roles, "roles": roles,
"provider": provider, "login_method": loginMethod,
} }
token, err := SignJWTToken(customClaims) token, err := SignJWTToken(customClaims)
@ -209,7 +209,13 @@ func ValidateAccessToken(gc *gin.Context, accessToken string) (map[string]interf
userID := res["sub"].(string) userID := res["sub"].(string)
nonce := res["nonce"].(string) nonce := res["nonce"].(string)
token, err := memorystore.Provider.GetUserSession(userID, constants.TokenTypeAccessToken+"_"+nonce) loginMethod := res["login_method"]
sessionKey := userID
if loginMethod != nil && loginMethod != "" {
sessionKey = loginMethod.(string) + ":" + userID
}
token, err := memorystore.Provider.GetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+nonce)
if nonce == "" || err != nil { if nonce == "" || err != nil {
return res, fmt.Errorf(`unauthorized`) return res, fmt.Errorf(`unauthorized`)
} }
@ -245,7 +251,13 @@ func ValidateRefreshToken(gc *gin.Context, refreshToken string) (map[string]inte
userID := res["sub"].(string) userID := res["sub"].(string)
nonce := res["nonce"].(string) nonce := res["nonce"].(string)
token, err := memorystore.Provider.GetUserSession(userID, constants.TokenTypeRefreshToken+"_"+nonce) loginMethod := res["login_method"]
sessionKey := userID
if loginMethod != nil && loginMethod != "" {
sessionKey = loginMethod.(string) + ":" + userID
}
token, err := memorystore.Provider.GetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+nonce)
if nonce == "" || err != nil { if nonce == "" || err != nil {
return res, fmt.Errorf(`unauthorized`) return res, fmt.Errorf(`unauthorized`)
} }
@ -283,8 +295,8 @@ func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionD
} }
sessionStoreKey := res.Subject sessionStoreKey := res.Subject
if res.Provider != "" { if res.LoginMethod != "" {
sessionStoreKey = res.Provider + ":" + res.Subject sessionStoreKey = res.LoginMethod + ":" + res.Subject
} }
token, err := memorystore.Provider.GetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+res.Nonce) token, err := memorystore.Provider.GetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+res.Nonce)
@ -306,7 +318,7 @@ func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionD
// CreateIDToken util to create JWT token, based on // CreateIDToken util to create JWT token, based on
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT // user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
func CreateIDToken(user models.User, roles []string, hostname, nonce, provider string) (string, int64, error) { func CreateIDToken(user models.User, roles []string, hostname, nonce, loginMethod string) (string, int64, error) {
expireTime, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyAccessTokenExpiryTime) expireTime, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyAccessTokenExpiryTime)
if err != nil { if err != nil {
return "", 0, err return "", 0, err
@ -341,7 +353,7 @@ func CreateIDToken(user models.User, roles []string, hostname, nonce, provider s
"iat": time.Now().Unix(), "iat": time.Now().Unix(),
"token_type": constants.TokenTypeIdentityToken, "token_type": constants.TokenTypeIdentityToken,
"allowed_roles": strings.Split(user.Roles, ","), "allowed_roles": strings.Split(user.Roles, ","),
"provider": provider, "login_method": loginMethod,
claimKey: roles, claimKey: roles,
} }