fix: session invalidation

This commit is contained in:
Lakhan Samani
2022-06-11 19:10:39 +05:30
parent 7a2dbea019
commit 926ab07c07
29 changed files with 401 additions and 285 deletions

View File

@@ -38,7 +38,7 @@ func DeleteUserResolver(ctx context.Context, params model.DeleteUserInput) (*mod
return res, err
}
go memorystore.Provider.DeleteAllUserSession(fmt.Sprintf("%x", user.ID))
go memorystore.Provider.DeleteAllUserSessions(user.ID)
err = db.Provider.DeleteUser(user)
if err != nil {

View File

@@ -117,12 +117,12 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
}
cookie.SetSession(gc, authToken.FingerPrintHash)
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
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.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
}
go db.Provider.AddSession(models.Session{

View File

@@ -2,6 +2,7 @@ package resolvers
import (
"context"
"encoding/json"
log "github.com/sirupsen/logrus"
@@ -9,38 +10,41 @@ import (
"github.com/authorizerdev/authorizer/server/crypto"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils"
)
// LogoutResolver is a resolver for logout mutation
func LogoutResolver(ctx context.Context) (*model.Response, error) {
var res *model.Response
gc, err := utils.GinContextFromContext(ctx)
if err != nil {
log.Debug("Failed to get GinContext: ", err)
return res, err
return nil, err
}
// get fingerprint hash
fingerprintHash, err := cookie.GetSession(gc)
if err != nil {
log.Debug("Failed to get fingerprint hash: ", err)
return res, err
return nil, err
}
decryptedFingerPrint, err := crypto.DecryptAES(fingerprintHash)
if err != nil {
log.Debug("Failed to decrypt fingerprint hash: ", err)
return res, err
return nil, err
}
fingerPrint := string(decryptedFingerPrint)
var sessionData token.SessionData
err = json.Unmarshal([]byte(decryptedFingerPrint), &sessionData)
if err != nil {
return nil, err
}
memorystore.Provider.RemoveState(fingerPrint)
memorystore.Provider.DeleteUserSession(sessionData.Subject, fingerprintHash)
cookie.DeleteSession(gc)
res = &model.Response{
res := &model.Response{
Message: "Logged out successfully",
}

View File

@@ -57,12 +57,17 @@ func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput)
// verify if token exists in db
hostname := parsers.GetHost(gc)
claim, err := token.ParseJWTToken(params.Token, hostname, verificationRequest.Nonce, verificationRequest.Email)
claim, err := token.ParseJWTToken(params.Token)
if err != nil {
log.Debug("Failed to parse token: ", err)
return res, fmt.Errorf(`invalid token`)
}
if ok, err := token.ValidateJWTClaims(claim, hostname, verificationRequest.Nonce, verificationRequest.Email); !ok || err != nil {
log.Debug("Failed to validate jwt claims: ", err)
return res, fmt.Errorf(`invalid token`)
}
email := claim["sub"].(string)
log := log.WithFields(log.Fields{
"email": email,

View File

@@ -47,7 +47,7 @@ func RevokeAccessResolver(ctx context.Context, params model.UpdateAccessInput) (
return res, err
}
go memorystore.Provider.DeleteAllUserSession(fmt.Sprintf("%x", user.ID))
go memorystore.Provider.DeleteAllUserSessions(user.ID)
res = &model.Response{
Message: `user access revoked successfully`,

View File

@@ -77,8 +77,8 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
// rollover the session for security
memorystore.Provider.RemoveState(sessionToken)
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
cookie.SetSession(gc, authToken.FingerPrintHash)
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
@@ -96,7 +96,7 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
}
return res, nil

View File

@@ -225,7 +225,14 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
return res, err
}
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
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,

View File

@@ -142,7 +142,7 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
return res, fmt.Errorf("user with this email address already exists")
}
go memorystore.Provider.DeleteAllUserSession(user.ID)
go memorystore.Provider.DeleteAllUserSessions(user.ID)
go cookie.DeleteSession(gc)
user.Email = newEmail

View File

@@ -113,7 +113,7 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
}
// TODO figure out how to do this
go memorystore.Provider.DeleteAllUserSession(user.ID)
go memorystore.Provider.DeleteAllUserSessions(user.ID)
hostname := parsers.GetHost(gc)
user.Email = newEmail
@@ -182,7 +182,7 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
rolesToSave = strings.Join(inputRoles, ",")
}
go memorystore.Provider.DeleteAllUserSession(user.ID)
go memorystore.Provider.DeleteAllUserSessions(user.ID)
}
if rolesToSave != "" {

View File

@@ -4,7 +4,6 @@ import (
"context"
"errors"
"fmt"
"strings"
"github.com/golang-jwt/jwt"
log "github.com/sirupsen/logrus"
@@ -35,43 +34,46 @@ func ValidateJwtTokenResolver(ctx context.Context, params model.ValidateJWTToken
return nil, errors.New("invalid token type")
}
var claimRoles []string
var claims jwt.MapClaims
userID := ""
nonce := ""
// access_token and refresh_token should be validated from session store as well
if tokenType == "access_token" || tokenType == "refresh_token" {
savedSession, err := memorystore.Provider.GetState(params.Token)
if savedSession == "" || err != nil {
return &model.ValidateJWTTokenResponse{
IsValid: false,
}, nil
claims, err = token.ParseJWTToken(params.Token)
if err != nil {
log.Debug("Failed to parse JWT token: ", err)
return nil, err
}
savedSessionSplit := strings.Split(savedSession, "@")
nonce = savedSessionSplit[0]
userID = savedSessionSplit[1]
userID = claims["sub"].(string)
nonce, err = memorystore.Provider.GetUserSession(userID, params.Token)
if err != nil || nonce == "" {
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)
var claimRoles []string
var claims jwt.MapClaims
// we cannot validate sub and nonce in case of id_token as that token is not persisted in session store
if userID != "" && nonce != "" {
claims, err = token.ParseJWTToken(params.Token, hostname, nonce, userID)
if err != nil {
if ok, err := token.ValidateJWTClaims(claims, hostname, nonce, userID); !ok || err != nil {
log.Debug("Failed to parse jwt token: ", err)
return &model.ValidateJWTTokenResponse{
IsValid: false,
}, nil
return nil, errors.New("invalid claims")
}
} else {
claims, err = token.ParseJWTTokenWithoutNonce(params.Token, hostname)
if err != nil {
if ok, err := token.ValidateJWTTokenWithoutNonce(claims, hostname); !ok || err != nil {
log.Debug("Failed to parse jwt token without nonce: ", err)
return &model.ValidateJWTTokenResponse{
IsValid: false,
}, nil
return nil, errors.New("invalid claims")
}
}
claimRolesInterface := claims["roles"]

View File

@@ -36,12 +36,17 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
// verify if token exists in db
hostname := parsers.GetHost(gc)
claim, err := token.ParseJWTToken(params.Token, hostname, verificationRequest.Nonce, verificationRequest.Email)
claim, err := token.ParseJWTToken(params.Token)
if err != nil {
log.Debug("Failed to parse token: ", err)
return res, fmt.Errorf(`invalid token: %s`, err.Error())
}
if ok, err := token.ValidateJWTClaims(claim, hostname, verificationRequest.Nonce, verificationRequest.Email); !ok || err != nil {
log.Debug("Failed to validate jwt claims: ", err)
return res, fmt.Errorf(`invalid token: %s`, err.Error())
}
email := claim["sub"].(string)
log := log.WithFields(log.Fields{
"email": email,
@@ -75,8 +80,14 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
return res, err
}
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
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,