Implement refresh token logic with fingerprint + rotation

This commit is contained in:
Lakhan Samani
2022-01-23 01:24:41 +05:30
parent 0511e737ae
commit 7f18a3f634
50 changed files with 802 additions and 560 deletions

View File

@@ -0,0 +1,112 @@
package sessionstore
import (
"sync"
)
// InMemoryStore is a simple in-memory store for sessions.
type InMemoryStore struct {
mutex sync.Mutex
store map[string]map[string]string
socialLoginState map[string]string
}
// AddUserSession adds a user session to the in-memory store.
func (c *InMemoryStore) AddUserSession(userId, accessToken, refreshToken string) {
c.mutex.Lock()
defer c.mutex.Unlock()
// delete sessions > 500 // not recommended for production
if len(c.store) >= 500 {
c.store = map[string]map[string]string{}
}
// check if entry exists in map
_, exists := c.store[userId]
if exists {
tempMap := c.store[userId]
tempMap[accessToken] = refreshToken
c.store[userId] = tempMap
} else {
tempMap := map[string]string{
accessToken: refreshToken,
}
c.store[userId] = tempMap
}
}
// DeleteAllUserSession deletes all the user sessions from in-memory store.
func (c *InMemoryStore) DeleteAllUserSession(userId string) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.store, userId)
}
// DeleteUserSession deletes the particular user session from in-memory store.
func (c *InMemoryStore) DeleteUserSession(userId, accessToken string) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.store[userId], accessToken)
}
// ClearStore clears the in-memory store.
func (c *InMemoryStore) ClearStore() {
c.mutex.Lock()
defer c.mutex.Unlock()
c.store = map[string]map[string]string{}
}
// GetUserSession returns the user session token from the in-memory store.
func (c *InMemoryStore) GetUserSession(userId, accessToken string) string {
// c.mutex.Lock()
// defer c.mutex.Unlock()
token := ""
if sessionMap, ok := c.store[userId]; ok {
if val, ok := sessionMap[accessToken]; ok {
token = val
}
}
return token
}
// GetUserSessions returns all the user session token from the in-memory store.
func (c *InMemoryStore) GetUserSessions(userId string) map[string]string {
// c.mutex.Lock()
// defer c.mutex.Unlock()
sessionMap, ok := c.store[userId]
if !ok {
return nil
}
return sessionMap
}
// SetSocialLoginState sets the social login state in the in-memory store.
func (c *InMemoryStore) SetSocialLoginState(key, state string) {
c.mutex.Lock()
defer c.mutex.Unlock()
c.socialLoginState[key] = state
}
// GetSocialLoginState gets the social login state from the in-memory store.
func (c *InMemoryStore) GetSocialLoginState(key string) string {
c.mutex.Lock()
defer c.mutex.Unlock()
state := ""
if stateVal, ok := c.socialLoginState[key]; ok {
state = stateVal
}
return state
}
// RemoveSocialLoginState removes the social login state from the in-memory store.
func (c *InMemoryStore) RemoveSocialLoginState(key string) {
c.mutex.Lock()
defer c.mutex.Unlock()
delete(c.socialLoginState, key)
}

View File

@@ -0,0 +1,98 @@
package sessionstore
import (
"context"
"fmt"
"log"
"github.com/go-redis/redis/v8"
)
type RedisStore struct {
ctx context.Context
store *redis.Client
}
// AddUserSession adds the user session to redis
func (c *RedisStore) AddUserSession(userId, accessToken, refreshToken string) {
err := c.store.HMSet(c.ctx, "authorizer_"+userId, map[string]string{
accessToken: refreshToken,
}).Err()
if err != nil {
log.Fatalln("Error saving redis token:", err)
}
}
// DeleteAllUserSession deletes all the user session from redis
func (c *RedisStore) DeleteAllUserSession(userId string) {
err := c.store.Del(c.ctx, "authorizer_"+userId).Err()
if err != nil {
log.Fatalln("Error deleting redis token:", err)
}
}
// DeleteUserSession deletes the particular user session from redis
func (c *RedisStore) DeleteUserSession(userId, accessToken string) {
err := c.store.HDel(c.ctx, "authorizer_"+userId, accessToken).Err()
if err != nil {
log.Fatalln("Error deleting redis token:", err)
}
}
// ClearStore clears the redis store for authorizer related tokens
func (c *RedisStore) ClearStore() {
err := c.store.Del(c.ctx, "authorizer_*").Err()
if err != nil {
log.Fatalln("Error clearing redis store:", err)
}
}
// GetUserSession returns the user session token from the redis store.
func (c *RedisStore) GetUserSession(userId, accessToken string) string {
token := ""
res, err := c.store.HMGet(c.ctx, "authorizer_"+userId, accessToken).Result()
if err != nil {
log.Println("error getting token from redis store:", err)
}
if len(res) > 0 && res[0] != nil {
token = fmt.Sprintf("%v", res[0])
}
return token
}
// GetUserSessions returns all the user session token from the redis store.
func (c *RedisStore) GetUserSessions(userID string) map[string]string {
res, err := c.store.HGetAll(c.ctx, "authorizer_"+userID).Result()
if err != nil {
log.Println("error getting token from redis store:", err)
}
return res
}
// SetSocialLoginState sets the social login state in redis store.
func (c *RedisStore) SetSocialLoginState(key, state string) {
err := c.store.Set(c.ctx, key, state, 0).Err()
if err != nil {
log.Fatalln("Error saving redis token:", err)
}
}
// GetSocialLoginState gets the social login state from redis store.
func (c *RedisStore) GetSocialLoginState(key string) string {
state := ""
state, err := c.store.Get(c.ctx, key).Result()
if err != nil {
log.Println("error getting token from redis store:", err)
}
return state
}
// RemoveSocialLoginState removes the social login state from redis store.
func (c *RedisStore) RemoveSocialLoginState(key string) {
err := c.store.Del(c.ctx, key).Err()
if err != nil {
log.Fatalln("Error deleting redis token:", err)
}
}

