Skip to content

Commit 3337dbd

Browse files
authored
Merge pull request #191 from authorizerdev/fix/session-invalidation
fix: session invalidation
2 parents 7a2dbea + 82a2a42 commit 3337dbd

34 files changed

+491
-322
lines changed

server/constants/token_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ const (
77
TokenTypeAccessToken = "access_token"
88
// TokenTypeIdentityToken is the identity_token token type
99
TokenTypeIdentityToken = "id_token"
10+
// TokenTypeSessionToken is the session_token type used for browser session
11+
TokenTypeSessionToken = "session_token"
1012
)

server/db/models/user.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ func (user *User) AsAPIUser() *model.User {
3838
email := user.Email
3939
createdAt := user.CreatedAt
4040
updatedAt := user.UpdatedAt
41-
revokedTimestamp := user.RevokedTimestamp
4241
return &model.User{
4342
ID: user.ID,
4443
Email: user.Email,
@@ -55,7 +54,7 @@ func (user *User) AsAPIUser() *model.User {
5554
PhoneNumberVerified: &isPhoneVerified,
5655
Picture: user.Picture,
5756
Roles: strings.Split(user.Roles, ","),
58-
RevokedTimestamp: revokedTimestamp,
57+
RevokedTimestamp: user.RevokedTimestamp,
5958
CreatedAt: &createdAt,
6059
UpdatedAt: &updatedAt,
6160
}

server/db/providers/mongodb/user.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error)
6868
opts.SetSort(bson.M{"created_at": -1})
6969

7070
paginationClone := pagination
71-
// TODO add pagination total
7271

7372
userCollection := p.db.Collection(models.Collections.User, options.Collection())
7473
count, err := userCollection.CountDocuments(nil, bson.M{}, options.Count())

server/db/providers/sql/user.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error)
9797
func (p *provider) GetUserByEmail(email string) (models.User, error) {
9898
var user models.User
9999
result := p.db.Where("email = ?", email).First(&user)
100-
101100
if result.Error != nil {
102101
return user, result.Error
103102
}
@@ -110,7 +109,6 @@ func (p *provider) GetUserByID(id string) (models.User, error) {
110109
var user models.User
111110

112111
result := p.db.Where("id = ?", id).First(&user)
113-
114112
if result.Error != nil {
115113
return user, result.Error
116114
}

server/handlers/authorize.go

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -222,10 +222,7 @@ func AuthorizeHandler() gin.HandlerFunc {
222222
// based on the response type, generate the response
223223
if isResponseTypeCode {
224224
// rollover the session for security
225-
err = memorystore.Provider.RemoveState(sessionToken)
226-
if err != nil {
227-
log.Debug("Failed to remove state: ", err)
228-
}
225+
go memorystore.Provider.DeleteUserSession(user.ID, claims.Nonce)
229226
nonce := uuid.New().String()
230227
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope)
231228
if err != nil {
@@ -246,7 +243,7 @@ func AuthorizeHandler() gin.HandlerFunc {
246243
return
247244
}
248245

249-
memorystore.Provider.SetState(newSessionToken, newSessionTokenData.Nonce+"@"+user.ID)
246+
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken)
250247
cookie.SetSession(gc, newSessionToken)
251248
code := uuid.New().String()
252249
memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken)
@@ -283,9 +280,9 @@ func AuthorizeHandler() gin.HandlerFunc {
283280
}
284281
return
285282
}
286-
memorystore.Provider.RemoveState(sessionToken)
287-
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
288-
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
283+
go memorystore.Provider.DeleteUserSession(user.ID, claims.Nonce)
284+
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
285+
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
289286
cookie.SetSession(gc, authToken.FingerPrintHash)
290287

291288
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
@@ -308,7 +305,7 @@ func AuthorizeHandler() gin.HandlerFunc {
308305
if authToken.RefreshToken != nil {
309306
res["refresh_token"] = authToken.RefreshToken.Token
310307
params += "&refresh_token=" + authToken.RefreshToken.Token
311-
memorystore.Provider.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
308+
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
312309
}
313310

314311
if isQuery {

server/handlers/logout.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package handlers
22

33
import (
4+
"encoding/json"
45
"net/http"
56
"strings"
67

@@ -10,6 +11,7 @@ import (
1011
"github.com/authorizerdev/authorizer/server/cookie"
1112
"github.com/authorizerdev/authorizer/server/crypto"
1213
"github.com/authorizerdev/authorizer/server/memorystore"
14+
"github.com/authorizerdev/authorizer/server/token"
1315
)
1416

1517
// Handler to logout user
@@ -35,12 +37,17 @@ func LogoutHandler() gin.HandlerFunc {
3537
return
3638
}
3739

38-
fingerPrint := string(decryptedFingerPrint)
39-
40-
err = memorystore.Provider.RemoveState(fingerPrint)
40+
var sessionData token.SessionData
41+
err = json.Unmarshal([]byte(decryptedFingerPrint), &sessionData)
4142
if err != nil {
42-
log.Debug("Failed to remove state: ", err)
43+
log.Debug("Failed to decrypt fingerprint: ", err)
44+
gc.JSON(http.StatusUnauthorized, gin.H{
45+
"error": err.Error(),
46+
})
47+
return
4348
}
49+
50+
memorystore.Provider.DeleteUserSession(sessionData.Subject, sessionData.Nonce)
4451
cookie.DeleteSession(gc)
4552

4653
if redirectURL != "" {

server/handlers/oauth_callback.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ func OAuthCallbackHandler() gin.HandlerFunc {
3636
log.Debug("Invalid oauth state: ", state)
3737
c.JSON(400, gin.H{"error": "invalid oauth state"})
3838
}
39-
memorystore.Provider.GetState(state)
4039
// contains random token, redirect url, role
4140
sessionSplit := strings.Split(state, "___")
4241

@@ -46,6 +45,9 @@ func OAuthCallbackHandler() gin.HandlerFunc {
4645
return
4746
}
4847

48+
// remove state from store
49+
go memorystore.Provider.RemoveState(state)
50+
4951
stateValue := sessionSplit[0]
5052
redirectURL := sessionSplit[1]
5153
inputRoles := strings.Split(sessionSplit[2], ",")
@@ -117,9 +119,11 @@ func OAuthCallbackHandler() gin.HandlerFunc {
117119
user.EmailVerifiedAt = &now
118120
user, _ = db.Provider.AddUser(user)
119121
} else {
122+
user = existingUser
120123
if user.RevokedTimestamp != nil {
121124
log.Debug("User access revoked at: ", user.RevokedTimestamp)
122125
c.JSON(400, gin.H{"error": "user access has been revoked"})
126+
return
123127
}
124128

125129
// user exists in db, check if method was google
@@ -128,7 +132,6 @@ func OAuthCallbackHandler() gin.HandlerFunc {
128132
if !strings.Contains(signupMethod, provider) {
129133
signupMethod = signupMethod + "," + provider
130134
}
131-
user = existingUser
132135
user.SignupMethods = signupMethod
133136

134137
if user.EmailVerifiedAt == nil {
@@ -200,12 +203,12 @@ func OAuthCallbackHandler() gin.HandlerFunc {
200203
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + stateValue + "&id_token=" + authToken.IDToken.Token
201204

202205
cookie.SetSession(c, authToken.FingerPrintHash)
203-
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
204-
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
206+
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
207+
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
205208

206209
if authToken.RefreshToken != nil {
207210
params = params + `&refresh_token=` + authToken.RefreshToken.Token
208-
memorystore.Provider.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
211+
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
209212
}
210213

211214
go db.Provider.AddSession(models.Session{

server/handlers/revoke.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/authorizerdev/authorizer/server/constants"
1111
"github.com/authorizerdev/authorizer/server/memorystore"
12+
"github.com/authorizerdev/authorizer/server/token"
1213
)
1314

1415
// Revoke handler to revoke refresh token
@@ -45,7 +46,17 @@ func RevokeHandler() gin.HandlerFunc {
4546
return
4647
}
4748

48-
memorystore.Provider.RemoveState(refreshToken)
49+
claims, err := token.ParseJWTToken(refreshToken)
50+
if err != nil {
51+
log.Debug("Client ID is invalid: ", clientID)
52+
gc.JSON(http.StatusBadRequest, gin.H{
53+
"error": err.Error(),
54+
"error_description": "Failed to parse jwt",
55+
})
56+
return
57+
}
58+
59+
memorystore.Provider.DeleteUserSession(claims["sub"].(string), claims["nonce"].(string))
4960

5061
gc.JSON(http.StatusOK, gin.H{
5162
"message": "Token revoked successfully",

server/handlers/token.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ func TokenHandler() gin.HandlerFunc {
107107
return
108108
}
109109

110+
go memorystore.Provider.RemoveState(encryptedCode)
110111
// split session data
111112
// it contains code@sessiontoken
112113
sessionDataSplit := strings.Split(sessionData, "@")
@@ -130,11 +131,11 @@ func TokenHandler() gin.HandlerFunc {
130131
})
131132
return
132133
}
133-
// rollover the session for security
134-
memorystore.Provider.RemoveState(sessionDataSplit[1])
135134
userID = claims.Subject
136135
roles = claims.Roles
137136
scope = claims.Scope
137+
// rollover the session for security
138+
go memorystore.Provider.DeleteUserSession(userID, claims.Nonce)
138139
} else {
139140
// validate refresh token
140141
if refreshToken == "" {
@@ -163,7 +164,7 @@ func TokenHandler() gin.HandlerFunc {
163164
scope = append(scope, v.(string))
164165
}
165166
// remove older refresh token and rotate it for security
166-
memorystore.Provider.RemoveState(refreshToken)
167+
go memorystore.Provider.DeleteUserSession(userID, claims["nonce"].(string))
167168
}
168169

169170
user, err := db.Provider.GetUserByID(userID)
@@ -185,8 +186,8 @@ func TokenHandler() gin.HandlerFunc {
185186
})
186187
return
187188
}
188-
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
189-
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
189+
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
190+
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
190191
cookie.SetSession(gc, authToken.FingerPrintHash)
191192

192193
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
@@ -204,7 +205,7 @@ func TokenHandler() gin.HandlerFunc {
204205

205206
if authToken.RefreshToken != nil {
206207
res["refresh_token"] = authToken.RefreshToken.Token
207-
memorystore.Provider.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
208+
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
208209
}
209210

210211
gc.JSON(http.StatusOK, res)

server/handlers/verify_email.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/gin-gonic/gin"
1010
log "github.com/sirupsen/logrus"
1111

12+
"github.com/authorizerdev/authorizer/server/constants"
1213
"github.com/authorizerdev/authorizer/server/cookie"
1314
"github.com/authorizerdev/authorizer/server/db"
1415
"github.com/authorizerdev/authorizer/server/db/models"
@@ -42,15 +43,22 @@ func VerifyEmailHandler() gin.HandlerFunc {
4243

4344
// verify if token exists in db
4445
hostname := parsers.GetHost(c)
45-
claim, err := token.ParseJWTToken(tokenInQuery, hostname, verificationRequest.Nonce, verificationRequest.Email)
46+
claim, err := token.ParseJWTToken(tokenInQuery)
4647
if err != nil {
4748
log.Debug("Error parsing token: ", err)
4849
errorRes["error_description"] = err.Error()
4950
c.JSON(400, errorRes)
5051
return
5152
}
5253

53-
user, err := db.Provider.GetUserByEmail(claim["sub"].(string))
54+
if ok, err := token.ValidateJWTClaims(claim, hostname, verificationRequest.Nonce, verificationRequest.Email); !ok || err != nil {
55+
log.Debug("Error validating jwt claims: ", err)
56+
errorRes["error_description"] = err.Error()
57+
c.JSON(400, errorRes)
58+
return
59+
}
60+
61+
user, err := db.Provider.GetUserByEmail(verificationRequest.Email)
5462
if err != nil {
5563
log.Debug("Error getting user: ", err)
5664
errorRes["error_description"] = err.Error()
@@ -100,12 +108,12 @@ func VerifyEmailHandler() gin.HandlerFunc {
100108
params := "access_token=" + authToken.AccessToken.Token + "&token_type=bearer&expires_in=" + strconv.FormatInt(expiresIn, 10) + "&state=" + state + "&id_token=" + authToken.IDToken.Token
101109

102110
cookie.SetSession(c, authToken.FingerPrintHash)
103-
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
104-
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
111+
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
112+
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
105113

106114
if authToken.RefreshToken != nil {
107-
params = params + `&refresh_token=${refresh_token}`
108-
memorystore.Provider.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
115+
params = params + `&refresh_token=` + authToken.RefreshToken.Token
116+
memorystore.Provider.SetUserSession(user.ID, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
109117
}
110118

111119
if redirectURL == "" {

0 commit comments

Comments
 (0)