fix: session storage

This commit is contained in:
Lakhan Samani 2023-04-08 13:06:15 +05:30
parent 9a284c03ca
commit 02c0ebb9c4
22 changed files with 290 additions and 137 deletions

View File

@ -194,7 +194,7 @@ func AuthorizeHandler() gin.HandlerFunc {
// rollover the session for security // rollover the session for security
go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce) go memorystore.Provider.DeleteUserSession(sessionKey, claims.Nonce)
if responseType == constants.ResponseTypeCode { if responseType == constants.ResponseTypeCode {
newSessionTokenData, newSessionToken, err := token.CreateSessionToken(user, nonce, claims.Roles, scope, claims.LoginMethod) newSessionTokenData, newSessionToken, newSessionExpiresAt, err := token.CreateSessionToken(user, nonce, claims.Roles, scope, claims.LoginMethod)
if err != nil { if err != nil {
log.Debug("CreateSessionToken failed: ", err) log.Debug("CreateSessionToken failed: ", err)
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK) handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
@ -215,7 +215,7 @@ func AuthorizeHandler() gin.HandlerFunc {
return return
} }
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken); err != nil { if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+newSessionTokenData.Nonce, newSessionToken, newSessionExpiresAt); err != nil {
log.Debug("SetUserSession failed: ", err) log.Debug("SetUserSession failed: ", err)
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK) handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
return return
@ -271,13 +271,13 @@ func AuthorizeHandler() gin.HandlerFunc {
return return
} }
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+nonce, authToken.FingerPrintHash); err != nil { if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+nonce, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt); err != nil {
log.Debug("SetUserSession failed: ", err) log.Debug("SetUserSession failed: ", err)
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK) handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
return return
} }
if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+nonce, authToken.AccessToken.Token); err != nil { if err := memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+nonce, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt); err != nil {
log.Debug("SetUserSession failed: ", err) log.Debug("SetUserSession failed: ", err)
handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK) handleResponse(gc, responseMode, loginURL, redirectURI, loginError, http.StatusOK)
return return
@ -305,7 +305,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(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
} }
if responseMode == constants.ResponseModeQuery { if responseMode == constants.ResponseModeQuery {

View File

@ -47,7 +47,14 @@ func LogoutHandler() gin.HandlerFunc {
return return
} }
memorystore.Provider.DeleteUserSession(sessionData.Subject, sessionData.Nonce) userID := sessionData.Subject
loginMethod := sessionData.LoginMethod
sessionToken := userID
if loginMethod != "" {
sessionToken = loginMethod + ":" + userID
}
memorystore.Provider.DeleteUserSession(sessionToken, sessionData.Nonce)
cookie.DeleteSession(gc) cookie.DeleteSession(gc)
if redirectURL != "" { if redirectURL != "" {

View File

@ -249,12 +249,12 @@ func OAuthCallbackHandler() gin.HandlerFunc {
sessionKey := provider + ":" + user.ID sessionKey := provider + ":" + user.ID
cookie.SetSession(ctx, authToken.FingerPrintHash) cookie.SetSession(ctx, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
params += `&refresh_token=` + authToken.RefreshToken.Token params += `&refresh_token=` + authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
} }
go func() { go func() {

View File

@ -247,8 +247,8 @@ func TokenHandler() gin.HandlerFunc {
return return
} }
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
cookie.SetSession(gc, authToken.FingerPrintHash) cookie.SetSession(gc, authToken.FingerPrintHash)
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix() expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
@ -266,7 +266,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(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
} }
gc.JSON(http.StatusOK, res) gc.JSON(http.StatusOK, res)

View File

@ -154,12 +154,12 @@ func VerifyEmailHandler() gin.HandlerFunc {
sessionKey := loginMethod + ":" + user.ID sessionKey := loginMethod + ":" + user.ID
cookie.SetSession(c, authToken.FingerPrintHash) cookie.SetSession(c, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
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(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
} }
if redirectURL == "" { if redirectURL == "" {

View File

@ -0,0 +1,14 @@
package inmemory
import (
"testing"
"github.com/authorizerdev/authorizer/server/memorystore/providers"
"github.com/stretchr/testify/assert"
)
func TestInMemoryProvider(t *testing.T) {
p, err := NewInMemoryProvider()
assert.NoError(t, err)
providers.ProviderTests(t, p)
}

View File

@ -8,39 +8,31 @@ import (
) )
// SetUserSession sets the user session for given user identifier in form recipe:user_id // SetUserSession sets the user session for given user identifier in form recipe:user_id
func (c *provider) SetUserSession(userId, key, token string) error { func (c *provider) SetUserSession(userId, key, token string, expiration int64) error {
c.sessionStore.Set(userId, key, token) c.sessionStore.Set(userId, key, token, expiration)
return nil return nil
} }
// GetUserSession returns value for given session token // GetUserSession returns value for given session token
func (c *provider) GetUserSession(userId, sessionToken string) (string, error) { func (c *provider) GetUserSession(userId, sessionToken string) (string, error) {
return c.sessionStore.Get(userId, sessionToken), nil val := c.sessionStore.Get(userId, sessionToken)
if val == "" {
return "", fmt.Errorf("Not found")
}
return val, nil
} }
// 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{ c.sessionStore.RemoveAll(userId)
constants.AuthRecipeMethodBasicAuth,
constants.AuthRecipeMethodMagicLinkLogin,
constants.AuthRecipeMethodApple,
constants.AuthRecipeMethodFacebook,
constants.AuthRecipeMethodGithub,
constants.AuthRecipeMethodGoogle,
constants.AuthRecipeMethodLinkedIn,
constants.AuthRecipeMethodTwitter,
constants.AuthRecipeMethodMicrosoft,
}
for _, namespace := range namespaces {
c.sessionStore.RemoveAll(namespace + ":" + userId)
}
return nil return nil
} }
// DeleteUserSession deletes the user session from the in-memory store. // DeleteUserSession deletes the user session from the in-memory store.
func (c *provider) DeleteUserSession(userId, sessionToken string) error { func (c *provider) DeleteUserSession(userId, sessionToken string) error {
c.sessionStore.Remove(userId, sessionToken) c.sessionStore.Remove(userId, constants.TokenTypeSessionToken+"_"+sessionToken)
c.sessionStore.Remove(userId, constants.TokenTypeAccessToken+"_"+sessionToken)
c.sessionStore.Remove(userId, constants.TokenTypeRefreshToken+"_"+sessionToken)
return nil return nil
} }

View File

@ -1,8 +1,15 @@
package stores package stores
import ( import (
"fmt"
"strings" "strings"
"sync" "sync"
"time"
)
const (
// Maximum entries to keep in session storage
maxCacheSize = 1000
) )
// SessionEntry is the struct for entry stored in store // SessionEntry is the struct for entry stored in store
@ -13,15 +20,16 @@ type SessionEntry struct {
// SessionStore struct to store the env variables // SessionStore struct to store the env variables
type SessionStore struct { type SessionStore struct {
mutex sync.Mutex mutex sync.Mutex
store map[string]map[string]*SessionEntry store map[string]*SessionEntry
itemsToEvict []string
} }
// NewSessionStore create a new session store // NewSessionStore create a new session store
func NewSessionStore() *SessionStore { func NewSessionStore() *SessionStore {
return &SessionStore{ return &SessionStore{
mutex: sync.Mutex{}, mutex: sync.Mutex{},
store: make(map[string]map[string]*SessionEntry), store: make(map[string]*SessionEntry),
} }
} }
@ -29,53 +37,59 @@ func NewSessionStore() *SessionStore {
func (s *SessionStore) Get(key, subKey string) string { func (s *SessionStore) Get(key, subKey string) string {
s.mutex.Lock() s.mutex.Lock()
defer s.mutex.Unlock() defer s.mutex.Unlock()
return s.store[key][subKey].Value currentTime := time.Now().Unix()
k := fmt.Sprintf("%s:%s", key, subKey)
if v, ok := s.store[k]; ok {
if v.ExpiresAt > currentTime {
return v.Value
}
s.itemsToEvict = append(s.itemsToEvict, k)
}
return ""
} }
// Set sets the value of the key in state store // Set sets the value of the key in state store
func (s *SessionStore) Set(key string, subKey, value string) { func (s *SessionStore) Set(key string, subKey, value string, expiration int64) {
s.mutex.Lock() s.mutex.Lock()
defer s.mutex.Unlock() defer s.mutex.Unlock()
k := fmt.Sprintf("%s:%s", key, subKey)
if _, ok := s.store[key]; !ok { if _, ok := s.store[k]; !ok {
s.store[key] = make(map[string]string) s.store[k] = &SessionEntry{
Value: value,
ExpiresAt: expiration,
// TODO add expire time
}
}
s.store[k] = &SessionEntry{
Value: value,
ExpiresAt: expiration,
// TODO add expire time
} }
s.store[key][subKey] = value
} }
// RemoveAll all values for given key // RemoveAll all values for given key
func (s *SessionStore) RemoveAll(key string) { func (s *SessionStore) RemoveAll(key string) {
s.mutex.Lock() s.mutex.Lock()
defer s.mutex.Unlock() defer s.mutex.Unlock()
for k := range s.store {
delete(s.store, key) if strings.Contains(k, key) {
delete(s.store, k)
}
}
} }
// Remove value for given key and subkey // Remove value for given key and subkey
func (s *SessionStore) Remove(key, subKey string) { func (s *SessionStore) Remove(key, subKey string) {
s.mutex.Lock() s.mutex.Lock()
defer s.mutex.Unlock() defer s.mutex.Unlock()
if _, ok := s.store[key]; ok { k := fmt.Sprintf("%s:%s", key, subKey)
delete(s.store[key], subKey) delete(s.store, k)
}
}
// Get all the values for given key
func (s *SessionStore) GetAll(key string) map[string]string {
s.mutex.Lock()
defer s.mutex.Unlock()
if _, ok := s.store[key]; !ok {
s.store[key] = make(map[string]string)
}
return s.store[key]
} }
// RemoveByNamespace to delete session for a given namespace example google,github // RemoveByNamespace to delete session for a given namespace example google,github
func (s *SessionStore) RemoveByNamespace(namespace string) error { func (s *SessionStore) RemoveByNamespace(namespace string) error {
s.mutex.Lock() s.mutex.Lock()
defer s.mutex.Unlock() defer s.mutex.Unlock()
for key := range s.store { for key := range s.store {
if strings.Contains(key, namespace+":") { if strings.Contains(key, namespace+":") {
delete(s.store, key) delete(s.store, key)

View File

@ -0,0 +1,115 @@
package providers
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
// ProviderTests runs all provider tests
func ProviderTests(t *testing.T, p Provider) {
err := p.SetUserSession("auth_provider:123", "session_token_key", "test_hash123", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
err = p.SetUserSession("auth_provider:123", "access_token_key", "test_jwt123", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
// Same user multiple session
err = p.SetUserSession("auth_provider:123", "session_token_key1", "test_hash1123", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
err = p.SetUserSession("auth_provider:123", "access_token_key1", "test_jwt1123", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
// Different user session
err = p.SetUserSession("auth_provider:124", "session_token_key", "test_hash124", time.Now().Add(5*time.Second).Unix())
assert.NoError(t, err)
err = p.SetUserSession("auth_provider:124", "access_token_key", "test_jwt124", time.Now().Add(5*time.Second).Unix())
assert.NoError(t, err)
// Different provider session
err = p.SetUserSession("auth_provider1:124", "session_token_key", "test_hash124", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
err = p.SetUserSession("auth_provider1:124", "access_token_key", "test_jwt124", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
// Different provider session
err = p.SetUserSession("auth_provider1:123", "session_token_key", "test_hash1123", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
err = p.SetUserSession("auth_provider1:123", "access_token_key", "test_jwt1123", time.Now().Add(60*time.Second).Unix())
assert.NoError(t, err)
// Get session
key, err := p.GetUserSession("auth_provider:123", "session_token_key")
assert.NoError(t, err)
assert.Equal(t, "test_hash123", key)
key, err = p.GetUserSession("auth_provider:123", "access_token_key")
assert.NoError(t, err)
assert.Equal(t, "test_jwt123", key)
key, err = p.GetUserSession("auth_provider:124", "session_token_key")
assert.NoError(t, err)
assert.Equal(t, "test_hash124", key)
key, err = p.GetUserSession("auth_provider:124", "access_token_key")
assert.NoError(t, err)
assert.Equal(t, "test_jwt124", key)
// Expire some tokens and make sure they are empty
time.Sleep(5 * time.Second)
key, err = p.GetUserSession("auth_provider:124", "session_token_key")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider:124", "access_token_key")
assert.Empty(t, key)
assert.Error(t, err)
// Delete user session
err = p.DeleteUserSession("auth_provider:123", "key")
assert.NoError(t, err)
err = p.DeleteUserSession("auth_provider:123", "key")
assert.NoError(t, err)
key, err = p.GetUserSession("auth_provider:123", "key")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider:123", "access_token_key")
assert.Empty(t, key)
assert.Error(t, err)
// Delete all user session
err = p.DeleteAllUserSessions("123")
assert.NoError(t, err)
err = p.DeleteAllUserSessions("123")
assert.NoError(t, err)
key, err = p.GetUserSession("auth_provider:123", "session_token_key1")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider:123", "access_token_key1")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider1:123", "session_token_key")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider1:123", "access_token_key")
assert.Empty(t, key)
assert.Error(t, err)
// Delete namespace
err = p.DeleteSessionForNamespace("auth_provider")
assert.NoError(t, err)
err = p.DeleteSessionForNamespace("auth_provider1")
assert.NoError(t, err)
key, err = p.GetUserSession("auth_provider:123", "session_token_key1")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider:123", "access_token_key1")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider1:123", "session_token_key")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider1:123", "access_token_key")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider:124", "session_token_key1")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider:124", "access_token_key1")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider1:124", "session_token_key")
assert.Empty(t, key)
assert.Error(t, err)
key, err = p.GetUserSession("auth_provider1:124", "access_token_key")
assert.Empty(t, key)
assert.Error(t, err)
}

View File

@ -3,7 +3,7 @@ package providers
// Provider defines current memory store provider // Provider defines current memory store provider
type Provider interface { type Provider interface {
// SetUserSession sets the user session for given user identifier in form recipe:user_id // SetUserSession sets the user session for given user identifier in form recipe:user_id
SetUserSession(userId, key, token string) error SetUserSession(userId, key, token string, expiration int64) error
// GetUserSession returns the session token for given token // GetUserSession returns the session token for given token
GetUserSession(userId, key string) (string, error) GetUserSession(userId, key string) (string, error)
// DeleteUserSession deletes the user session // DeleteUserSession deletes the user session

View File

@ -32,7 +32,6 @@ type provider struct {
// NewRedisProvider returns a new redis provider // NewRedisProvider returns a new redis provider
func NewRedisProvider(redisURL string) (*provider, error) { func NewRedisProvider(redisURL string) (*provider, error) {
redisURLHostPortsList := strings.Split(redisURL, ",") redisURLHostPortsList := strings.Split(redisURL, ",")
if len(redisURLHostPortsList) > 1 { if len(redisURLHostPortsList) > 1 {
opt, err := redis.ParseURL(redisURLHostPortsList[0]) opt, err := redis.ParseURL(redisURLHostPortsList[0])
if err != nil { if err != nil {

View File

@ -0,0 +1,15 @@
package redis
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/authorizerdev/authorizer/server/memorystore/providers"
)
func TestRedisProvider(t *testing.T) {
p, err := NewRedisProvider("redis://127.0.0.1:6379")
assert.NoError(t, err)
providers.ProviderTests(t, p)
}

View File

@ -3,6 +3,7 @@ package redis
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"time"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -16,8 +17,11 @@ var (
) )
// SetUserSession sets the user session for given user identifier in form recipe:user_id // SetUserSession sets the user session for given user identifier in form recipe:user_id
func (c *provider) SetUserSession(userId, key, token string) error { func (c *provider) SetUserSession(userId, key, token string, expiration int64) error {
err := c.store.Set(c.ctx, fmt.Sprintf("%s:%s", userId, key), token, 0).Err() currentTime := time.Now()
expireTime := time.Unix(expiration, 0)
duration := expireTime.Sub(currentTime)
err := c.store.Set(c.ctx, fmt.Sprintf("%s:%s", userId, key), token, duration).Err()
if err != nil { if err != nil {
log.Debug("Error saving user session to redis: ", err) log.Debug("Error saving user session to redis: ", err)
return err return err
@ -38,37 +42,35 @@ func (c *provider) GetUserSession(userId, key string) (string, error) {
func (c *provider) DeleteUserSession(userId, key string) error { func (c *provider) DeleteUserSession(userId, key string) error {
if err := c.store.Del(c.ctx, fmt.Sprintf("%s:%s", userId, constants.TokenTypeSessionToken+"_"+key)).Err(); err != nil { if err := c.store.Del(c.ctx, fmt.Sprintf("%s:%s", userId, constants.TokenTypeSessionToken+"_"+key)).Err(); err != nil {
log.Debug("Error deleting user session from redis: ", err) log.Debug("Error deleting user session from redis: ", err)
return err fmt.Println("Error deleting user session from redis: ", err, userId, constants.TokenTypeSessionToken, key)
// continue
} }
if err := c.store.Del(c.ctx, fmt.Sprintf("%s:%s", userId, constants.TokenTypeAccessToken+"_"+key)).Err(); err != nil { if err := c.store.Del(c.ctx, fmt.Sprintf("%s:%s", userId, constants.TokenTypeAccessToken+"_"+key)).Err(); err != nil {
log.Debug("Error deleting user session from redis: ", err) log.Debug("Error deleting user session from redis: ", err)
return err fmt.Println("Error deleting user session from redis: ", err, userId, constants.TokenTypeAccessToken, key)
// continue
} }
if err := c.store.Del(c.ctx, fmt.Sprintf("%s:%s", userId, constants.TokenTypeRefreshToken+"_"+key)).Err(); err != nil { if err := c.store.Del(c.ctx, fmt.Sprintf("%s:%s", userId, constants.TokenTypeRefreshToken+"_"+key)).Err(); err != nil {
log.Debug("Error deleting user session from redis: ", err) log.Debug("Error deleting user session from redis: ", err)
return err fmt.Println("Error deleting user session from redis: ", err, userId, constants.TokenTypeRefreshToken, key)
// continue
} }
return nil return nil
} }
// 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 {
namespaces := []string{ res := c.store.Keys(c.ctx, fmt.Sprintf("*%s*", userID))
constants.AuthRecipeMethodBasicAuth, if res.Err() != nil {
constants.AuthRecipeMethodMagicLinkLogin, log.Debug("Error getting all user sessions from redis: ", res.Err())
constants.AuthRecipeMethodApple, return res.Err()
constants.AuthRecipeMethodFacebook,
constants.AuthRecipeMethodGithub,
constants.AuthRecipeMethodGoogle,
constants.AuthRecipeMethodLinkedIn,
constants.AuthRecipeMethodTwitter,
constants.AuthRecipeMethodMicrosoft,
} }
for _, namespace := range namespaces { keys := res.Val()
err := c.store.Del(c.ctx, namespace+":"+userID).Err() for _, key := range keys {
err := c.store.Del(c.ctx, key).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 continue
} }
} }
return nil return nil
@ -76,27 +78,19 @@ func (c *provider) DeleteAllUserSessions(userID string) error {
// DeleteSessionForNamespace to delete session for a given namespace example google,github // DeleteSessionForNamespace to delete session for a given namespace example google,github
func (c *provider) DeleteSessionForNamespace(namespace string) error { func (c *provider) DeleteSessionForNamespace(namespace string) error {
var cursor uint64 res := c.store.Keys(c.ctx, fmt.Sprintf("%s:*", namespace))
for { if res.Err() != nil {
keys := []string{} log.Debug("Error getting all user sessions from redis: ", res.Err())
keys, cursor, err := c.store.Scan(c.ctx, cursor, namespace+":*", 0).Result() return res.Err()
}
keys := res.Val()
for _, key := range keys {
err := c.store.Del(c.ctx, key).Err()
if err != nil { if err != nil {
log.Debugf("Error scanning keys for %s namespace: %s", namespace, err.Error()) log.Debug("Error deleting all user sessions from redis: ", err)
return err continue
}
for _, key := range keys {
err := c.store.Del(c.ctx, key).Err()
if err != nil {
log.Debugf("Error deleting sessions for %s namespace: %s", namespace, err.Error())
return err
}
}
if cursor == 0 { // no more keys
break
} }
} }
return nil return nil
} }

View File

@ -193,12 +193,12 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
cookie.SetSession(gc, authToken.FingerPrintHash) cookie.SetSession(gc, authToken.FingerPrintHash)
sessionStoreKey := constants.AuthRecipeMethodBasicAuth + ":" + user.ID sessionStoreKey := constants.AuthRecipeMethodBasicAuth + ":" + user.ID
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
} }
go func() { go func() {

View File

@ -195,12 +195,12 @@ func MobileLoginResolver(ctx context.Context, params model.MobileLoginInput) (*m
cookie.SetSession(gc, authToken.FingerPrintHash) cookie.SetSession(gc, authToken.FingerPrintHash)
sessionStoreKey := constants.AuthRecipeMethodMobileBasicAuth + ":" + user.ID sessionStoreKey := constants.AuthRecipeMethodMobileBasicAuth + ":" + user.ID
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
} }
go func() { go func() {

View File

@ -249,12 +249,12 @@ func MobileSignupResolver(ctx context.Context, params *model.MobileSignUpInput)
sessionKey := constants.AuthRecipeMethodMobileBasicAuth + ":" + user.ID sessionKey := constants.AuthRecipeMethodMobileBasicAuth + ":" + user.ID
cookie.SetSession(gc, authToken.FingerPrintHash) cookie.SetSession(gc, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
} }
go func() { go func() {

View File

@ -99,12 +99,12 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
} }
cookie.SetSession(gc, authToken.FingerPrintHash) cookie.SetSession(gc, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
} }
return res, nil return res, nil
} }

View File

@ -91,7 +91,6 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
} }
inputRoles := []string{} inputRoles := []string{}
if len(params.Roles) > 0 { if len(params.Roles) > 0 {
// check if roles exists // check if roles exists
rolesString, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyRoles) rolesString, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyRoles)
@ -293,12 +292,12 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
sessionKey := constants.AuthRecipeMethodBasicAuth + ":" + user.ID sessionKey := constants.AuthRecipeMethodBasicAuth + ":" + user.ID
cookie.SetSession(gc, authToken.FingerPrintHash) cookie.SetSession(gc, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
} }
go func() { go func() {

View File

@ -150,12 +150,12 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
sessionKey := loginMethod + ":" + user.ID sessionKey := loginMethod + ":" + user.ID
cookie.SetSession(gc, authToken.FingerPrintHash) cookie.SetSession(gc, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
} }
return res, nil return res, nil
} }

View File

@ -123,12 +123,12 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod
sessionKey := loginMethod + ":" + user.ID sessionKey := loginMethod + ":" + user.ID
cookie.SetSession(gc, authToken.FingerPrintHash) cookie.SetSession(gc, authToken.FingerPrintHash)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
res.RefreshToken = &authToken.RefreshToken.Token res.RefreshToken = &authToken.RefreshToken.Token
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
} }
return res, nil return res, nil
} }

View File

@ -55,11 +55,11 @@ func validateJwtTokenTest(t *testing.T, s TestSetup) {
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth, nonce, "") authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth, nonce, "")
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, authToken) assert.NotNil(t, authToken)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash, authToken.SessionTokenExpiresAt)
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token, authToken.AccessToken.ExpiresAt)
if authToken.RefreshToken != nil { if authToken.RefreshToken != nil {
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token, authToken.RefreshToken.ExpiresAt)
} }
t.Run(`should validate the access token`, func(t *testing.T) { t.Run(`should validate the access token`, func(t *testing.T) {

View File

@ -30,11 +30,13 @@ type JWTToken struct {
// Token object to hold the finger print and refresh token information // Token object to hold the finger print and refresh token information
type Token struct { type Token struct {
FingerPrint string `json:"fingerprint"` FingerPrint string `json:"fingerprint"`
FingerPrintHash string `json:"fingerprint_hash"` // Session Token
RefreshToken *JWTToken `json:"refresh_token"` FingerPrintHash string `json:"fingerprint_hash"`
AccessToken *JWTToken `json:"access_token"` SessionTokenExpiresAt int64 `json:"expires_at"`
IDToken *JWTToken `json:"id_token"` RefreshToken *JWTToken `json:"refresh_token"`
AccessToken *JWTToken `json:"access_token"`
IDToken *JWTToken `json:"id_token"`
} }
// SessionData // SessionData
@ -51,7 +53,7 @@ type SessionData struct {
// 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, loginMethod, nonce string, code string) (*Token, error) { func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string, loginMethod, nonce string, code string) (*Token, error) {
hostname := parsers.GetHost(gc) hostname := parsers.GetHost(gc)
_, fingerPrintHash, err := CreateSessionToken(user, nonce, roles, scope, loginMethod) _, fingerPrintHash, sessionTokenExpiresAt, err := CreateSessionToken(user, nonce, roles, scope, loginMethod)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -82,10 +84,11 @@ func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string, l
} }
res := &Token{ res := &Token{
FingerPrint: nonce, FingerPrint: nonce,
FingerPrintHash: fingerPrintHash, FingerPrintHash: fingerPrintHash,
AccessToken: &JWTToken{Token: accessToken, ExpiresAt: accessTokenExpiresAt}, SessionTokenExpiresAt: sessionTokenExpiresAt,
IDToken: &JWTToken{Token: idToken, ExpiresAt: idTokenExpiresAt}, AccessToken: &JWTToken{Token: accessToken, ExpiresAt: accessTokenExpiresAt},
IDToken: &JWTToken{Token: idToken, ExpiresAt: idTokenExpiresAt},
} }
if utils.StringSliceContains(scope, "offline_access") { if utils.StringSliceContains(scope, "offline_access") {
@ -101,7 +104,8 @@ func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string, l
} }
// CreateSessionToken creates a new session token // CreateSessionToken creates a new session token
func CreateSessionToken(user models.User, nonce string, roles, scope []string, loginMethod string) (*SessionData, string, error) { func CreateSessionToken(user models.User, nonce string, roles, scope []string, loginMethod string) (*SessionData, string, int64, error) {
expiresAt := time.Now().AddDate(1, 0, 0).Unix()
fingerPrintMap := &SessionData{ fingerPrintMap := &SessionData{
Nonce: nonce, Nonce: nonce,
Roles: roles, Roles: roles,
@ -109,15 +113,15 @@ func CreateSessionToken(user models.User, nonce string, roles, scope []string, l
Scope: scope, Scope: scope,
LoginMethod: loginMethod, LoginMethod: loginMethod,
IssuedAt: time.Now().Unix(), IssuedAt: time.Now().Unix(),
ExpiresAt: time.Now().AddDate(1, 0, 0).Unix(), ExpiresAt: expiresAt,
} }
fingerPrintBytes, _ := json.Marshal(fingerPrintMap) fingerPrintBytes, _ := json.Marshal(fingerPrintMap)
fingerPrintHash, err := crypto.EncryptAES(string(fingerPrintBytes)) fingerPrintHash, err := crypto.EncryptAES(string(fingerPrintBytes))
if err != nil { if err != nil {
return nil, "", err return nil, "", 0, err
} }
return fingerPrintMap, fingerPrintHash, nil return fingerPrintMap, fingerPrintHash, expiresAt, nil
} }
// CreateRefreshToken util to create JWT token // CreateRefreshToken util to create JWT token