fix: user session access
This commit is contained in:
parent
ac49b5bb70
commit
82a2a42f84
|
@ -7,4 +7,6 @@ const (
|
|||
TokenTypeAccessToken = "access_token"
|
||||
// TokenTypeIdentityToken is the identity_token token type
|
||||
TokenTypeIdentityToken = "id_token"
|
||||
// TokenTypeSessionToken is the session_token type used for browser session
|
||||
TokenTypeSessionToken = "session_token"
|
||||
)
|
||||
|
|
|
@ -38,7 +38,6 @@ func (user *User) AsAPIUser() *model.User {
|
|||
email := user.Email
|
||||
createdAt := user.CreatedAt
|
||||
updatedAt := user.UpdatedAt
|
||||
revokedTimestamp := user.RevokedTimestamp
|
||||
return &model.User{
|
||||
ID: user.ID,
|
||||
Email: user.Email,
|
||||
|
@ -55,7 +54,7 @@ func (user *User) AsAPIUser() *model.User {
|
|||
PhoneNumberVerified: &isPhoneVerified,
|
||||
Picture: user.Picture,
|
||||
Roles: strings.Split(user.Roles, ","),
|
||||
RevokedTimestamp: revokedTimestamp,
|
||||
RevokedTimestamp: user.RevokedTimestamp,
|
||||
CreatedAt: &createdAt,
|
||||
UpdatedAt: &updatedAt,
|
||||
}
|
||||
|
|
|
@ -97,7 +97,6 @@ func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error)
|
|||
func (p *provider) GetUserByEmail(email string) (models.User, error) {
|
||||
var user models.User
|
||||
result := p.db.Where("email = ?", email).First(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
return user, result.Error
|
||||
}
|
||||
|
@ -110,7 +109,6 @@ func (p *provider) GetUserByID(id string) (models.User, error) {
|
|||
var user models.User
|
||||
|
||||
result := p.db.Where("id = ?", id).First(&user)
|
||||
|
||||
if result.Error != nil {
|
||||
return user, result.Error
|
||||
}
|
||||
|
|
|
@ -222,10 +222,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||
// based on the response type, generate the response
|
||||
if isResponseTypeCode {
|
||||
// rollover the session for security
|
||||
err = memorystore.Provider.RemoveState(sessionToken)
|
||||
if err != nil {
|
||||
log.Debug("Failed to remove state: ", err)
|
||||
}
|
||||
go memorystore.Provider.DeleteUserSession(user.ID, claims.Nonce)
|
||||
nonce := uuid.New().String()
|
||||
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope)
|
||||
if err != nil {
|
||||
|
@ -246,7 +243,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
memorystore.Provider.SetUserSession(user.ID, newSessionToken, newSessionTokenData.Nonce)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken)
|
||||
cookie.SetSession(gc, newSessionToken)
|
||||
code := uuid.New().String()
|
||||
memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken)
|
||||
|
@ -283,9 +280,9 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||
}
|
||||
return
|
||||
}
|
||||
memorystore.Provider.RemoveState(sessionToken)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||
go memorystore.Provider.DeleteUserSession(user.ID, claims.Nonce)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
|
||||
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||
|
@ -308,7 +305,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
|||
if authToken.RefreshToken != nil {
|
||||
res["refresh_token"] = authToken.RefreshToken.Token
|
||||
params += "&refresh_token=" + authToken.RefreshToken.Token
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
}
|
||||
|
||||
if isQuery {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
|
@ -10,6 +11,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/cookie"
|
||||
"github.com/authorizerdev/authorizer/server/crypto"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
)
|
||||
|
||||
// Handler to logout user
|
||||
|
@ -35,12 +37,17 @@ func LogoutHandler() gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
fingerPrint := string(decryptedFingerPrint)
|
||||
|
||||
err = memorystore.Provider.RemoveState(fingerPrint)
|
||||
var sessionData token.SessionData
|
||||
err = json.Unmarshal([]byte(decryptedFingerPrint), &sessionData)
|
||||
if err != nil {
|
||||
log.Debug("Failed to remove state: ", err)
|
||||
log.Debug("Failed to decrypt fingerprint: ", err)
|
||||
gc.JSON(http.StatusUnauthorized, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
memorystore.Provider.DeleteUserSession(sessionData.Subject, sessionData.Nonce)
|
||||
cookie.DeleteSession(gc)
|
||||
|
||||
if redirectURL != "" {
|
||||
|
|
|
@ -36,7 +36,6 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||
log.Debug("Invalid oauth state: ", state)
|
||||
c.JSON(400, gin.H{"error": "invalid oauth state"})
|
||||
}
|
||||
memorystore.Provider.GetState(state)
|
||||
// contains random token, redirect url, role
|
||||
sessionSplit := strings.Split(state, "___")
|
||||
|
||||
|
@ -46,6 +45,9 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
// remove state from store
|
||||
go memorystore.Provider.RemoveState(state)
|
||||
|
||||
stateValue := sessionSplit[0]
|
||||
redirectURL := sessionSplit[1]
|
||||
inputRoles := strings.Split(sessionSplit[2], ",")
|
||||
|
@ -117,9 +119,11 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||
user.EmailVerifiedAt = &now
|
||||
user, _ = db.Provider.AddUser(user)
|
||||
} else {
|
||||
user = existingUser
|
||||
if user.RevokedTimestamp != nil {
|
||||
log.Debug("User access revoked at: ", user.RevokedTimestamp)
|
||||
c.JSON(400, gin.H{"error": "user access has been revoked"})
|
||||
return
|
||||
}
|
||||
|
||||
// user exists in db, check if method was google
|
||||
|
@ -128,7 +132,6 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||
if !strings.Contains(signupMethod, provider) {
|
||||
signupMethod = signupMethod + "," + provider
|
||||
}
|
||||
user = existingUser
|
||||
user.SignupMethods = signupMethod
|
||||
|
||||
if user.EmailVerifiedAt == nil {
|
||||
|
@ -200,12 +203,12 @@ 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
|
||||
|
||||
cookie.SetSession(c, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
params = params + `&refresh_token=` + authToken.RefreshToken.Token
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
}
|
||||
|
||||
go db.Provider.AddSession(models.Session{
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
)
|
||||
|
||||
// Revoke handler to revoke refresh token
|
||||
|
@ -45,7 +46,17 @@ func RevokeHandler() gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
memorystore.Provider.RemoveState(refreshToken)
|
||||
claims, err := token.ParseJWTToken(refreshToken)
|
||||
if err != nil {
|
||||
log.Debug("Client ID is invalid: ", clientID)
|
||||
gc.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": err.Error(),
|
||||
"error_description": "Failed to parse jwt",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
memorystore.Provider.DeleteUserSession(claims["sub"].(string), claims["nonce"].(string))
|
||||
|
||||
gc.JSON(http.StatusOK, gin.H{
|
||||
"message": "Token revoked successfully",
|
||||
|
|
|
@ -107,6 +107,7 @@ func TokenHandler() gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
go memorystore.Provider.RemoveState(encryptedCode)
|
||||
// split session data
|
||||
// it contains code@sessiontoken
|
||||
sessionDataSplit := strings.Split(sessionData, "@")
|
||||
|
@ -130,11 +131,11 @@ func TokenHandler() gin.HandlerFunc {
|
|||
})
|
||||
return
|
||||
}
|
||||
// rollover the session for security
|
||||
memorystore.Provider.RemoveState(sessionDataSplit[1])
|
||||
userID = claims.Subject
|
||||
roles = claims.Roles
|
||||
scope = claims.Scope
|
||||
// rollover the session for security
|
||||
go memorystore.Provider.DeleteUserSession(userID, claims.Nonce)
|
||||
} else {
|
||||
// validate refresh token
|
||||
if refreshToken == "" {
|
||||
|
@ -163,7 +164,7 @@ func TokenHandler() gin.HandlerFunc {
|
|||
scope = append(scope, v.(string))
|
||||
}
|
||||
// remove older refresh token and rotate it for security
|
||||
memorystore.Provider.RemoveState(refreshToken)
|
||||
go memorystore.Provider.DeleteUserSession(userID, claims["nonce"].(string))
|
||||
}
|
||||
|
||||
user, err := db.Provider.GetUserByID(userID)
|
||||
|
@ -185,8 +186,8 @@ func TokenHandler() gin.HandlerFunc {
|
|||
})
|
||||
return
|
||||
}
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
|
||||
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||
|
@ -204,7 +205,7 @@ func TokenHandler() gin.HandlerFunc {
|
|||
|
||||
if authToken.RefreshToken != nil {
|
||||
res["refresh_token"] = authToken.RefreshToken.Token
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
}
|
||||
|
||||
gc.JSON(http.StatusOK, res)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/cookie"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
|
@ -107,12 +108,12 @@ 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
|
||||
|
||||
cookie.SetSession(c, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
params = params + `&refresh_token=${refresh_token}`
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
|
||||
params = params + `&refresh_token=` + authToken.RefreshToken.Token
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
}
|
||||
|
||||
if redirectURL == "" {
|
||||
|
|
|
@ -7,9 +7,9 @@ type Provider interface {
|
|||
// GetAllUserSessions returns all the user sessions from the session store
|
||||
GetAllUserSessions(userId string) (map[string]string, error)
|
||||
// GetUserSession returns the session token for given token
|
||||
GetUserSession(userId, token string) (string, error)
|
||||
GetUserSession(userId, key string) (string, error)
|
||||
// DeleteUserSession deletes the user session
|
||||
DeleteUserSession(userId, token string) error
|
||||
DeleteUserSession(userId, key string) error
|
||||
// DeleteAllSessions deletes all the sessions from the session store
|
||||
DeleteAllUserSessions(userId string) error
|
||||
|
||||
|
|
|
@ -37,18 +37,24 @@ func (c *provider) GetAllUserSessions(userID string) (map[string]string, error)
|
|||
|
||||
// GetUserSession returns the user session from redis store.
|
||||
func (c *provider) GetUserSession(userId, key string) (string, error) {
|
||||
var res string
|
||||
err := c.store.HGet(c.ctx, userId, key).Scan(&res)
|
||||
data, err := c.store.HGet(c.ctx, userId, key).Result()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return res, nil
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// DeleteUserSession deletes the user session from redis store.
|
||||
func (c *provider) DeleteUserSession(userId, key string) error {
|
||||
err := c.store.HDel(c.ctx, userId, key).Err()
|
||||
if err != nil {
|
||||
if err := c.store.HDel(c.ctx, userId, constants.TokenTypeSessionToken+"_"+key).Err(); err != nil {
|
||||
log.Debug("Error deleting user session from redis: ", err)
|
||||
return err
|
||||
}
|
||||
if err := c.store.HDel(c.ctx, userId, constants.TokenTypeAccessToken+"_"+key).Err(); err != nil {
|
||||
log.Debug("Error deleting user session from redis: ", err)
|
||||
return err
|
||||
}
|
||||
if err := c.store.HDel(c.ctx, userId, constants.TokenTypeRefreshToken+"_"+key).Err(); err != nil {
|
||||
log.Debug("Error deleting user session from redis: ", err)
|
||||
return err
|
||||
}
|
||||
|
@ -57,7 +63,7 @@ func (c *provider) DeleteUserSession(userId, key string) error {
|
|||
|
||||
// DeleteAllUserSessions deletes all the user session from redis
|
||||
func (c *provider) DeleteAllUserSessions(userID string) error {
|
||||
err := c.store.HDel(c.ctx, userID).Err()
|
||||
err := c.store.Del(c.ctx, userID).Err()
|
||||
if err != nil {
|
||||
log.Debug("Error deleting all user sessions from redis: ", err)
|
||||
return err
|
||||
|
@ -78,13 +84,13 @@ func (c *provider) SetState(key, value string) error {
|
|||
|
||||
// GetState gets the state from redis store.
|
||||
func (c *provider) GetState(key string) (string, error) {
|
||||
var res string
|
||||
err := c.store.Get(c.ctx, stateStorePrefix+key).Scan(&res)
|
||||
data, err := c.store.Get(c.ctx, stateStorePrefix+key).Result()
|
||||
if err != nil {
|
||||
log.Debug("error getting token from redis store: ", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return res, err
|
||||
return data, err
|
||||
}
|
||||
|
||||
// RemoveState removes the state from redis store.
|
||||
|
@ -142,22 +148,20 @@ func (c *provider) UpdateEnvVariable(key string, value interface{}) error {
|
|||
|
||||
// GetStringStoreEnvVariable to get the string env variable from env store
|
||||
func (c *provider) GetStringStoreEnvVariable(key string) (string, error) {
|
||||
var res string
|
||||
err := c.store.HGet(c.ctx, envStorePrefix, key).Scan(&res)
|
||||
data, err := c.store.HGet(c.ctx, envStorePrefix, key).Result()
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// GetBoolStoreEnvVariable to get the bool env variable from env store
|
||||
func (c *provider) GetBoolStoreEnvVariable(key string) (bool, error) {
|
||||
var res bool
|
||||
err := c.store.HGet(c.ctx, envStorePrefix, key).Scan(res)
|
||||
data, err := c.store.HGet(c.ctx, envStorePrefix, key).Result()
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return res, nil
|
||||
return data == "1", nil
|
||||
}
|
||||
|
|
|
@ -117,12 +117,12 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||
}
|
||||
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
res.RefreshToken = &authToken.RefreshToken.Token
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
}
|
||||
|
||||
go db.Provider.AddSession(models.Session{
|
||||
|
|
|
@ -41,7 +41,7 @@ func LogoutResolver(ctx context.Context) (*model.Response, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
memorystore.Provider.DeleteUserSession(sessionData.Subject, fingerprintHash)
|
||||
memorystore.Provider.DeleteUserSession(sessionData.Subject, sessionData.Nonce)
|
||||
cookie.DeleteSession(gc)
|
||||
|
||||
res := &model.Response{
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/cookie"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
|
@ -29,7 +30,7 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
|
|||
|
||||
sessionToken, err := cookie.GetSession(gc)
|
||||
if err != nil {
|
||||
log.Debug("Failed to get session token", err)
|
||||
log.Debug("Failed to get session token: ", err)
|
||||
return res, errors.New("unauthorized")
|
||||
}
|
||||
|
||||
|
@ -76,10 +77,7 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
|
|||
}
|
||||
|
||||
// rollover the session for security
|
||||
memorystore.Provider.RemoveState(sessionToken)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
go memorystore.Provider.DeleteUserSession(userID, claims.Nonce)
|
||||
|
||||
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||
if expiresIn <= 0 {
|
||||
|
@ -94,10 +92,13 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
|
|||
User: user.AsAPIUser(),
|
||||
}
|
||||
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
res.RefreshToken = &authToken.RefreshToken.Token
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
@ -225,15 +225,6 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
|
|||
return res, err
|
||||
}
|
||||
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
res.RefreshToken = &authToken.RefreshToken.Token
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
|
||||
}
|
||||
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
go db.Provider.AddSession(models.Session{
|
||||
UserID: user.ID,
|
||||
UserAgent: utils.GetUserAgent(gc.Request),
|
||||
|
@ -251,6 +242,15 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
|
|||
ExpiresIn: &expiresIn,
|
||||
User: userToReturn,
|
||||
}
|
||||
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
res.RefreshToken = &authToken.RefreshToken.Token
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/golang-jwt/jwt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/parsers"
|
||||
|
@ -29,7 +30,7 @@ func ValidateJwtTokenResolver(ctx context.Context, params model.ValidateJWTToken
|
|||
}
|
||||
|
||||
tokenType := params.TokenType
|
||||
if tokenType != "access_token" && tokenType != "refresh_token" && tokenType != "id_token" {
|
||||
if tokenType != constants.TokenTypeAccessToken && tokenType != constants.TokenTypeRefreshToken && tokenType != constants.TokenTypeIdentityToken {
|
||||
log.Debug("Invalid token type: ", tokenType)
|
||||
return nil, errors.New("invalid token type")
|
||||
}
|
||||
|
@ -38,39 +39,34 @@ func ValidateJwtTokenResolver(ctx context.Context, params model.ValidateJWTToken
|
|||
var claims jwt.MapClaims
|
||||
userID := ""
|
||||
nonce := ""
|
||||
|
||||
claims, err = token.ParseJWTToken(params.Token)
|
||||
if err != nil {
|
||||
log.Debug("Failed to parse JWT token: ", err)
|
||||
return nil, err
|
||||
}
|
||||
userID = claims["sub"].(string)
|
||||
|
||||
// access_token and refresh_token should be validated from session store as well
|
||||
if tokenType == "access_token" || tokenType == "refresh_token" {
|
||||
claims, err = token.ParseJWTToken(params.Token)
|
||||
if err != nil {
|
||||
log.Debug("Failed to parse JWT token: ", err)
|
||||
return nil, err
|
||||
}
|
||||
userID = claims["sub"].(string)
|
||||
nonce, err = memorystore.Provider.GetUserSession(userID, params.Token)
|
||||
if err != nil || nonce == "" {
|
||||
if tokenType == constants.TokenTypeAccessToken || tokenType == constants.TokenTypeRefreshToken {
|
||||
nonce = claims["nonce"].(string)
|
||||
token, err := memorystore.Provider.GetUserSession(userID, tokenType+"_"+claims["nonce"].(string))
|
||||
if err != nil || token == "" {
|
||||
log.Debug("Failed to get user session: ", err)
|
||||
return nil, errors.New("invalid token")
|
||||
}
|
||||
} else {
|
||||
// for ID token just parse jwt
|
||||
claims, err = token.ParseJWTToken(params.Token)
|
||||
if err != nil {
|
||||
log.Debug("Failed to parse JWT token: ", err)
|
||||
return nil, err
|
||||
}
|
||||
userID = claims["sub"].(string)
|
||||
}
|
||||
|
||||
hostname := parsers.GetHost(gc)
|
||||
|
||||
// we cannot validate sub and nonce in case of id_token as that token is not persisted in session store
|
||||
if userID != "" && nonce != "" {
|
||||
// we cannot validate nonce in case of id_token as that token is not persisted in session store
|
||||
if nonce != "" {
|
||||
if ok, err := token.ValidateJWTClaims(claims, hostname, nonce, userID); !ok || err != nil {
|
||||
log.Debug("Failed to parse jwt token: ", err)
|
||||
return nil, errors.New("invalid claims")
|
||||
}
|
||||
} else {
|
||||
if ok, err := token.ValidateJWTTokenWithoutNonce(claims, hostname); !ok || err != nil {
|
||||
if ok, err := token.ValidateJWTTokenWithoutNonce(claims, hostname, userID); !ok || err != nil {
|
||||
log.Debug("Failed to parse jwt token without nonce: ", err)
|
||||
return nil, errors.New("invalid claims")
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/cookie"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
|
@ -80,15 +81,6 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
|
|||
return res, err
|
||||
}
|
||||
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
res.RefreshToken = &authToken.RefreshToken.Token
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
|
||||
}
|
||||
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
go db.Provider.AddSession(models.Session{
|
||||
UserID: user.ID,
|
||||
UserAgent: utils.GetUserAgent(gc.Request),
|
||||
|
@ -107,5 +99,14 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
|
|||
ExpiresIn: &expiresIn,
|
||||
User: user.AsAPIUser(),
|
||||
}
|
||||
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
res.RefreshToken = &authToken.RefreshToken.Token
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -28,17 +29,18 @@ func logoutTests(t *testing.T, s TestSetup) {
|
|||
Token: verificationRequest.Token,
|
||||
})
|
||||
|
||||
token := *verifyRes.AccessToken
|
||||
sessions, err := memorystore.Provider.GetAllUserSessions(verifyRes.User.ID)
|
||||
accessToken := *verifyRes.AccessToken
|
||||
assert.NotEmpty(t, accessToken)
|
||||
|
||||
claims, err := token.ParseJWTToken(accessToken)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, sessions)
|
||||
cookie := ""
|
||||
// set all they keys in cookie one of them should be session cookie
|
||||
for key := range sessions {
|
||||
if key != token {
|
||||
cookie += fmt.Sprintf("%s=%s;", constants.AppCookieName+"_session", key)
|
||||
}
|
||||
}
|
||||
assert.NotEmpty(t, claims)
|
||||
|
||||
sessionToken, err := memorystore.Provider.GetUserSession(verifyRes.User.ID, constants.TokenTypeSessionToken+"_"+claims["nonce"].(string))
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, sessionToken)
|
||||
|
||||
cookie := fmt.Sprintf("%s=%s;", constants.AppCookieName+"_session", sessionToken)
|
||||
cookie = strings.TrimSuffix(cookie, ";")
|
||||
|
||||
req.Header.Set("Cookie", cookie)
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -33,17 +34,18 @@ func sessionTests(t *testing.T, s TestSetup) {
|
|||
Token: verificationRequest.Token,
|
||||
})
|
||||
|
||||
token := *verifyRes.AccessToken
|
||||
sessions, err := memorystore.Provider.GetAllUserSessions(verifyRes.User.ID)
|
||||
accessToken := *verifyRes.AccessToken
|
||||
assert.NotEmpty(t, accessToken)
|
||||
|
||||
claims, err := token.ParseJWTToken(accessToken)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, sessions)
|
||||
cookie := ""
|
||||
// set all they keys in cookie one of them should be session cookie
|
||||
for key := range sessions {
|
||||
if key != token {
|
||||
cookie += fmt.Sprintf("%s=%s;", constants.AppCookieName+"_session", key)
|
||||
}
|
||||
}
|
||||
assert.NotEmpty(t, claims)
|
||||
|
||||
sessionToken, err := memorystore.Provider.GetUserSession(verifyRes.User.ID, constants.TokenTypeSessionToken+"_"+claims["nonce"].(string))
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, sessionToken)
|
||||
|
||||
cookie := fmt.Sprintf("%s=%s;", constants.AppCookieName+"_session", sessionToken)
|
||||
cookie = strings.TrimSuffix(cookie, ";")
|
||||
|
||||
req.Header.Set("Cookie", cookie)
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||
|
@ -50,9 +51,12 @@ func validateJwtTokenTest(t *testing.T, s TestSetup) {
|
|||
gc, err := utils.GinContextFromContext(ctx)
|
||||
assert.NoError(t, err)
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
}
|
||||
|
||||
t.Run(`should validate the access token`, func(t *testing.T) {
|
||||
res, err := resolvers.ValidateJwtTokenResolver(ctx, model.ValidateJWTTokenInput{
|
||||
|
|
|
@ -204,11 +204,16 @@ func ValidateAccessToken(gc *gin.Context, accessToken string) (map[string]interf
|
|||
}
|
||||
|
||||
userID := res["sub"].(string)
|
||||
nonce, err := memorystore.Provider.GetUserSession(userID, accessToken)
|
||||
nonce := res["nonce"].(string)
|
||||
token, err := memorystore.Provider.GetUserSession(userID, constants.TokenTypeAccessToken+"_"+nonce)
|
||||
if nonce == "" || err != nil {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
if token != accessToken {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
hostname := parsers.GetHost(gc)
|
||||
if ok, err := ValidateJWTClaims(res, hostname, nonce, userID); !ok || err != nil {
|
||||
return res, err
|
||||
|
@ -235,11 +240,16 @@ func ValidateRefreshToken(gc *gin.Context, refreshToken string) (map[string]inte
|
|||
}
|
||||
|
||||
userID := res["sub"].(string)
|
||||
nonce, err := memorystore.Provider.GetUserSession(userID, refreshToken)
|
||||
nonce := res["nonce"].(string)
|
||||
token, err := memorystore.Provider.GetUserSession(userID, constants.TokenTypeRefreshToken+"_"+nonce)
|
||||
if nonce == "" || err != nil {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
if token != refreshToken {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
hostname := parsers.GetHost(gc)
|
||||
if ok, err := ValidateJWTClaims(res, hostname, nonce, userID); !ok || err != nil {
|
||||
return res, err
|
||||
|
@ -268,13 +278,13 @@ func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionD
|
|||
return nil, err
|
||||
}
|
||||
|
||||
nonce, err := memorystore.Provider.GetUserSession(res.Subject, encryptedSession)
|
||||
if nonce == "" || err != nil {
|
||||
token, err := memorystore.Provider.GetUserSession(res.Subject, constants.TokenTypeSessionToken+"_"+res.Nonce)
|
||||
if token == "" || err != nil {
|
||||
log.Debug("invalid browser session:", err)
|
||||
return nil, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
if res.Nonce != nonce {
|
||||
if encryptedSession != token {
|
||||
return nil, fmt.Errorf(`unauthorized: invalid nonce`)
|
||||
}
|
||||
|
||||
|
|
|
@ -145,8 +145,8 @@ func ValidateJWTClaims(claims jwt.MapClaims, hostname, nonce, subject string) (b
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// ValidateJWTClaimsWithoutNonce common util to validate claims without nonce
|
||||
func ValidateJWTTokenWithoutNonce(claims jwt.MapClaims, hostname string) (bool, error) {
|
||||
// ValidateJWTTokenWithoutNonce common util to validate claims without nonce
|
||||
func ValidateJWTTokenWithoutNonce(claims jwt.MapClaims, hostname, subject string) (bool, error) {
|
||||
clientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -159,5 +159,8 @@ func ValidateJWTTokenWithoutNonce(claims jwt.MapClaims, hostname string) (bool,
|
|||
return false, errors.New("invalid issuer")
|
||||
}
|
||||
|
||||
if claims["sub"] != subject {
|
||||
return false, errors.New("invalid subject")
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user