fix: session invalidation
This commit is contained in:
parent
7a2dbea019
commit
926ab07c07
|
@ -68,7 +68,6 @@ func (p *provider) ListUsers(pagination model.Pagination) (*model.Users, error)
|
||||||
opts.SetSort(bson.M{"created_at": -1})
|
opts.SetSort(bson.M{"created_at": -1})
|
||||||
|
|
||||||
paginationClone := pagination
|
paginationClone := pagination
|
||||||
// TODO add pagination total
|
|
||||||
|
|
||||||
userCollection := p.db.Collection(models.Collections.User, options.Collection())
|
userCollection := p.db.Collection(models.Collections.User, options.Collection())
|
||||||
count, err := userCollection.CountDocuments(nil, bson.M{}, options.Count())
|
count, err := userCollection.CountDocuments(nil, bson.M{}, options.Count())
|
||||||
|
|
|
@ -246,7 +246,7 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
memorystore.Provider.SetState(newSessionToken, newSessionTokenData.Nonce+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, newSessionToken, newSessionTokenData.Nonce)
|
||||||
cookie.SetSession(gc, newSessionToken)
|
cookie.SetSession(gc, newSessionToken)
|
||||||
code := uuid.New().String()
|
code := uuid.New().String()
|
||||||
memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken)
|
memorystore.Provider.SetState(codeChallenge, code+"@"+newSessionToken)
|
||||||
|
@ -284,8 +284,8 @@ func AuthorizeHandler() gin.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
memorystore.Provider.RemoveState(sessionToken)
|
memorystore.Provider.RemoveState(sessionToken)
|
||||||
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||||
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
|
|
||||||
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||||
|
@ -308,7 +308,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.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isQuery {
|
if isQuery {
|
||||||
|
|
|
@ -200,12 +200,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
|
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)
|
cookie.SetSession(c, authToken.FingerPrintHash)
|
||||||
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||||
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
if authToken.RefreshToken != nil {
|
||||||
params = params + `&refresh_token=` + authToken.RefreshToken.Token
|
params = params + `&refresh_token=` + 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{
|
go db.Provider.AddSession(models.Session{
|
||||||
|
|
|
@ -185,8 +185,8 @@ func TokenHandler() gin.HandlerFunc {
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||||
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
|
|
||||||
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||||
|
@ -204,7 +204,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.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
|
||||||
}
|
}
|
||||||
|
|
||||||
gc.JSON(http.StatusOK, res)
|
gc.JSON(http.StatusOK, res)
|
||||||
|
|
|
@ -42,7 +42,7 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
||||||
|
|
||||||
// verify if token exists in db
|
// verify if token exists in db
|
||||||
hostname := parsers.GetHost(c)
|
hostname := parsers.GetHost(c)
|
||||||
claim, err := token.ParseJWTToken(tokenInQuery, hostname, verificationRequest.Nonce, verificationRequest.Email)
|
claim, err := token.ParseJWTToken(tokenInQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Error parsing token: ", err)
|
log.Debug("Error parsing token: ", err)
|
||||||
errorRes["error_description"] = err.Error()
|
errorRes["error_description"] = err.Error()
|
||||||
|
@ -50,7 +50,14 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := db.Provider.GetUserByEmail(claim["sub"].(string))
|
if ok, err := token.ValidateJWTClaims(claim, hostname, verificationRequest.Nonce, verificationRequest.Email); !ok || err != nil {
|
||||||
|
log.Debug("Error validating jwt claims: ", err)
|
||||||
|
errorRes["error_description"] = err.Error()
|
||||||
|
c.JSON(400, errorRes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := db.Provider.GetUserByEmail(verificationRequest.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Error getting user: ", err)
|
log.Debug("Error getting user: ", err)
|
||||||
errorRes["error_description"] = err.Error()
|
errorRes["error_description"] = err.Error()
|
||||||
|
@ -100,12 +107,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
|
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)
|
cookie.SetSession(c, authToken.FingerPrintHash)
|
||||||
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||||
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
if authToken.RefreshToken != nil {
|
||||||
params = params + `&refresh_token=${refresh_token}`
|
params = params + `&refresh_token=${refresh_token}`
|
||||||
memorystore.Provider.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
|
||||||
}
|
}
|
||||||
|
|
||||||
if redirectURL == "" {
|
if redirectURL == "" {
|
||||||
|
|
|
@ -2,24 +2,23 @@ package inmemory
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/memorystore/providers/inmemory/stores"
|
||||||
)
|
)
|
||||||
|
|
||||||
type provider struct {
|
type provider struct {
|
||||||
mutex sync.Mutex
|
mutex sync.Mutex
|
||||||
sessionStore map[string]map[string]string
|
sessionStore *stores.SessionStore
|
||||||
stateStore map[string]string
|
stateStore *stores.StateStore
|
||||||
envStore *EnvStore
|
envStore *stores.EnvStore
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInMemoryStore returns a new in-memory store.
|
// NewInMemoryStore returns a new in-memory store.
|
||||||
func NewInMemoryProvider() (*provider, error) {
|
func NewInMemoryProvider() (*provider, error) {
|
||||||
return &provider{
|
return &provider{
|
||||||
mutex: sync.Mutex{},
|
mutex: sync.Mutex{},
|
||||||
sessionStore: map[string]map[string]string{},
|
envStore: stores.NewEnvStore(),
|
||||||
stateStore: map[string]string{},
|
sessionStore: stores.NewSessionStore(),
|
||||||
envStore: &EnvStore{
|
stateStore: stores.NewStateStore(),
|
||||||
mutex: sync.Mutex{},
|
|
||||||
store: map[string]interface{}{},
|
|
||||||
},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,46 +3,44 @@ package inmemory
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClearStore clears the in-memory store.
|
// SetUserSession sets the user session
|
||||||
func (c *provider) ClearStore() error {
|
func (c *provider) SetUserSession(userId, key, token string) error {
|
||||||
if os.Getenv("ENV") != constants.TestEnv {
|
c.sessionStore.Set(userId, key, token)
|
||||||
c.mutex.Lock()
|
|
||||||
defer c.mutex.Unlock()
|
|
||||||
}
|
|
||||||
c.sessionStore = map[string]map[string]string{}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserSessions returns all the user session token from the in-memory store.
|
// GetAllUserSessions returns all the user sessions token from the in-memory store.
|
||||||
func (c *provider) GetUserSessions(userId string) map[string]string {
|
func (c *provider) GetAllUserSessions(userId string) (map[string]string, error) {
|
||||||
res := map[string]string{}
|
data := c.sessionStore.GetAll(userId)
|
||||||
for k, v := range c.stateStore {
|
return data, nil
|
||||||
split := strings.Split(v, "@")
|
|
||||||
if split[1] == userId {
|
|
||||||
res[k] = split[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAllUserSession deletes all the user sessions from in-memory store.
|
// GetUserSession returns value for given session token
|
||||||
func (c *provider) DeleteAllUserSession(userId string) error {
|
func (c *provider) GetUserSession(userId, sessionToken string) (string, error) {
|
||||||
|
return c.sessionStore.Get(userId, sessionToken), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAllUserSessions deletes all the user sessions from in-memory store.
|
||||||
|
func (c *provider) DeleteAllUserSessions(userId string) error {
|
||||||
if os.Getenv("ENV") != constants.TestEnv {
|
if os.Getenv("ENV") != constants.TestEnv {
|
||||||
c.mutex.Lock()
|
c.mutex.Lock()
|
||||||
defer c.mutex.Unlock()
|
defer c.mutex.Unlock()
|
||||||
}
|
}
|
||||||
sessions := c.GetUserSessions(userId)
|
c.sessionStore.RemoveAll(userId)
|
||||||
for k := range sessions {
|
return nil
|
||||||
c.RemoveState(k)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// DeleteUserSession deletes the user session from the in-memory store.
|
||||||
|
func (c *provider) DeleteUserSession(userId, sessionToken string) error {
|
||||||
|
if os.Getenv("ENV") != constants.TestEnv {
|
||||||
|
c.mutex.Lock()
|
||||||
|
defer c.mutex.Unlock()
|
||||||
|
}
|
||||||
|
c.sessionStore.Remove(userId, sessionToken)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,29 +50,19 @@ func (c *provider) SetState(key, state string) error {
|
||||||
c.mutex.Lock()
|
c.mutex.Lock()
|
||||||
defer c.mutex.Unlock()
|
defer c.mutex.Unlock()
|
||||||
}
|
}
|
||||||
c.stateStore[key] = state
|
c.stateStore.Set(key, state)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetState gets the state from the in-memory store.
|
// GetState gets the state from the in-memory store.
|
||||||
func (c *provider) GetState(key string) (string, error) {
|
func (c *provider) GetState(key string) (string, error) {
|
||||||
state := ""
|
return c.stateStore.Get(key), nil
|
||||||
if stateVal, ok := c.stateStore[key]; ok {
|
|
||||||
state = stateVal
|
|
||||||
}
|
|
||||||
|
|
||||||
return state, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveState removes the state from the in-memory store.
|
// RemoveState removes the state from the in-memory store.
|
||||||
func (c *provider) RemoveState(key string) error {
|
func (c *provider) RemoveState(key string) error {
|
||||||
if os.Getenv("ENV") != constants.TestEnv {
|
c.stateStore.Remove(key)
|
||||||
c.mutex.Lock()
|
|
||||||
defer c.mutex.Unlock()
|
|
||||||
}
|
|
||||||
delete(c.stateStore, key)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package inmemory
|
package stores
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
@ -13,6 +13,14 @@ type EnvStore struct {
|
||||||
store map[string]interface{}
|
store map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewEnvStore create a new env store
|
||||||
|
func NewEnvStore() *EnvStore {
|
||||||
|
return &EnvStore{
|
||||||
|
mutex: sync.Mutex{},
|
||||||
|
store: make(map[string]interface{}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// UpdateEnvStore to update the whole env store object
|
// UpdateEnvStore to update the whole env store object
|
||||||
func (e *EnvStore) UpdateStore(store map[string]interface{}) {
|
func (e *EnvStore) UpdateStore(store map[string]interface{}) {
|
||||||
if os.Getenv("ENV") != constants.TestEnv {
|
if os.Getenv("ENV") != constants.TestEnv {
|
|
@ -0,0 +1,67 @@
|
||||||
|
package stores
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SessionStore struct to store the env variables
|
||||||
|
type SessionStore struct {
|
||||||
|
mutex sync.Mutex
|
||||||
|
store map[string]map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSessionStore create a new session store
|
||||||
|
func NewSessionStore() *SessionStore {
|
||||||
|
return &SessionStore{
|
||||||
|
mutex: sync.Mutex{},
|
||||||
|
store: make(map[string]map[string]string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value of the key in state store
|
||||||
|
func (s *SessionStore) Get(key, subKey string) string {
|
||||||
|
return s.store[key][subKey]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the value of the key in state store
|
||||||
|
func (s *SessionStore) Set(key string, subKey, value string) {
|
||||||
|
if os.Getenv("ENV") != constants.TestEnv {
|
||||||
|
s.mutex.Lock()
|
||||||
|
defer s.mutex.Unlock()
|
||||||
|
}
|
||||||
|
if _, ok := s.store[key]; !ok {
|
||||||
|
s.store[key] = make(map[string]string)
|
||||||
|
}
|
||||||
|
s.store[key][subKey] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAll all values for given key
|
||||||
|
func (s *SessionStore) RemoveAll(key string) {
|
||||||
|
if os.Getenv("ENV") != constants.TestEnv {
|
||||||
|
s.mutex.Lock()
|
||||||
|
defer s.mutex.Unlock()
|
||||||
|
}
|
||||||
|
delete(s.store, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove value for given key and subkey
|
||||||
|
func (s *SessionStore) Remove(key, subKey string) {
|
||||||
|
if os.Getenv("ENV") != constants.TestEnv {
|
||||||
|
s.mutex.Lock()
|
||||||
|
defer s.mutex.Unlock()
|
||||||
|
}
|
||||||
|
if _, ok := s.store[key]; ok {
|
||||||
|
delete(s.store[key], subKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all the values for given key
|
||||||
|
func (s *SessionStore) GetAll(key string) map[string]string {
|
||||||
|
if _, ok := s.store[key]; !ok {
|
||||||
|
s.store[key] = make(map[string]string)
|
||||||
|
}
|
||||||
|
return s.store[key]
|
||||||
|
}
|
46
server/memorystore/providers/inmemory/stores/state_store.go
Normal file
46
server/memorystore/providers/inmemory/stores/state_store.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package stores
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StateStore struct to store the env variables
|
||||||
|
type StateStore struct {
|
||||||
|
mutex sync.Mutex
|
||||||
|
store map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStateStore create a new state store
|
||||||
|
func NewStateStore() *StateStore {
|
||||||
|
return &StateStore{
|
||||||
|
mutex: sync.Mutex{},
|
||||||
|
store: make(map[string]string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the value of the key in state store
|
||||||
|
func (s *StateStore) Get(key string) string {
|
||||||
|
return s.store[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets the value of the key in state store
|
||||||
|
func (s *StateStore) Set(key string, value string) {
|
||||||
|
if os.Getenv("ENV") != constants.TestEnv {
|
||||||
|
s.mutex.Lock()
|
||||||
|
defer s.mutex.Unlock()
|
||||||
|
}
|
||||||
|
s.store[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the key from state store
|
||||||
|
func (s *StateStore) Remove(key string) {
|
||||||
|
if os.Getenv("ENV") != constants.TestEnv {
|
||||||
|
s.mutex.Lock()
|
||||||
|
defer s.mutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(s.store, key)
|
||||||
|
}
|
|
@ -2,12 +2,17 @@ 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
|
||||||
|
SetUserSession(userId, key, token string) error
|
||||||
|
// 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)
|
||||||
|
// DeleteUserSession deletes the user session
|
||||||
|
DeleteUserSession(userId, token string) error
|
||||||
// DeleteAllSessions deletes all the sessions from the session store
|
// DeleteAllSessions deletes all the sessions from the session store
|
||||||
DeleteAllUserSession(userId string) error
|
DeleteAllUserSessions(userId string) error
|
||||||
// GetUserSessions returns all the user sessions from the session store
|
|
||||||
GetUserSessions(userId string) map[string]string
|
|
||||||
// ClearStore clears the session store for authorizer tokens
|
|
||||||
ClearStore() error
|
|
||||||
// SetState sets the login state (key, value form) in the session store
|
// SetState sets the login state (key, value form) in the session store
|
||||||
SetState(key, state string) error
|
SetState(key, state string) error
|
||||||
// GetState returns the state from the session store
|
// GetState returns the state from the session store
|
||||||
|
|
|
@ -2,67 +2,72 @@ package redis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// session store prefix
|
// state store prefix
|
||||||
sessionStorePrefix = "authorizer_session:"
|
stateStorePrefix = "authorizer_state:"
|
||||||
// env store prefix
|
// env store prefix
|
||||||
envStorePrefix = "authorizer_env"
|
envStorePrefix = "authorizer_env"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClearStore clears the redis store for authorizer related tokens
|
// SetUserSession sets the user session in redis store.
|
||||||
func (c *provider) ClearStore() error {
|
func (c *provider) SetUserSession(userId, key, token string) error {
|
||||||
err := c.store.Del(c.ctx, sessionStorePrefix+"*").Err()
|
err := c.store.HSet(c.ctx, userId, key, token).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Error clearing redis store: ", err)
|
log.Debug("Error saving to redis: ", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserSessions returns all the user session token from the redis store.
|
// GetAllUserSessions returns all the user session token from the redis store.
|
||||||
func (c *provider) GetUserSessions(userID string) map[string]string {
|
func (c *provider) GetAllUserSessions(userID string) (map[string]string, error) {
|
||||||
data, err := c.store.HGetAll(c.ctx, "*").Result()
|
data, err := c.store.HGetAll(c.ctx, userID).Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("error getting token from redis store: ", err)
|
log.Debug("error getting all user sessions from redis store: ", err)
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
res := map[string]string{}
|
return data, nil
|
||||||
for k, v := range data {
|
|
||||||
split := strings.Split(v, "@")
|
|
||||||
if split[1] == userID {
|
|
||||||
res[k] = split[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteAllUserSession deletes all the user session from redis
|
// GetUserSession returns the user session from redis store.
|
||||||
func (c *provider) DeleteAllUserSession(userId string) error {
|
func (c *provider) GetUserSession(userId, key string) (string, error) {
|
||||||
sessions := c.GetUserSessions(userId)
|
var res string
|
||||||
for k, v := range sessions {
|
err := c.store.HGet(c.ctx, userId, key).Scan(&res)
|
||||||
if k == "token" {
|
if err != nil {
|
||||||
err := c.store.Del(c.ctx, v).Err()
|
return "", err
|
||||||
if err != nil {
|
|
||||||
log.Debug("Error deleting redis token: ", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return res, 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 {
|
||||||
|
log.Debug("Error deleting user session from redis: ", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAllUserSessions deletes all the user session from redis
|
||||||
|
func (c *provider) DeleteAllUserSessions(userID string) error {
|
||||||
|
err := c.store.HDel(c.ctx, userID).Err()
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error deleting all user sessions from redis: ", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetState sets the state in redis store.
|
// SetState sets the state in redis store.
|
||||||
func (c *provider) SetState(key, value string) error {
|
func (c *provider) SetState(key, value string) error {
|
||||||
err := c.store.Set(c.ctx, sessionStorePrefix+key, value, 0).Err()
|
err := c.store.Set(c.ctx, stateStorePrefix+key, value, 0).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Error saving redis token: ", err)
|
log.Debug("Error saving redis token: ", err)
|
||||||
return err
|
return err
|
||||||
|
@ -74,7 +79,7 @@ func (c *provider) SetState(key, value string) error {
|
||||||
// GetState gets the state from redis store.
|
// GetState gets the state from redis store.
|
||||||
func (c *provider) GetState(key string) (string, error) {
|
func (c *provider) GetState(key string) (string, error) {
|
||||||
var res string
|
var res string
|
||||||
err := c.store.Get(c.ctx, sessionStorePrefix+key).Scan(&res)
|
err := c.store.Get(c.ctx, stateStorePrefix+key).Scan(&res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("error getting token from redis store: ", err)
|
log.Debug("error getting token from redis store: ", err)
|
||||||
}
|
}
|
||||||
|
@ -84,7 +89,7 @@ func (c *provider) GetState(key string) (string, error) {
|
||||||
|
|
||||||
// RemoveState removes the state from redis store.
|
// RemoveState removes the state from redis store.
|
||||||
func (c *provider) RemoveState(key string) error {
|
func (c *provider) RemoveState(key string) error {
|
||||||
err := c.store.Del(c.ctx, sessionStorePrefix+key).Err()
|
err := c.store.Del(c.ctx, stateStorePrefix+key).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("Error deleting redis token: ", err)
|
log.Fatalln("Error deleting redis token: ", err)
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -38,7 +38,7 @@ func DeleteUserResolver(ctx context.Context, params model.DeleteUserInput) (*mod
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
go memorystore.Provider.DeleteAllUserSession(fmt.Sprintf("%x", user.ID))
|
go memorystore.Provider.DeleteAllUserSessions(user.ID)
|
||||||
|
|
||||||
err = db.Provider.DeleteUser(user)
|
err = db.Provider.DeleteUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -117,12 +117,12 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
||||||
}
|
}
|
||||||
|
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||||
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
if authToken.RefreshToken != nil {
|
||||||
res.RefreshToken = &authToken.RefreshToken.Token
|
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{
|
go db.Provider.AddSession(models.Session{
|
||||||
|
|
|
@ -2,6 +2,7 @@ package resolvers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
@ -9,38 +10,41 @@ import (
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
"github.com/authorizerdev/authorizer/server/crypto"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/memorystore"
|
"github.com/authorizerdev/authorizer/server/memorystore"
|
||||||
|
"github.com/authorizerdev/authorizer/server/token"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LogoutResolver is a resolver for logout mutation
|
// LogoutResolver is a resolver for logout mutation
|
||||||
func LogoutResolver(ctx context.Context) (*model.Response, error) {
|
func LogoutResolver(ctx context.Context) (*model.Response, error) {
|
||||||
var res *model.Response
|
|
||||||
|
|
||||||
gc, err := utils.GinContextFromContext(ctx)
|
gc, err := utils.GinContextFromContext(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to get GinContext: ", err)
|
log.Debug("Failed to get GinContext: ", err)
|
||||||
return res, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// get fingerprint hash
|
// get fingerprint hash
|
||||||
fingerprintHash, err := cookie.GetSession(gc)
|
fingerprintHash, err := cookie.GetSession(gc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to get fingerprint hash: ", err)
|
log.Debug("Failed to get fingerprint hash: ", err)
|
||||||
return res, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
decryptedFingerPrint, err := crypto.DecryptAES(fingerprintHash)
|
decryptedFingerPrint, err := crypto.DecryptAES(fingerprintHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to decrypt fingerprint hash: ", err)
|
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)
|
cookie.DeleteSession(gc)
|
||||||
|
|
||||||
res = &model.Response{
|
res := &model.Response{
|
||||||
Message: "Logged out successfully",
|
Message: "Logged out successfully",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,12 +57,17 @@ func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput)
|
||||||
|
|
||||||
// verify if token exists in db
|
// verify if token exists in db
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
claim, err := token.ParseJWTToken(params.Token, hostname, verificationRequest.Nonce, verificationRequest.Email)
|
claim, err := token.ParseJWTToken(params.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to parse token: ", err)
|
log.Debug("Failed to parse token: ", err)
|
||||||
return res, fmt.Errorf(`invalid token`)
|
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)
|
email := claim["sub"].(string)
|
||||||
log := log.WithFields(log.Fields{
|
log := log.WithFields(log.Fields{
|
||||||
"email": email,
|
"email": email,
|
||||||
|
|
|
@ -47,7 +47,7 @@ func RevokeAccessResolver(ctx context.Context, params model.UpdateAccessInput) (
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
go memorystore.Provider.DeleteAllUserSession(fmt.Sprintf("%x", user.ID))
|
go memorystore.Provider.DeleteAllUserSessions(user.ID)
|
||||||
|
|
||||||
res = &model.Response{
|
res = &model.Response{
|
||||||
Message: `user access revoked successfully`,
|
Message: `user access revoked successfully`,
|
||||||
|
|
|
@ -77,8 +77,8 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
|
||||||
|
|
||||||
// rollover the session for security
|
// rollover the session for security
|
||||||
memorystore.Provider.RemoveState(sessionToken)
|
memorystore.Provider.RemoveState(sessionToken)
|
||||||
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||||
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
|
|
||||||
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||||
|
@ -96,7 +96,7 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
|
||||||
|
|
||||||
if authToken.RefreshToken != nil {
|
if authToken.RefreshToken != nil {
|
||||||
res.RefreshToken = &authToken.RefreshToken.Token
|
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
|
return res, nil
|
||||||
|
|
|
@ -225,7 +225,14 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
|
||||||
return res, err
|
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)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
go db.Provider.AddSession(models.Session{
|
go db.Provider.AddSession(models.Session{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
|
|
|
@ -142,7 +142,7 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
|
||||||
return res, fmt.Errorf("user with this email address already exists")
|
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)
|
go cookie.DeleteSession(gc)
|
||||||
|
|
||||||
user.Email = newEmail
|
user.Email = newEmail
|
||||||
|
|
|
@ -113,7 +113,7 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO figure out how to do this
|
// TODO figure out how to do this
|
||||||
go memorystore.Provider.DeleteAllUserSession(user.ID)
|
go memorystore.Provider.DeleteAllUserSessions(user.ID)
|
||||||
|
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
user.Email = newEmail
|
user.Email = newEmail
|
||||||
|
@ -182,7 +182,7 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
|
||||||
rolesToSave = strings.Join(inputRoles, ",")
|
rolesToSave = strings.Join(inputRoles, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
go memorystore.Provider.DeleteAllUserSession(user.ID)
|
go memorystore.Provider.DeleteAllUserSessions(user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rolesToSave != "" {
|
if rolesToSave != "" {
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
@ -35,43 +34,46 @@ func ValidateJwtTokenResolver(ctx context.Context, params model.ValidateJWTToken
|
||||||
return nil, errors.New("invalid token type")
|
return nil, errors.New("invalid token type")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var claimRoles []string
|
||||||
|
var claims jwt.MapClaims
|
||||||
userID := ""
|
userID := ""
|
||||||
nonce := ""
|
nonce := ""
|
||||||
// access_token and refresh_token should be validated from session store as well
|
// access_token and refresh_token should be validated from session store as well
|
||||||
if tokenType == "access_token" || tokenType == "refresh_token" {
|
if tokenType == "access_token" || tokenType == "refresh_token" {
|
||||||
savedSession, err := memorystore.Provider.GetState(params.Token)
|
claims, err = token.ParseJWTToken(params.Token)
|
||||||
if savedSession == "" || err != nil {
|
if err != nil {
|
||||||
return &model.ValidateJWTTokenResponse{
|
log.Debug("Failed to parse JWT token: ", err)
|
||||||
IsValid: false,
|
return nil, err
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
savedSessionSplit := strings.Split(savedSession, "@")
|
userID = claims["sub"].(string)
|
||||||
nonce = savedSessionSplit[0]
|
nonce, err = memorystore.Provider.GetUserSession(userID, params.Token)
|
||||||
userID = savedSessionSplit[1]
|
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)
|
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
|
// we cannot validate sub and nonce in case of id_token as that token is not persisted in session store
|
||||||
if userID != "" && nonce != "" {
|
if userID != "" && nonce != "" {
|
||||||
claims, err = token.ParseJWTToken(params.Token, hostname, nonce, userID)
|
if ok, err := token.ValidateJWTClaims(claims, hostname, nonce, userID); !ok || err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Debug("Failed to parse jwt token: ", err)
|
log.Debug("Failed to parse jwt token: ", err)
|
||||||
return &model.ValidateJWTTokenResponse{
|
return nil, errors.New("invalid claims")
|
||||||
IsValid: false,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
claims, err = token.ParseJWTTokenWithoutNonce(params.Token, hostname)
|
if ok, err := token.ValidateJWTTokenWithoutNonce(claims, hostname); !ok || err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Debug("Failed to parse jwt token without nonce: ", err)
|
log.Debug("Failed to parse jwt token without nonce: ", err)
|
||||||
return &model.ValidateJWTTokenResponse{
|
return nil, errors.New("invalid claims")
|
||||||
IsValid: false,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
claimRolesInterface := claims["roles"]
|
claimRolesInterface := claims["roles"]
|
||||||
|
|
|
@ -36,12 +36,17 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
|
||||||
|
|
||||||
// verify if token exists in db
|
// verify if token exists in db
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
claim, err := token.ParseJWTToken(params.Token, hostname, verificationRequest.Nonce, verificationRequest.Email)
|
claim, err := token.ParseJWTToken(params.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to parse token: ", err)
|
log.Debug("Failed to parse token: ", err)
|
||||||
return res, fmt.Errorf(`invalid token: %s`, err.Error())
|
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)
|
email := claim["sub"].(string)
|
||||||
log := log.WithFields(log.Fields{
|
log := log.WithFields(log.Fields{
|
||||||
"email": email,
|
"email": email,
|
||||||
|
@ -75,8 +80,14 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
memorystore.Provider.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||||
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
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)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
go db.Provider.AddSession(models.Session{
|
go db.Provider.AddSession(models.Session{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
|
|
|
@ -52,7 +52,7 @@ func TestJwt(t *testing.T) {
|
||||||
}
|
}
|
||||||
jwtToken, err := token.SignJWTToken(expiredClaims)
|
jwtToken, err := token.SignJWTToken(expiredClaims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
_, err = token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
_, err = token.ParseJWTToken(jwtToken)
|
||||||
assert.Error(t, err, err.Error(), "Token is expired")
|
assert.Error(t, err, err.Error(), "Token is expired")
|
||||||
})
|
})
|
||||||
t.Run("HMAC algorithms", func(t *testing.T) {
|
t.Run("HMAC algorithms", func(t *testing.T) {
|
||||||
|
@ -62,27 +62,36 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
c, err := token.ParseJWTToken(jwtToken)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
|
valid, err := token.ValidateJWTClaims(c, hostname, nonce, subject)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, valid)
|
||||||
})
|
})
|
||||||
t.Run("HS384", func(t *testing.T) {
|
t.Run("HS384", func(t *testing.T) {
|
||||||
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyJwtType, "HS384")
|
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyJwtType, "HS384")
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
c, err := token.ParseJWTToken(jwtToken)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
|
valid, err := token.ValidateJWTClaims(c, hostname, nonce, subject)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, valid)
|
||||||
})
|
})
|
||||||
t.Run("HS512", func(t *testing.T) {
|
t.Run("HS512", func(t *testing.T) {
|
||||||
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyJwtType, "HS512")
|
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyJwtType, "HS512")
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
c, err := token.ParseJWTToken(jwtToken)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
|
valid, err := token.ValidateJWTClaims(c, hostname, nonce, subject)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, valid)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -96,9 +105,12 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
c, err := token.ParseJWTToken(jwtToken)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
|
valid, err := token.ValidateJWTClaims(c, hostname, nonce, subject)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, valid)
|
||||||
})
|
})
|
||||||
t.Run("RS384", func(t *testing.T) {
|
t.Run("RS384", func(t *testing.T) {
|
||||||
_, privateKey, publickKey, _, err := crypto.NewRSAKey("RS384", clientID)
|
_, privateKey, publickKey, _, err := crypto.NewRSAKey("RS384", clientID)
|
||||||
|
@ -109,9 +121,12 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
c, err := token.ParseJWTToken(jwtToken)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
|
valid, err := token.ValidateJWTClaims(c, hostname, nonce, subject)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, valid)
|
||||||
})
|
})
|
||||||
t.Run("RS512", func(t *testing.T) {
|
t.Run("RS512", func(t *testing.T) {
|
||||||
_, privateKey, publickKey, _, err := crypto.NewRSAKey("RS512", clientID)
|
_, privateKey, publickKey, _, err := crypto.NewRSAKey("RS512", clientID)
|
||||||
|
@ -122,9 +137,12 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
c, err := token.ParseJWTToken(jwtToken)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
|
valid, err := token.ValidateJWTClaims(c, hostname, nonce, subject)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, valid)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -138,9 +156,12 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
c, err := token.ParseJWTToken(jwtToken)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
|
valid, err := token.ValidateJWTClaims(c, hostname, nonce, subject)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, valid)
|
||||||
})
|
})
|
||||||
t.Run("ES384", func(t *testing.T) {
|
t.Run("ES384", func(t *testing.T) {
|
||||||
_, privateKey, publickKey, _, err := crypto.NewECDSAKey("ES384", clientID)
|
_, privateKey, publickKey, _, err := crypto.NewECDSAKey("ES384", clientID)
|
||||||
|
@ -151,9 +172,12 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
c, err := token.ParseJWTToken(jwtToken)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
|
valid, err := token.ValidateJWTClaims(c, hostname, nonce, subject)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, valid)
|
||||||
})
|
})
|
||||||
t.Run("ES512", func(t *testing.T) {
|
t.Run("ES512", func(t *testing.T) {
|
||||||
_, privateKey, publickKey, _, err := crypto.NewECDSAKey("ES512", clientID)
|
_, privateKey, publickKey, _, err := crypto.NewECDSAKey("ES512", clientID)
|
||||||
|
@ -164,9 +188,12 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
c, err := token.ParseJWTToken(jwtToken)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
|
valid, err := token.ValidateJWTClaims(c, hostname, nonce, subject)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, valid)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
@ -28,14 +29,11 @@ func logoutTests(t *testing.T, s TestSetup) {
|
||||||
})
|
})
|
||||||
|
|
||||||
token := *verifyRes.AccessToken
|
token := *verifyRes.AccessToken
|
||||||
sessions := memorystore.Provider.GetUserSessions(verifyRes.User.ID)
|
session, err := memorystore.Provider.GetUserSession(verifyRes.User.ID, token)
|
||||||
cookie := ""
|
assert.NoError(t, err)
|
||||||
// set all they keys in cookie one of them should be session cookie
|
assert.NotEmpty(t, session)
|
||||||
for key := range sessions {
|
cookie := fmt.Sprintf("%s=%s;", constants.AppCookieName+"_session", session)
|
||||||
if key != token {
|
cookie = strings.TrimSuffix(cookie, ";")
|
||||||
cookie += fmt.Sprintf("%s=%s;", constants.AppCookieName+"_session", key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Cookie", cookie)
|
req.Header.Set("Cookie", cookie)
|
||||||
_, err = resolvers.LogoutResolver(ctx)
|
_, err = resolvers.LogoutResolver(ctx)
|
||||||
|
|
|
@ -33,15 +33,11 @@ func sessionTests(t *testing.T, s TestSetup) {
|
||||||
Token: verificationRequest.Token,
|
Token: verificationRequest.Token,
|
||||||
})
|
})
|
||||||
|
|
||||||
sessions := memorystore.Provider.GetUserSessions(verifyRes.User.ID)
|
|
||||||
cookie := ""
|
|
||||||
token := *verifyRes.AccessToken
|
token := *verifyRes.AccessToken
|
||||||
// set all they keys in cookie one of them should be session cookie
|
session, err := memorystore.Provider.GetUserSession(verifyRes.User.ID, token)
|
||||||
for key := range sessions {
|
assert.NoError(t, err)
|
||||||
if key != token {
|
assert.NotEmpty(t, session)
|
||||||
cookie += fmt.Sprintf("%s=%s;", constants.AppCookieName+"_session", key)
|
cookie := fmt.Sprintf("%s=%s;", constants.AppCookieName+"_session", session)
|
||||||
}
|
|
||||||
}
|
|
||||||
cookie = strings.TrimSuffix(cookie, ";")
|
cookie = strings.TrimSuffix(cookie, ";")
|
||||||
|
|
||||||
req.Header.Set("Cookie", cookie)
|
req.Header.Set("Cookie", cookie)
|
||||||
|
|
|
@ -22,12 +22,14 @@ func validateJwtTokenTest(t *testing.T, s TestSetup) {
|
||||||
TokenType: "access_token",
|
TokenType: "access_token",
|
||||||
Token: "",
|
Token: "",
|
||||||
})
|
})
|
||||||
assert.False(t, res.IsValid)
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, res)
|
||||||
res, err = resolvers.ValidateJwtTokenResolver(ctx, model.ValidateJWTTokenInput{
|
res, err = resolvers.ValidateJwtTokenResolver(ctx, model.ValidateJWTTokenInput{
|
||||||
TokenType: "access_token",
|
TokenType: "access_token",
|
||||||
Token: "invalid",
|
Token: "invalid",
|
||||||
})
|
})
|
||||||
assert.False(t, res.IsValid)
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, res)
|
||||||
_, err = resolvers.ValidateJwtTokenResolver(ctx, model.ValidateJWTTokenInput{
|
_, err = resolvers.ValidateJwtTokenResolver(ctx, model.ValidateJWTTokenInput{
|
||||||
TokenType: "access_token_invalid",
|
TokenType: "access_token_invalid",
|
||||||
Token: "invalid@invalid",
|
Token: "invalid@invalid",
|
||||||
|
@ -48,8 +50,9 @@ func validateJwtTokenTest(t *testing.T, s TestSetup) {
|
||||||
gc, err := utils.GinContextFromContext(ctx)
|
gc, err := utils.GinContextFromContext(ctx)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope)
|
authToken, err := token.CreateAuthToken(gc, user, roles, scope)
|
||||||
memorystore.Provider.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.FingerPrintHash, authToken.FingerPrint)
|
||||||
memorystore.Provider.SetState(authToken.RefreshToken.Token, authToken.FingerPrint+"@"+user.ID)
|
memorystore.Provider.SetUserSession(user.ID, authToken.AccessToken.Token, authToken.FingerPrint)
|
||||||
|
memorystore.Provider.SetUserSession(user.ID, authToken.RefreshToken.Token, authToken.FingerPrint)
|
||||||
|
|
||||||
t.Run(`should validate the access token`, func(t *testing.T) {
|
t.Run(`should validate the access token`, func(t *testing.T) {
|
||||||
res, err := resolvers.ValidateJwtTokenResolver(ctx, model.ValidateJWTTokenInput{
|
res, err := resolvers.ValidateJwtTokenResolver(ctx, model.ValidateJWTTokenInput{
|
||||||
|
@ -57,7 +60,6 @@ func validateJwtTokenTest(t *testing.T, s TestSetup) {
|
||||||
Token: authToken.AccessToken.Token,
|
Token: authToken.AccessToken.Token,
|
||||||
Roles: []string{"user"},
|
Roles: []string{"user"},
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.True(t, res.IsValid)
|
assert.True(t, res.IsValid)
|
||||||
|
|
||||||
|
|
|
@ -198,18 +198,19 @@ func ValidateAccessToken(gc *gin.Context, accessToken string) (map[string]interf
|
||||||
return res, fmt.Errorf(`unauthorized`)
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
}
|
}
|
||||||
|
|
||||||
savedSession, err := memorystore.Provider.GetState(accessToken)
|
res, err := ParseJWTToken(accessToken)
|
||||||
if savedSession == "" || err != nil {
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userID := res["sub"].(string)
|
||||||
|
nonce, err := memorystore.Provider.GetUserSession(userID, accessToken)
|
||||||
|
if nonce == "" || err != nil {
|
||||||
return res, fmt.Errorf(`unauthorized`)
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
}
|
}
|
||||||
|
|
||||||
savedSessionSplit := strings.Split(savedSession, "@")
|
|
||||||
nonce := savedSessionSplit[0]
|
|
||||||
userID := savedSessionSplit[1]
|
|
||||||
|
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
res, err = ParseJWTToken(accessToken, hostname, nonce, userID)
|
if ok, err := ValidateJWTClaims(res, hostname, nonce, userID); !ok || err != nil {
|
||||||
if err != nil {
|
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,18 +229,19 @@ func ValidateRefreshToken(gc *gin.Context, refreshToken string) (map[string]inte
|
||||||
return res, fmt.Errorf(`unauthorized`)
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
}
|
}
|
||||||
|
|
||||||
savedSession, err := memorystore.Provider.GetState(refreshToken)
|
res, err := ParseJWTToken(refreshToken)
|
||||||
if savedSession == "" || err != nil {
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userID := res["sub"].(string)
|
||||||
|
nonce, err := memorystore.Provider.GetUserSession(userID, refreshToken)
|
||||||
|
if nonce == "" || err != nil {
|
||||||
return res, fmt.Errorf(`unauthorized`)
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
}
|
}
|
||||||
|
|
||||||
savedSessionSplit := strings.Split(savedSession, "@")
|
|
||||||
nonce := savedSessionSplit[0]
|
|
||||||
userID := savedSessionSplit[1]
|
|
||||||
|
|
||||||
hostname := parsers.GetHost(gc)
|
hostname := parsers.GetHost(gc)
|
||||||
res, err = ParseJWTToken(refreshToken, hostname, nonce, userID)
|
if ok, err := ValidateJWTClaims(res, hostname, nonce, userID); !ok || err != nil {
|
||||||
if err != nil {
|
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,15 +257,6 @@ func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionD
|
||||||
return nil, fmt.Errorf(`unauthorized`)
|
return nil, fmt.Errorf(`unauthorized`)
|
||||||
}
|
}
|
||||||
|
|
||||||
savedSession, err := memorystore.Provider.GetState(encryptedSession)
|
|
||||||
if savedSession == "" || err != nil {
|
|
||||||
return nil, fmt.Errorf(`unauthorized`)
|
|
||||||
}
|
|
||||||
|
|
||||||
savedSessionSplit := strings.Split(savedSession, "@")
|
|
||||||
nonce := savedSessionSplit[0]
|
|
||||||
userID := savedSessionSplit[1]
|
|
||||||
|
|
||||||
decryptedFingerPrint, err := crypto.DecryptAES(encryptedSession)
|
decryptedFingerPrint, err := crypto.DecryptAES(encryptedSession)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -275,23 +268,20 @@ func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionD
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if res.Nonce != nonce {
|
nonce, err := memorystore.Provider.GetUserSession(res.Subject, encryptedSession)
|
||||||
return nil, fmt.Errorf(`unauthorized: invalid nonce`)
|
if nonce == "" || err != nil {
|
||||||
|
log.Debug("invalid browser session:", err)
|
||||||
|
return nil, fmt.Errorf(`unauthorized`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if res.Subject != userID {
|
if res.Nonce != nonce {
|
||||||
return nil, fmt.Errorf(`unauthorized: invalid user id`)
|
return nil, fmt.Errorf(`unauthorized: invalid nonce`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if res.ExpiresAt < time.Now().Unix() {
|
if res.ExpiresAt < time.Now().Unix() {
|
||||||
return nil, fmt.Errorf(`unauthorized: token expired`)
|
return nil, fmt.Errorf(`unauthorized: token expired`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO validate scope
|
|
||||||
// if !reflect.DeepEqual(res.Roles, roles) {
|
|
||||||
// return res, "", fmt.Errorf(`unauthorized`)
|
|
||||||
// }
|
|
||||||
|
|
||||||
return &res, nil
|
return &res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ func SignJWTToken(claims jwt.MapClaims) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseJWTToken common util to parse jwt token
|
// ParseJWTToken common util to parse jwt token
|
||||||
func ParseJWTToken(token, hostname, nonce, subject string) (jwt.MapClaims, error) {
|
func ParseJWTToken(token string) (jwt.MapClaims, error) {
|
||||||
jwtType, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyJwtType)
|
jwtType, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyJwtType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -116,98 +116,48 @@ func ParseJWTToken(token, hostname, nonce, subject string) (jwt.MapClaims, error
|
||||||
intIat := int64(claims["iat"].(float64))
|
intIat := int64(claims["iat"].(float64))
|
||||||
claims["exp"] = intExp
|
claims["exp"] = intExp
|
||||||
claims["iat"] = intIat
|
claims["iat"] = intIat
|
||||||
|
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateJWTClaims common util to validate claims
|
||||||
|
func ValidateJWTClaims(claims jwt.MapClaims, hostname, nonce, subject string) (bool, error) {
|
||||||
clientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientID)
|
clientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return claims, err
|
return false, err
|
||||||
}
|
}
|
||||||
if claims["aud"] != clientID {
|
if claims["aud"] != clientID {
|
||||||
return claims, errors.New("invalid audience")
|
return false, errors.New("invalid audience")
|
||||||
}
|
}
|
||||||
|
|
||||||
if claims["nonce"] != nonce {
|
if claims["nonce"] != nonce {
|
||||||
return claims, errors.New("invalid nonce")
|
return false, errors.New("invalid nonce")
|
||||||
}
|
}
|
||||||
|
|
||||||
if claims["iss"] != hostname {
|
if claims["iss"] != hostname {
|
||||||
return claims, errors.New("invalid issuer")
|
return false, errors.New("invalid issuer")
|
||||||
}
|
}
|
||||||
|
|
||||||
if claims["sub"] != subject {
|
if claims["sub"] != subject {
|
||||||
return claims, errors.New("invalid subject")
|
return false, errors.New("invalid subject")
|
||||||
}
|
}
|
||||||
|
|
||||||
return claims, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseJWTTokenWithoutNonce common util to parse jwt token without nonce
|
// ValidateJWTClaimsWithoutNonce common util to validate claims without nonce
|
||||||
// used to validate ID token as it is not persisted in store
|
func ValidateJWTTokenWithoutNonce(claims jwt.MapClaims, hostname string) (bool, error) {
|
||||||
func ParseJWTTokenWithoutNonce(token, hostname string) (jwt.MapClaims, error) {
|
|
||||||
jwtType, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyJwtType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
signingMethod := jwt.GetSigningMethod(jwtType)
|
|
||||||
|
|
||||||
var claims jwt.MapClaims
|
|
||||||
|
|
||||||
switch signingMethod {
|
|
||||||
case jwt.SigningMethodHS256, jwt.SigningMethodHS384, jwt.SigningMethodHS512:
|
|
||||||
_, err = jwt.ParseWithClaims(token, &claims, func(token *jwt.Token) (interface{}, error) {
|
|
||||||
jwtSecret, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyJwtSecret)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return []byte(jwtSecret), nil
|
|
||||||
})
|
|
||||||
case jwt.SigningMethodRS256, jwt.SigningMethodRS384, jwt.SigningMethodRS512:
|
|
||||||
_, err = jwt.ParseWithClaims(token, &claims, func(token *jwt.Token) (interface{}, error) {
|
|
||||||
jwtPublicKey, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyJwtPublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
key, err := crypto.ParseRsaPublicKeyFromPemStr(jwtPublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return key, nil
|
|
||||||
})
|
|
||||||
case jwt.SigningMethodES256, jwt.SigningMethodES384, jwt.SigningMethodES512:
|
|
||||||
_, err = jwt.ParseWithClaims(token, &claims, func(token *jwt.Token) (interface{}, error) {
|
|
||||||
jwtPublicKey, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyJwtPublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
key, err := crypto.ParseEcdsaPublicKeyFromPemStr(jwtPublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return key, nil
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
err = errors.New("unsupported signing method")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return claims, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// claim parses exp & iat into float 64 with e^10,
|
|
||||||
// but we expect it to be int64
|
|
||||||
// hence we need to assert interface and convert to int64
|
|
||||||
intExp := int64(claims["exp"].(float64))
|
|
||||||
intIat := int64(claims["iat"].(float64))
|
|
||||||
claims["exp"] = intExp
|
|
||||||
claims["iat"] = intIat
|
|
||||||
clientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientID)
|
clientID, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyClientID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return claims, err
|
return false, err
|
||||||
}
|
}
|
||||||
if claims["aud"] != clientID {
|
if claims["aud"] != clientID {
|
||||||
return claims, errors.New("invalid audience")
|
return false, errors.New("invalid audience")
|
||||||
}
|
}
|
||||||
|
|
||||||
if claims["iss"] != hostname {
|
if claims["iss"] != hostname {
|
||||||
return claims, errors.New("invalid issuer")
|
return false, errors.New("invalid issuer")
|
||||||
}
|
}
|
||||||
|
|
||||||
return claims, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user