View File

@@ -0,0 +1,147 @@
package sessionstore
import (
"context"
"log"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/go-redis/redis/v8"
)
// SessionStore is a struct that defines available session stores
// If redis store is available, higher preference is given to that store.
// Else in memory store is used.
type SessionStore struct {
InMemoryStoreObj *InMemoryStore
RedisMemoryStoreObj *RedisStore
}
// SessionStoreObj is a global variable that holds the
// reference to various session store instances
var SessionStoreObj SessionStore
// SetUserSession sets the user session in the session store
func SetUserSession(userId, fingerprint, refreshToken string) {
if SessionStoreObj.RedisMemoryStoreObj != nil {
SessionStoreObj.RedisMemoryStoreObj.AddUserSession(userId, fingerprint, refreshToken)
}
if SessionStoreObj.InMemoryStoreObj != nil {
SessionStoreObj.InMemoryStoreObj.AddUserSession(userId, fingerprint, refreshToken)
}
}
// DeleteUserSession deletes the particular user session from the session store
func DeleteUserSession(userId, fingerprint string) {
if SessionStoreObj.RedisMemoryStoreObj != nil {
SessionStoreObj.RedisMemoryStoreObj.DeleteUserSession(userId, fingerprint)
}
if SessionStoreObj.InMemoryStoreObj != nil {
SessionStoreObj.InMemoryStoreObj.DeleteUserSession(userId, fingerprint)
}
}
// DeleteAllSessions deletes all the sessions from the session store
func DeleteAllUserSession(userId string) {
if SessionStoreObj.RedisMemoryStoreObj != nil {
SessionStoreObj.RedisMemoryStoreObj.DeleteAllUserSession(userId)
}
if SessionStoreObj.InMemoryStoreObj != nil {
SessionStoreObj.InMemoryStoreObj.DeleteAllUserSession(userId)
}
}
// GetUserSession returns the user session from the session store
func GetUserSession(userId, fingerprint string) string {
if SessionStoreObj.RedisMemoryStoreObj != nil {
return SessionStoreObj.RedisMemoryStoreObj.GetUserSession(userId, fingerprint)
}
if SessionStoreObj.InMemoryStoreObj != nil {
return SessionStoreObj.InMemoryStoreObj.GetUserSession(userId, fingerprint)
}
return ""
}
// GetUserSessions returns all the user sessions from the session store
func GetUserSessions(userId string) map[string]string {
if SessionStoreObj.RedisMemoryStoreObj != nil {
return SessionStoreObj.RedisMemoryStoreObj.GetUserSessions(userId)
}
if SessionStoreObj.InMemoryStoreObj != nil {
return SessionStoreObj.InMemoryStoreObj.GetUserSessions(userId)
}
return nil
}
// ClearStore clears the session store for authorizer tokens
func ClearStore() {
if SessionStoreObj.RedisMemoryStoreObj != nil {
SessionStoreObj.RedisMemoryStoreObj.ClearStore()
}
if SessionStoreObj.InMemoryStoreObj != nil {
SessionStoreObj.InMemoryStoreObj.ClearStore()
}
}
// SetSocialLoginState sets the social login state in the session store
func SetSocailLoginState(key, state string) {
if SessionStoreObj.RedisMemoryStoreObj != nil {
SessionStoreObj.RedisMemoryStoreObj.SetSocialLoginState(key, state)
}
if SessionStoreObj.InMemoryStoreObj != nil {
SessionStoreObj.InMemoryStoreObj.SetSocialLoginState(key, state)
}
}
// GetSocialLoginState returns the social login state from the session store
func GetSocailLoginState(key string) string {
if SessionStoreObj.RedisMemoryStoreObj != nil {
return SessionStoreObj.RedisMemoryStoreObj.GetSocialLoginState(key)
}
if SessionStoreObj.InMemoryStoreObj != nil {
return SessionStoreObj.InMemoryStoreObj.GetSocialLoginState(key)
}
return ""
}
// RemoveSocialLoginState removes the social login state from the session store
func RemoveSocialLoginState(key string) {
if SessionStoreObj.RedisMemoryStoreObj != nil {
SessionStoreObj.RedisMemoryStoreObj.RemoveSocialLoginState(key)
}
if SessionStoreObj.InMemoryStoreObj != nil {
SessionStoreObj.InMemoryStoreObj.RemoveSocialLoginState(key)
}
}
// InitializeSessionStore initializes the SessionStoreObj based on environment variables
func InitSession() {
if envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyRedisURL) != "" {
log.Println("using redis store to save sessions")
opt, err := redis.ParseURL(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyRedisURL))
if err != nil {
log.Fatalln("Error parsing redis url:", err)
}
rdb := redis.NewClient(opt)
ctx := context.Background()
_, err = rdb.Ping(ctx).Result()
if err != nil {
log.Fatalln("Error connecting to redis server", err)
}
SessionStoreObj.RedisMemoryStoreObj = &RedisStore{
ctx: ctx,
store: rdb,
}
} else {
log.Println("using in memory store to save sessions")
SessionStoreObj.InMemoryStoreObj = &InMemoryStore{
store: map[string]map[string]string{},
socialLoginState: map[string]string{},
}
}
}