fix: auth flow
This commit is contained in:
parent
5399ea8f32
commit
f0f2e0b6c8
|
@ -5,4 +5,6 @@ const (
|
|||
TokenTypeRefreshToken = "refresh_token"
|
||||
// TokenTypeAccessToken is the access_token token type
|
||||
TokenTypeAccessToken = "access_token"
|
||||
// TokenTypeIdentityToken is the identity_token token type
|
||||
TokenTypeIdentityToken = "id_token"
|
||||
)
|
||||
|
|
|
@ -2,7 +2,6 @@ package cookie
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
|
@ -10,8 +9,8 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SetSessionCookie sets the session cookie in the response
|
||||
func SetSessionCookie(gc *gin.Context, sessionID string) {
|
||||
// SetSession sets the session cookie in the response
|
||||
func SetSession(gc *gin.Context, sessionID string) {
|
||||
secure := true
|
||||
httpOnly := true
|
||||
hostname := utils.GetHost(gc)
|
||||
|
@ -26,21 +25,16 @@ func SetSessionCookie(gc *gin.Context, sessionID string) {
|
|||
|
||||
gc.SetSameSite(http.SameSiteNoneMode)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session", sessionID, year, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session.domain", sessionID, year, "/", domain, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session_domain", sessionID, year, "/", domain, secure, httpOnly)
|
||||
|
||||
// Fallback cookie for anomaly getection on browsers that don’t support the sameSite=None attribute.
|
||||
gc.SetSameSite(http.SameSiteDefaultMode)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session_compat", sessionID, year, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session.domain_compat", sessionID, year, "/", domain, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session_domain_compat", sessionID, year, "/", domain, secure, httpOnly)
|
||||
}
|
||||
|
||||
// SetCookie sets the cookie in the response. It sets 4 cookies
|
||||
// 1 COOKIE_NAME.access_token jwt token for the host (temp.abc.com)
|
||||
// 2 COOKIE_NAME.access_token.domain jwt token for the domain (abc.com).
|
||||
// 3 COOKIE_NAME.fingerprint fingerprint hash for the refresh token verification.
|
||||
// 4 COOKIE_NAME.refresh_token refresh token
|
||||
// Note all sites don't allow 2nd type of cookie
|
||||
func SetCookie(gc *gin.Context, accessToken, refreshToken, fingerprintHash string) {
|
||||
// DeleteSession sets session cookies to expire
|
||||
func DeleteSession(gc *gin.Context) {
|
||||
secure := true
|
||||
httpOnly := true
|
||||
hostname := utils.GetHost(gc)
|
||||
|
@ -50,77 +44,33 @@ func SetCookie(gc *gin.Context, accessToken, refreshToken, fingerprintHash strin
|
|||
domain = "." + domain
|
||||
}
|
||||
|
||||
year := 60 * 60 * 24 * 365
|
||||
thirtyMin := 60 * 30
|
||||
|
||||
gc.SetSameSite(http.SameSiteNoneMode)
|
||||
// set cookie for host
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", accessToken, thirtyMin, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session", "", -1, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session_domain", "", -1, "/", domain, secure, httpOnly)
|
||||
|
||||
// in case of subdomain, set cookie for domain
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token.domain", accessToken, thirtyMin, "/", domain, secure, httpOnly)
|
||||
|
||||
// set finger print cookie (this should be accessed via cookie only)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".fingerprint", fingerprintHash, year, "/", host, secure, httpOnly)
|
||||
|
||||
// set refresh token cookie (this should be accessed via cookie only)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".refresh_token", refreshToken, year, "/", host, secure, httpOnly)
|
||||
gc.SetSameSite(http.SameSiteDefaultMode)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session_compat", "", -1, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session.domain_compat", "", -1, "/", domain, secure, httpOnly)
|
||||
}
|
||||
|
||||
// GetAccessTokenCookie to get access token cookie from the request
|
||||
func GetAccessTokenCookie(gc *gin.Context) (string, error) {
|
||||
cookie, err := gc.Request.Cookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + ".access_token")
|
||||
// GetSession gets the session cookie from context
|
||||
func GetSession(gc *gin.Context) (string, error) {
|
||||
var cookie *http.Cookie
|
||||
var err error
|
||||
cookie, err = gc.Request.Cookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + "_session")
|
||||
if err != nil {
|
||||
cookie, err = gc.Request.Cookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + ".access_token.domain")
|
||||
cookie, err = gc.Request.Cookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + "_session_domain")
|
||||
if err != nil {
|
||||
return "", err
|
||||
cookie, err = gc.Request.Cookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + "_session_compat")
|
||||
if err != nil {
|
||||
cookie, err = gc.Request.Cookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + "_session_domain_compat")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cookie.Value, nil
|
||||
}
|
||||
|
||||
// GetRefreshTokenCookie to get refresh token cookie
|
||||
func GetRefreshTokenCookie(gc *gin.Context) (string, error) {
|
||||
cookie, err := gc.Request.Cookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + ".refresh_token")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return cookie.Value, nil
|
||||
}
|
||||
|
||||
// GetFingerPrintCookie to get fingerprint cookie
|
||||
func GetFingerPrintCookie(gc *gin.Context) (string, error) {
|
||||
cookie, err := gc.Request.Cookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + ".fingerprint")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// cookie escapes special characters like $
|
||||
// hence we need to unescape before comparing
|
||||
decodedValue, err := url.QueryUnescape(cookie.Value)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return decodedValue, nil
|
||||
}
|
||||
|
||||
// DeleteCookie sets response cookies to expire
|
||||
func DeleteCookie(gc *gin.Context) {
|
||||
secure := true
|
||||
httpOnly := true
|
||||
hostname := utils.GetHost(gc)
|
||||
host, _ := utils.GetHostParts(hostname)
|
||||
domain := utils.GetDomainName(hostname)
|
||||
if domain != "localhost" {
|
||||
domain = "." + domain
|
||||
}
|
||||
|
||||
gc.SetSameSite(http.SameSiteNoneMode)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", "", -1, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token.domain", "", -1, "/", domain, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".fingerprint", "", -1, "/", host, secure, httpOnly)
|
||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".refresh_token", "", -1, "/", host, secure, httpOnly)
|
||||
}
|
||||
|
|
|
@ -3,71 +3,40 @@ package crypto
|
|||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
)
|
||||
|
||||
// EncryptAES encrypts data using AES algorithm
|
||||
func EncryptAES(text []byte) ([]byte, error) {
|
||||
var bytes = []byte{35, 46, 57, 24, 85, 35, 24, 74, 87, 35, 88, 98, 66, 32, 14, 05}
|
||||
|
||||
// EncryptAES method is to encrypt or hide any classified text
|
||||
func EncryptAES(text string) (string, error) {
|
||||
key := []byte(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey))
|
||||
c, err := aes.NewCipher(key)
|
||||
var res []byte
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return res, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
// gcm or Galois/Counter Mode, is a mode of operation
|
||||
// for symmetric key cryptographic block ciphers
|
||||
// - https://en.wikipedia.org/wiki/Galois/Counter_Mode
|
||||
gcm, err := cipher.NewGCM(c)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// creates a new byte array the size of the nonce
|
||||
// which must be passed to Seal
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
// populates our nonce with a cryptographically secure
|
||||
// random sequence
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// here we encrypt our text using the Seal function
|
||||
// Seal encrypts and authenticates plaintext, authenticates the
|
||||
// additional data and appends the result to dst, returning the updated
|
||||
// slice. The nonce must be NonceSize() bytes long and unique for all
|
||||
// time, for a given key.
|
||||
return gcm.Seal(nonce, nonce, text, nil), nil
|
||||
plainText := []byte(text)
|
||||
cfb := cipher.NewCFBEncrypter(block, bytes)
|
||||
cipherText := make([]byte, len(plainText))
|
||||
cfb.XORKeyStream(cipherText, plainText)
|
||||
return EncryptB64(string(cipherText)), nil
|
||||
}
|
||||
|
||||
// DecryptAES decrypts data using AES algorithm
|
||||
func DecryptAES(ciphertext []byte) ([]byte, error) {
|
||||
// DecryptAES method is to extract back the encrypted text
|
||||
func DecryptAES(text string) (string, error) {
|
||||
key := []byte(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey))
|
||||
c, err := aes.NewCipher(key)
|
||||
var res []byte
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return res, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(c)
|
||||
cipherText, err := DecryptB64(text)
|
||||
if err != nil {
|
||||
return res, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
nonceSize := gcm.NonceSize()
|
||||
if len(ciphertext) < nonceSize {
|
||||
return res, err
|
||||
}
|
||||
|
||||
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
||||
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
cfb := cipher.NewCFBDecrypter(block, bytes)
|
||||
plainText := make([]byte, len(cipherText))
|
||||
cfb.XORKeyStream(plainText, []byte(cipherText))
|
||||
return string(plainText), nil
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ func EncryptEnvData(data envstore.Store) (string, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
encryptedConfig, err := EncryptAES(configData)
|
||||
encryptedConfig, err := EncryptAES(string(configData))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ type VerificationRequest struct {
|
|||
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
||||
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"`
|
||||
Nonce string `gorm:"type:char(36)" json:"nonce" bson:"nonce"`
|
||||
}
|
||||
|
||||
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
|
||||
|
|
|
@ -56,7 +56,10 @@ func NewProvider() (*provider, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
sqlDB.AutoMigrate(&models.User{}, &models.VerificationRequest{}, &models.Session{}, &models.Env{})
|
||||
err = sqlDB.AutoMigrate(&models.User{}, &models.VerificationRequest{}, &models.Session{}, &models.Env{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &provider{
|
||||
db: sqlDB,
|
||||
}, nil
|
||||
|
|
|
@ -31,6 +31,10 @@ func addEmailTemplate(a string, b map[string]interface{}, templateName string) s
|
|||
|
||||
// SendMail function to send mail
|
||||
func SendMail(to []string, Subject, bodyMessage string) error {
|
||||
// dont trigger email sending in case of test
|
||||
if envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEnv) == "test" {
|
||||
return nil
|
||||
}
|
||||
m := gomail.NewMessage()
|
||||
m.SetHeader("From", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeySenderEmail))
|
||||
m.SetHeader("To", to...)
|
||||
|
|
18
server/env/persist_env.go
vendored
18
server/env/persist_env.go
vendored
|
@ -33,17 +33,13 @@ func GetEnvData() (envstore.Store, error) {
|
|||
}
|
||||
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
||||
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
|
||||
|
||||
decryptedConfigs, err := crypto.DecryptAES(env.EnvData)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
decryptedConfigs, err := crypto.DecryptAES([]byte(b64DecryptedConfig))
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(decryptedConfigs, &result)
|
||||
err = json.Unmarshal([]byte(decryptedConfigs), &result)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
@ -85,12 +81,8 @@ func PersistEnv() error {
|
|||
}
|
||||
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
||||
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
decryptedConfigs, err := crypto.DecryptAES([]byte(b64DecryptedConfig))
|
||||
decryptedConfigs, err := crypto.DecryptAES(env.EnvData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -98,7 +90,7 @@ func PersistEnv() error {
|
|||
// temp store variable
|
||||
var storeData envstore.Store
|
||||
|
||||
err = json.Unmarshal(decryptedConfigs, &storeData)
|
||||
err = json.Unmarshal([]byte(decryptedConfigs), &storeData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -44,10 +44,12 @@ type DirectiveRoot struct {
|
|||
|
||||
type ComplexityRoot struct {
|
||||
AuthResponse struct {
|
||||
AccessToken func(childComplexity int) int
|
||||
ExpiresAt func(childComplexity int) int
|
||||
Message func(childComplexity int) int
|
||||
User func(childComplexity int) int
|
||||
AccessToken func(childComplexity int) int
|
||||
ExpiresIn func(childComplexity int) int
|
||||
IDToken func(childComplexity int) int
|
||||
Message func(childComplexity int) int
|
||||
RefreshToken func(childComplexity int) int
|
||||
User func(childComplexity int) int
|
||||
}
|
||||
|
||||
Env struct {
|
||||
|
@ -134,7 +136,6 @@ type ComplexityRoot struct {
|
|||
Query struct {
|
||||
AdminSession func(childComplexity int) int
|
||||
Env func(childComplexity int) int
|
||||
IsValidJwt func(childComplexity int, params *model.IsValidJWTQueryInput) int
|
||||
Meta func(childComplexity int) int
|
||||
Profile func(childComplexity int) int
|
||||
Session func(childComplexity int, params *model.SessionQueryInput) int
|
||||
|
@ -171,11 +172,6 @@ type ComplexityRoot struct {
|
|||
Users func(childComplexity int) int
|
||||
}
|
||||
|
||||
ValidJWTResponse struct {
|
||||
Message func(childComplexity int) int
|
||||
Valid func(childComplexity int) int
|
||||
}
|
||||
|
||||
VerificationRequest struct {
|
||||
CreatedAt func(childComplexity int) int
|
||||
Email func(childComplexity int) int
|
||||
|
@ -212,7 +208,6 @@ type MutationResolver interface {
|
|||
type QueryResolver interface {
|
||||
Meta(ctx context.Context) (*model.Meta, error)
|
||||
Session(ctx context.Context, params *model.SessionQueryInput) (*model.AuthResponse, error)
|
||||
IsValidJwt(ctx context.Context, params *model.IsValidJWTQueryInput) (*model.ValidJWTResponse, error)
|
||||
Profile(ctx context.Context) (*model.User, error)
|
||||
Users(ctx context.Context, params *model.PaginatedInput) (*model.Users, error)
|
||||
VerificationRequests(ctx context.Context, params *model.PaginatedInput) (*model.VerificationRequests, error)
|
||||
|
@ -242,12 +237,19 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.AuthResponse.AccessToken(childComplexity), true
|
||||
|
||||
case "AuthResponse.expires_at":
|
||||
if e.complexity.AuthResponse.ExpiresAt == nil {
|
||||
case "AuthResponse.expires_in":
|
||||
if e.complexity.AuthResponse.ExpiresIn == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.AuthResponse.ExpiresAt(childComplexity), true
|
||||
return e.complexity.AuthResponse.ExpiresIn(childComplexity), true
|
||||
|
||||
case "AuthResponse.id_token":
|
||||
if e.complexity.AuthResponse.IDToken == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.AuthResponse.IDToken(childComplexity), true
|
||||
|
||||
case "AuthResponse.message":
|
||||
if e.complexity.AuthResponse.Message == nil {
|
||||
|
@ -256,6 +258,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.AuthResponse.Message(childComplexity), true
|
||||
|
||||
case "AuthResponse.refresh_token":
|
||||
if e.complexity.AuthResponse.RefreshToken == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.AuthResponse.RefreshToken(childComplexity), true
|
||||
|
||||
case "AuthResponse.user":
|
||||
if e.complexity.AuthResponse.User == nil {
|
||||
break
|
||||
|
@ -804,18 +813,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.Query.Env(childComplexity), true
|
||||
|
||||
case "Query.is_valid_jwt":
|
||||
if e.complexity.Query.IsValidJwt == nil {
|
||||
break
|
||||
}
|
||||
|
||||
args, err := ec.field_Query_is_valid_jwt_args(context.TODO(), rawArgs)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
|
||||
return e.complexity.Query.IsValidJwt(childComplexity, args["params"].(*model.IsValidJWTQueryInput)), true
|
||||
|
||||
case "Query.meta":
|
||||
if e.complexity.Query.Meta == nil {
|
||||
break
|
||||
|
@ -1006,20 +1003,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
|||
|
||||
return e.complexity.Users.Users(childComplexity), true
|
||||
|
||||
case "ValidJWTResponse.message":
|
||||
if e.complexity.ValidJWTResponse.Message == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.ValidJWTResponse.Message(childComplexity), true
|
||||
|
||||
case "ValidJWTResponse.valid":
|
||||
if e.complexity.ValidJWTResponse.Valid == nil {
|
||||
break
|
||||
}
|
||||
|
||||
return e.complexity.ValidJWTResponse.Valid(childComplexity), true
|
||||
|
||||
case "VerificationRequest.created_at":
|
||||
if e.complexity.VerificationRequest.CreatedAt == nil {
|
||||
break
|
||||
|
@ -1221,7 +1204,9 @@ type Error {
|
|||
type AuthResponse {
|
||||
message: String!
|
||||
access_token: String
|
||||
expires_at: Int64
|
||||
id_token: String
|
||||
refresh_token: String
|
||||
expires_in: Int64
|
||||
user: User
|
||||
}
|
||||
|
||||
|
@ -1229,11 +1214,6 @@ type Response {
|
|||
message: String!
|
||||
}
|
||||
|
||||
type ValidJWTResponse {
|
||||
valid: Boolean!
|
||||
message: String!
|
||||
}
|
||||
|
||||
type Env {
|
||||
ADMIN_SECRET: String
|
||||
DATABASE_NAME: String!
|
||||
|
@ -1337,6 +1317,7 @@ input LoginInput {
|
|||
email: String!
|
||||
password: String!
|
||||
roles: [String!]
|
||||
scope: [String!]
|
||||
}
|
||||
|
||||
input VerifyEmailInput {
|
||||
|
@ -1395,15 +1376,12 @@ input DeleteUserInput {
|
|||
input MagicLinkLoginInput {
|
||||
email: String!
|
||||
roles: [String!]
|
||||
scope: [String!]
|
||||
}
|
||||
|
||||
input SessionQueryInput {
|
||||
roles: [String!]
|
||||
}
|
||||
|
||||
input IsValidJWTQueryInput {
|
||||
jwt: String
|
||||
roles: [String!]
|
||||
scope: [String!]
|
||||
}
|
||||
|
||||
input PaginationInput {
|
||||
|
@ -1437,7 +1415,6 @@ type Mutation {
|
|||
type Query {
|
||||
meta: Meta!
|
||||
session(params: SessionQueryInput): AuthResponse!
|
||||
is_valid_jwt(params: IsValidJWTQueryInput): ValidJWTResponse!
|
||||
profile: User!
|
||||
# admin only apis
|
||||
_users(params: PaginatedInput): Users!
|
||||
|
@ -1693,21 +1670,6 @@ func (ec *executionContext) field_Query__verification_requests_args(ctx context.
|
|||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Query_is_valid_jwt_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
var arg0 *model.IsValidJWTQueryInput
|
||||
if tmp, ok := rawArgs["params"]; ok {
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
|
||||
arg0, err = ec.unmarshalOIsValidJWTQueryInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐIsValidJWTQueryInput(ctx, tmp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
args["params"] = arg0
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) field_Query_session_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||
var err error
|
||||
args := map[string]interface{}{}
|
||||
|
@ -1828,7 +1790,7 @@ func (ec *executionContext) _AuthResponse_access_token(ctx context.Context, fiel
|
|||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _AuthResponse_expires_at(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
|
||||
func (ec *executionContext) _AuthResponse_id_token(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
|
@ -1846,7 +1808,71 @@ func (ec *executionContext) _AuthResponse_expires_at(ctx context.Context, field
|
|||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.ExpiresAt, nil
|
||||
return obj.IDToken, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*string)
|
||||
fc.Result = res
|
||||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _AuthResponse_refresh_token(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "AuthResponse",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.RefreshToken, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*string)
|
||||
fc.Result = res
|
||||
return ec.marshalOString2ᚖstring(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _AuthResponse_expires_in(ctx context.Context, field graphql.CollectedField, obj *model.AuthResponse) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "AuthResponse",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.ExpiresIn, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
|
@ -4274,48 +4300,6 @@ func (ec *executionContext) _Query_session(ctx context.Context, field graphql.Co
|
|||
return ec.marshalNAuthResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAuthResponse(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Query_is_valid_jwt(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "Query",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: true,
|
||||
IsResolver: true,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
rawArgs := field.ArgumentMap(ec.Variables)
|
||||
args, err := ec.field_Query_is_valid_jwt_args(ctx, rawArgs)
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
fc.Args = args
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return ec.resolvers.Query().IsValidJwt(rctx, args["params"].(*model.IsValidJWTQueryInput))
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(*model.ValidJWTResponse)
|
||||
fc.Result = res
|
||||
return ec.marshalNValidJWTResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐValidJWTResponse(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _Query_profile(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -5240,76 +5224,6 @@ func (ec *executionContext) _Users_users(ctx context.Context, field graphql.Coll
|
|||
return ec.marshalNUser2ᚕᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐUserᚄ(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _ValidJWTResponse_valid(ctx context.Context, field graphql.CollectedField, obj *model.ValidJWTResponse) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "ValidJWTResponse",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Valid, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(bool)
|
||||
fc.Result = res
|
||||
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _ValidJWTResponse_message(ctx context.Context, field graphql.CollectedField, obj *model.ValidJWTResponse) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
ret = graphql.Null
|
||||
}
|
||||
}()
|
||||
fc := &graphql.FieldContext{
|
||||
Object: "ValidJWTResponse",
|
||||
Field: field,
|
||||
Args: nil,
|
||||
IsMethod: false,
|
||||
IsResolver: false,
|
||||
}
|
||||
|
||||
ctx = graphql.WithFieldContext(ctx, fc)
|
||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||
ctx = rctx // use context from middleware stack in children
|
||||
return obj.Message, nil
|
||||
})
|
||||
if err != nil {
|
||||
ec.Error(ctx, err)
|
||||
return graphql.Null
|
||||
}
|
||||
if resTmp == nil {
|
||||
if !graphql.HasFieldError(ctx, fc) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
res := resTmp.(string)
|
||||
fc.Result = res
|
||||
return ec.marshalNString2string(ctx, field.Selections, res)
|
||||
}
|
||||
|
||||
func (ec *executionContext) _VerificationRequest_id(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequest) (ret graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
@ -6821,37 +6735,6 @@ func (ec *executionContext) unmarshalInputForgotPasswordInput(ctx context.Contex
|
|||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputIsValidJWTQueryInput(ctx context.Context, obj interface{}) (model.IsValidJWTQueryInput, error) {
|
||||
var it model.IsValidJWTQueryInput
|
||||
asMap := map[string]interface{}{}
|
||||
for k, v := range obj.(map[string]interface{}) {
|
||||
asMap[k] = v
|
||||
}
|
||||
|
||||
for k, v := range asMap {
|
||||
switch k {
|
||||
case "jwt":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("jwt"))
|
||||
it.Jwt, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "roles":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roles"))
|
||||
it.Roles, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return it, nil
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj interface{}) (model.LoginInput, error) {
|
||||
var it model.LoginInput
|
||||
asMap := map[string]interface{}{}
|
||||
|
@ -6885,6 +6768,14 @@ func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj in
|
|||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "scope":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("scope"))
|
||||
it.Scope, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6916,6 +6807,14 @@ func (ec *executionContext) unmarshalInputMagicLinkLoginInput(ctx context.Contex
|
|||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "scope":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("scope"))
|
||||
it.Scope, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7063,6 +6962,14 @@ func (ec *executionContext) unmarshalInputSessionQueryInput(ctx context.Context,
|
|||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
case "scope":
|
||||
var err error
|
||||
|
||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("scope"))
|
||||
it.Scope, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v)
|
||||
if err != nil {
|
||||
return it, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7730,8 +7637,12 @@ func (ec *executionContext) _AuthResponse(ctx context.Context, sel ast.Selection
|
|||
}
|
||||
case "access_token":
|
||||
out.Values[i] = ec._AuthResponse_access_token(ctx, field, obj)
|
||||
case "expires_at":
|
||||
out.Values[i] = ec._AuthResponse_expires_at(ctx, field, obj)
|
||||
case "id_token":
|
||||
out.Values[i] = ec._AuthResponse_id_token(ctx, field, obj)
|
||||
case "refresh_token":
|
||||
out.Values[i] = ec._AuthResponse_refresh_token(ctx, field, obj)
|
||||
case "expires_in":
|
||||
out.Values[i] = ec._AuthResponse_expires_in(ctx, field, obj)
|
||||
case "user":
|
||||
out.Values[i] = ec._AuthResponse_user(ctx, field, obj)
|
||||
default:
|
||||
|
@ -8136,20 +8047,6 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
|
|||
}
|
||||
return res
|
||||
})
|
||||
case "is_valid_jwt":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ec.Error(ctx, ec.Recover(ctx, r))
|
||||
}
|
||||
}()
|
||||
res = ec._Query_is_valid_jwt(ctx, field)
|
||||
if res == graphql.Null {
|
||||
atomic.AddUint32(&invalids, 1)
|
||||
}
|
||||
return res
|
||||
})
|
||||
case "profile":
|
||||
field := field
|
||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||
|
@ -8365,38 +8262,6 @@ func (ec *executionContext) _Users(ctx context.Context, sel ast.SelectionSet, ob
|
|||
return out
|
||||
}
|
||||
|
||||
var validJWTResponseImplementors = []string{"ValidJWTResponse"}
|
||||
|
||||
func (ec *executionContext) _ValidJWTResponse(ctx context.Context, sel ast.SelectionSet, obj *model.ValidJWTResponse) graphql.Marshaler {
|
||||
fields := graphql.CollectFields(ec.OperationContext, sel, validJWTResponseImplementors)
|
||||
|
||||
out := graphql.NewFieldSet(fields)
|
||||
var invalids uint32
|
||||
for i, field := range fields {
|
||||
switch field.Name {
|
||||
case "__typename":
|
||||
out.Values[i] = graphql.MarshalString("ValidJWTResponse")
|
||||
case "valid":
|
||||
out.Values[i] = ec._ValidJWTResponse_valid(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
case "message":
|
||||
out.Values[i] = ec._ValidJWTResponse_message(ctx, field, obj)
|
||||
if out.Values[i] == graphql.Null {
|
||||
invalids++
|
||||
}
|
||||
default:
|
||||
panic("unknown field " + strconv.Quote(field.Name))
|
||||
}
|
||||
}
|
||||
out.Dispatch()
|
||||
if invalids > 0 {
|
||||
return graphql.Null
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
var verificationRequestImplementors = []string{"VerificationRequest"}
|
||||
|
||||
func (ec *executionContext) _VerificationRequest(ctx context.Context, sel ast.SelectionSet, obj *model.VerificationRequest) graphql.Marshaler {
|
||||
|
@ -9012,20 +8877,6 @@ func (ec *executionContext) marshalNUsers2ᚖgithubᚗcomᚋauthorizerdevᚋauth
|
|||
return ec._Users(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNValidJWTResponse2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐValidJWTResponse(ctx context.Context, sel ast.SelectionSet, v model.ValidJWTResponse) graphql.Marshaler {
|
||||
return ec._ValidJWTResponse(ctx, sel, &v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNValidJWTResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐValidJWTResponse(ctx context.Context, sel ast.SelectionSet, v *model.ValidJWTResponse) graphql.Marshaler {
|
||||
if v == nil {
|
||||
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
|
||||
ec.Errorf(ctx, "must not be null")
|
||||
}
|
||||
return graphql.Null
|
||||
}
|
||||
return ec._ValidJWTResponse(ctx, sel, v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) marshalNVerificationRequest2ᚕᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐVerificationRequestᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.VerificationRequest) graphql.Marshaler {
|
||||
ret := make(graphql.Array, len(v))
|
||||
var wg sync.WaitGroup
|
||||
|
@ -9395,14 +9246,6 @@ func (ec *executionContext) marshalOInt642ᚖint64(ctx context.Context, sel ast.
|
|||
return graphql.MarshalInt64(*v)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalOIsValidJWTQueryInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐIsValidJWTQueryInput(ctx context.Context, v interface{}) (*model.IsValidJWTQueryInput, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
res, err := ec.unmarshalInputIsValidJWTQueryInput(ctx, v)
|
||||
return &res, graphql.ErrorOnPath(ctx, err)
|
||||
}
|
||||
|
||||
func (ec *executionContext) unmarshalOPaginatedInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐPaginatedInput(ctx context.Context, v interface{}) (*model.PaginatedInput, error) {
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
|
|
|
@ -11,10 +11,12 @@ type AdminSignupInput struct {
|
|||
}
|
||||
|
||||
type AuthResponse struct {
|
||||
Message string `json:"message"`
|
||||
AccessToken *string `json:"access_token"`
|
||||
ExpiresAt *int64 `json:"expires_at"`
|
||||
User *User `json:"user"`
|
||||
Message string `json:"message"`
|
||||
AccessToken *string `json:"access_token"`
|
||||
IDToken *string `json:"id_token"`
|
||||
RefreshToken *string `json:"refresh_token"`
|
||||
ExpiresIn *int64 `json:"expires_in"`
|
||||
User *User `json:"user"`
|
||||
}
|
||||
|
||||
type DeleteUserInput struct {
|
||||
|
@ -70,20 +72,17 @@ type ForgotPasswordInput struct {
|
|||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type IsValidJWTQueryInput struct {
|
||||
Jwt *string `json:"jwt"`
|
||||
Roles []string `json:"roles"`
|
||||
}
|
||||
|
||||
type LoginInput struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Roles []string `json:"roles"`
|
||||
Scope []string `json:"scope"`
|
||||
}
|
||||
|
||||
type MagicLinkLoginInput struct {
|
||||
Email string `json:"email"`
|
||||
Roles []string `json:"roles"`
|
||||
Scope []string `json:"scope"`
|
||||
}
|
||||
|
||||
type Meta struct {
|
||||
|
@ -130,6 +129,7 @@ type Response struct {
|
|||
|
||||
type SessionQueryInput struct {
|
||||
Roles []string `json:"roles"`
|
||||
Scope []string `json:"scope"`
|
||||
}
|
||||
|
||||
type SignUpInput struct {
|
||||
|
@ -238,11 +238,6 @@ type Users struct {
|
|||
Users []*User `json:"users"`
|
||||
}
|
||||
|
||||
type ValidJWTResponse struct {
|
||||
Valid bool `json:"valid"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type VerificationRequest struct {
|
||||
ID string `json:"id"`
|
||||
Identifier *string `json:"identifier"`
|
||||
|
|
|
@ -72,7 +72,9 @@ type Error {
|
|||
type AuthResponse {
|
||||
message: String!
|
||||
access_token: String
|
||||
expires_at: Int64
|
||||
id_token: String
|
||||
refresh_token: String
|
||||
expires_in: Int64
|
||||
user: User
|
||||
}
|
||||
|
||||
|
@ -80,11 +82,6 @@ type Response {
|
|||
message: String!
|
||||
}
|
||||
|
||||
type ValidJWTResponse {
|
||||
valid: Boolean!
|
||||
message: String!
|
||||
}
|
||||
|
||||
type Env {
|
||||
ADMIN_SECRET: String
|
||||
DATABASE_NAME: String!
|
||||
|
@ -188,6 +185,7 @@ input LoginInput {
|
|||
email: String!
|
||||
password: String!
|
||||
roles: [String!]
|
||||
scope: [String!]
|
||||
}
|
||||
|
||||
input VerifyEmailInput {
|
||||
|
@ -246,15 +244,12 @@ input DeleteUserInput {
|
|||
input MagicLinkLoginInput {
|
||||
email: String!
|
||||
roles: [String!]
|
||||
scope: [String!]
|
||||
}
|
||||
|
||||
input SessionQueryInput {
|
||||
roles: [String!]
|
||||
}
|
||||
|
||||
input IsValidJWTQueryInput {
|
||||
jwt: String
|
||||
roles: [String!]
|
||||
scope: [String!]
|
||||
}
|
||||
|
||||
input PaginationInput {
|
||||
|
@ -288,7 +283,6 @@ type Mutation {
|
|||
type Query {
|
||||
meta: Meta!
|
||||
session(params: SessionQueryInput): AuthResponse!
|
||||
is_valid_jwt(params: IsValidJWTQueryInput): ValidJWTResponse!
|
||||
profile: User!
|
||||
# admin only apis
|
||||
_users(params: PaginatedInput): Users!
|
||||
|
|
|
@ -79,10 +79,6 @@ func (r *queryResolver) Session(ctx context.Context, params *model.SessionQueryI
|
|||
return resolvers.SessionResolver(ctx, params)
|
||||
}
|
||||
|
||||
func (r *queryResolver) IsValidJwt(ctx context.Context, params *model.IsValidJWTQueryInput) (*model.ValidJWTResponse, error) {
|
||||
return resolvers.IsValidJwtResolver(ctx, params)
|
||||
}
|
||||
|
||||
func (r *queryResolver) Profile(ctx context.Context) (*model.User, error) {
|
||||
return resolvers.ProfileResolver(ctx)
|
||||
}
|
||||
|
|
|
@ -144,11 +144,13 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
authToken, _ := token.CreateAuthToken(user, inputRoles)
|
||||
sessionstore.SetUserSession(user.ID, authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
cookie.SetCookie(c, authToken.AccessToken.Token, authToken.RefreshToken.Token, authToken.FingerPrintHash)
|
||||
utils.SaveSessionInDB(user.ID, c)
|
||||
// TODO use query param
|
||||
scope := []string{"openid", "email", "profile"}
|
||||
authToken, _ := token.CreateAuthToken(c, user, inputRoles, scope)
|
||||
|
||||
sessionstore.SetState(authToken.FingerPrint, user.ID)
|
||||
cookie.SetSession(c, authToken.FingerPrintHash)
|
||||
go utils.SaveSessionInDB(c, user.ID)
|
||||
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +229,7 @@ func processGithubUserInfo(code string) (models.User, error) {
|
|||
GivenName: &firstName,
|
||||
FamilyName: &lastName,
|
||||
Picture: &picture,
|
||||
Email: userRawData["email"],
|
||||
Email: userRawData["sub"],
|
||||
}
|
||||
|
||||
return user, nil
|
||||
|
@ -260,7 +262,7 @@ func processFacebookUserInfo(code string) (models.User, error) {
|
|||
userRawData := make(map[string]interface{})
|
||||
json.Unmarshal(body, &userRawData)
|
||||
|
||||
email := fmt.Sprintf("%v", userRawData["email"])
|
||||
email := fmt.Sprintf("%v", userRawData["sub"])
|
||||
|
||||
picObject := userRawData["picture"].(map[string]interface{})["data"]
|
||||
picDataObject := picObject.(map[string]interface{})
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// VerifyEmailHandler handles the verify email route.
|
||||
|
@ -18,7 +19,7 @@ import (
|
|||
func VerifyEmailHandler() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
errorRes := gin.H{
|
||||
"message": "invalid token",
|
||||
"error": "invalid token",
|
||||
}
|
||||
tokenInQuery := c.Query("token")
|
||||
if tokenInQuery == "" {
|
||||
|
@ -33,13 +34,21 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
|||
}
|
||||
|
||||
// verify if token exists in db
|
||||
claim, err := token.ParseJWTToken(tokenInQuery)
|
||||
hostname := utils.GetHost(c)
|
||||
encryptedNonce, err := utils.EncryptNonce(verificationRequest.Nonce)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
claim, err := token.ParseJWTToken(tokenInQuery, hostname, encryptedNonce, verificationRequest.Email)
|
||||
if err != nil {
|
||||
c.JSON(400, errorRes)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := db.Provider.GetUserByEmail(claim["email"].(string))
|
||||
user, err := db.Provider.GetUserByEmail(claim["sub"].(string))
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{
|
||||
"message": err.Error(),
|
||||
|
@ -57,16 +66,19 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
|||
db.Provider.DeleteVerificationRequest(verificationRequest)
|
||||
|
||||
roles := strings.Split(user.Roles, ",")
|
||||
authToken, err := token.CreateAuthToken(user, roles)
|
||||
scope := []string{"openid", "email", "profile"}
|
||||
nonce := uuid.New().String()
|
||||
_, authToken, err := token.CreateSessionToken(user, nonce, roles, scope)
|
||||
if err != nil {
|
||||
c.JSON(400, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
sessionstore.SetUserSession(user.ID, authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
cookie.SetCookie(c, authToken.AccessToken.Token, authToken.RefreshToken.Token, authToken.FingerPrintHash)
|
||||
utils.SaveSessionInDB(user.ID, c)
|
||||
sessionstore.SetState(authToken, nonce+"@"+user.ID)
|
||||
cookie.SetSession(c, authToken)
|
||||
|
||||
go utils.SaveSessionInDB(c, user.ID)
|
||||
|
||||
c.Redirect(http.StatusTemporaryRedirect, claim["redirect_url"].(string))
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ func DeleteUserResolver(ctx context.Context, params model.DeleteUserInput) (*mod
|
|||
return res, err
|
||||
}
|
||||
|
||||
sessionstore.DeleteAllUserSession(fmt.Sprintf("%x", user.ID))
|
||||
go sessionstore.DeleteAllUserSession(fmt.Sprintf("%x", user.ID))
|
||||
|
||||
err = db.Provider.DeleteUser(user)
|
||||
if err != nil {
|
||||
|
|
|
@ -39,7 +39,11 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
|
|||
}
|
||||
|
||||
hostname := utils.GetHost(gc)
|
||||
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname)
|
||||
nonce, nonceHash, err := utils.GenerateNonce()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash)
|
||||
if err != nil {
|
||||
log.Println(`error generating token`, err)
|
||||
}
|
||||
|
@ -48,12 +52,11 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
|
|||
Identifier: constants.VerificationTypeForgotPassword,
|
||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||
Email: params.Email,
|
||||
Nonce: nonce,
|
||||
})
|
||||
|
||||
// exec it as go routin so that we can reduce the api latency
|
||||
go func() {
|
||||
email.SendForgotPasswordMail(params.Email, verificationToken, hostname)
|
||||
}()
|
||||
go email.SendForgotPasswordMail(params.Email, verificationToken, hostname)
|
||||
|
||||
res = &model.Response{
|
||||
Message: `Please check your inbox! We have sent a password reset link.`,
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
package resolvers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
tokenHelper "github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
)
|
||||
|
||||
// IsValidJwtResolver resolver to return if given jwt is valid
|
||||
func IsValidJwtResolver(ctx context.Context, params *model.IsValidJWTQueryInput) (*model.ValidJWTResponse, error) {
|
||||
gc, err := utils.GinContextFromContext(ctx)
|
||||
token, err := token.GetAccessToken(gc)
|
||||
|
||||
if token == "" || err != nil {
|
||||
if params != nil && *params.Jwt != "" {
|
||||
token = *params.Jwt
|
||||
} else {
|
||||
return nil, errors.New("no jwt provided via cookie / header / params")
|
||||
}
|
||||
}
|
||||
|
||||
claims, err := tokenHelper.ParseJWTToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
claimRoleInterface := claims[envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtRoleClaim)].([]interface{})
|
||||
claimRoles := []string{}
|
||||
for _, v := range claimRoleInterface {
|
||||
claimRoles = append(claimRoles, v.(string))
|
||||
}
|
||||
|
||||
if params != nil && params.Roles != nil && len(params.Roles) > 0 {
|
||||
for _, v := range params.Roles {
|
||||
if !utils.StringSliceContains(claimRoles, v) {
|
||||
return nil, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &model.ValidJWTResponse{
|
||||
Valid: true,
|
||||
Message: "Valid JWT",
|
||||
}, nil
|
||||
}
|
|
@ -59,20 +59,36 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
|
|||
roles = params.Roles
|
||||
}
|
||||
|
||||
authToken, err := token.CreateAuthToken(user, roles)
|
||||
scope := []string{"openid", "email", "profile"}
|
||||
if params.Scope != nil && len(scope) > 0 {
|
||||
scope = params.Scope
|
||||
}
|
||||
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
sessionstore.SetUserSession(user.ID, authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
cookie.SetCookie(gc, authToken.AccessToken.Token, authToken.RefreshToken.Token, authToken.FingerPrintHash)
|
||||
utils.SaveSessionInDB(user.ID, gc)
|
||||
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
|
||||
expiresIn := int64(1800)
|
||||
res = &model.AuthResponse{
|
||||
Message: `Logged in successfully`,
|
||||
AccessToken: &authToken.AccessToken.Token,
|
||||
ExpiresAt: &authToken.AccessToken.ExpiresAt,
|
||||
IDToken: &authToken.IDToken.Token,
|
||||
ExpiresIn: &expiresIn,
|
||||
User: user.AsAPIUser(),
|
||||
}
|
||||
|
||||
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
res.RefreshToken = &authToken.RefreshToken.Token
|
||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||
}
|
||||
|
||||
go utils.SaveSessionInDB(gc, user.ID)
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/crypto"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
)
|
||||
|
||||
|
@ -19,34 +18,21 @@ func LogoutResolver(ctx context.Context) (*model.Response, error) {
|
|||
return res, err
|
||||
}
|
||||
|
||||
// get refresh token
|
||||
refreshToken, err := token.GetRefreshToken(gc)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// get fingerprint hash
|
||||
fingerprintHash, err := token.GetFingerPrint(gc)
|
||||
fingerprintHash, err := cookie.GetSession(gc)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
decryptedFingerPrint, err := crypto.DecryptAES([]byte(fingerprintHash))
|
||||
decryptedFingerPrint, err := crypto.DecryptAES(fingerprintHash)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
fingerPrint := string(decryptedFingerPrint)
|
||||
|
||||
// verify refresh token and fingerprint
|
||||
claims, err := token.ParseJWTToken(refreshToken)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
userID := claims["id"].(string)
|
||||
sessionstore.DeleteUserSession(userID, fingerPrint)
|
||||
cookie.DeleteCookie(gc)
|
||||
sessionstore.RemoveState(fingerPrint)
|
||||
cookie.DeleteSession(gc)
|
||||
|
||||
res = &model.Response{
|
||||
Message: "Logged out successfully",
|
||||
|
|
|
@ -109,8 +109,12 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
|
|||
hostname := utils.GetHost(gc)
|
||||
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
||||
// insert verification request
|
||||
nonce, nonceHash, err := utils.GenerateNonce()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
verificationType := constants.VerificationTypeMagicLinkLogin
|
||||
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname)
|
||||
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname, nonceHash)
|
||||
if err != nil {
|
||||
log.Println(`error generating token`, err)
|
||||
}
|
||||
|
@ -119,12 +123,11 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
|
|||
Identifier: verificationType,
|
||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||
Email: params.Email,
|
||||
Nonce: nonce,
|
||||
})
|
||||
|
||||
// exec it as go routin so that we can reduce the api latency
|
||||
go func() {
|
||||
email.SendVerificationMail(params.Email, verificationToken, hostname)
|
||||
}()
|
||||
go email.SendVerificationMail(params.Email, verificationToken, hostname)
|
||||
}
|
||||
|
||||
res = &model.Response{
|
||||
|
|
|
@ -2,7 +2,6 @@ package resolvers
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
|
@ -18,13 +17,17 @@ func ProfileResolver(ctx context.Context) (*model.User, error) {
|
|||
return res, err
|
||||
}
|
||||
|
||||
claims, err := token.ValidateAccessToken(gc)
|
||||
accessToken, err := token.GetAccessToken(gc)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
userID := fmt.Sprintf("%v", claims["id"])
|
||||
claims, err := token.ValidateAccessToken(gc, accessToken)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
userID := claims["sub"].(string)
|
||||
user, err := db.Provider.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return res, err
|
||||
|
|
|
@ -44,7 +44,11 @@ func ResendVerifyEmailResolver(ctx context.Context, params model.ResendVerifyEma
|
|||
}
|
||||
|
||||
hostname := utils.GetHost(gc)
|
||||
verificationToken, err := token.CreateVerificationToken(params.Email, params.Identifier, hostname)
|
||||
nonce, nonceHash, err := utils.GenerateNonce()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
verificationToken, err := token.CreateVerificationToken(params.Email, params.Identifier, hostname, nonceHash)
|
||||
if err != nil {
|
||||
log.Println(`error generating token`, err)
|
||||
}
|
||||
|
@ -53,12 +57,11 @@ func ResendVerifyEmailResolver(ctx context.Context, params model.ResendVerifyEma
|
|||
Identifier: params.Identifier,
|
||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||
Email: params.Email,
|
||||
Nonce: nonce,
|
||||
})
|
||||
|
||||
// exec it as go routin so that we can reduce the api latency
|
||||
go func() {
|
||||
email.SendVerificationMail(params.Email, verificationToken, hostname)
|
||||
}()
|
||||
go email.SendVerificationMail(params.Email, verificationToken, hostname)
|
||||
|
||||
res = &model.Response{
|
||||
Message: `Verification email has been sent. Please check your inbox`,
|
||||
|
|
|
@ -12,11 +12,16 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
)
|
||||
|
||||
// ResetPasswordResolver is a resolver for reset password mutation
|
||||
func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error) {
|
||||
var res *model.Response
|
||||
gc, err := utils.GinContextFromContext(ctx)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
if envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication) {
|
||||
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
|
||||
}
|
||||
|
@ -31,12 +36,17 @@ func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput)
|
|||
}
|
||||
|
||||
// verify if token exists in db
|
||||
claim, err := token.ParseJWTToken(params.Token)
|
||||
hostname := utils.GetHost(gc)
|
||||
encryptedNonce, err := utils.EncryptNonce(verificationRequest.Nonce)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
claim, err := token.ParseJWTToken(params.Token, hostname, encryptedNonce, verificationRequest.Email)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf(`invalid token`)
|
||||
}
|
||||
|
||||
user, err := db.Provider.GetUserByEmail(claim["email"].(string))
|
||||
user, err := db.Provider.GetUserByEmail(claim["sub"].(string))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/cookie"
|
||||
"github.com/authorizerdev/authorizer/server/crypto"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||
|
@ -14,6 +13,7 @@ import (
|
|||
)
|
||||
|
||||
// SessionResolver is a resolver for session query
|
||||
// TODO allow validating with code and code verifier instead of cookie (PKCE flow)
|
||||
func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*model.AuthResponse, error) {
|
||||
var res *model.AuthResponse
|
||||
|
||||
|
@ -22,48 +22,27 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
|
|||
return res, err
|
||||
}
|
||||
|
||||
// get refresh token
|
||||
refreshToken, err := token.GetRefreshToken(gc)
|
||||
sessionToken, err := cookie.GetSession(gc)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// get fingerprint hash
|
||||
fingerprintHash, err := token.GetFingerPrint(gc)
|
||||
// get session from cookie
|
||||
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
decryptedFingerPrint, err := crypto.DecryptAES([]byte(fingerprintHash))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
fingerPrint := string(decryptedFingerPrint)
|
||||
|
||||
// verify refresh token and fingerprint
|
||||
claims, err := token.ParseJWTToken(refreshToken)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
userID := claims["id"].(string)
|
||||
|
||||
persistedRefresh := sessionstore.GetUserSession(userID, fingerPrint)
|
||||
if refreshToken != persistedRefresh {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
userID := claims.Subject
|
||||
user, err := db.Provider.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// refresh token has "roles" as claim
|
||||
claimRoleInterface := claims["roles"].([]interface{})
|
||||
claimRoleInterface := claims.Roles
|
||||
claimRoles := []string{}
|
||||
for _, v := range claimRoleInterface {
|
||||
claimRoles = append(claimRoles, v.(string))
|
||||
claimRoles = append(claimRoles, v)
|
||||
}
|
||||
|
||||
if params != nil && params.Roles != nil && len(params.Roles) > 0 {
|
||||
|
@ -74,22 +53,35 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
|
|||
}
|
||||
}
|
||||
|
||||
// delete older session
|
||||
sessionstore.DeleteUserSession(userID, fingerPrint)
|
||||
scope := []string{"openid", "email", "profile"}
|
||||
if params != nil && params.Scope != nil && len(scope) > 0 {
|
||||
scope = params.Scope
|
||||
}
|
||||
|
||||
authToken, err := token.CreateAuthToken(user, claimRoles)
|
||||
authToken, err := token.CreateAuthToken(gc, user, claimRoles, scope)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
sessionstore.SetUserSession(user.ID, authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
cookie.SetCookie(gc, authToken.AccessToken.Token, authToken.RefreshToken.Token, authToken.FingerPrintHash)
|
||||
|
||||
// rollover the session for security
|
||||
sessionstore.RemoveState(sessionToken)
|
||||
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
|
||||
expiresIn := int64(1800)
|
||||
res = &model.AuthResponse{
|
||||
Message: `Session token refreshed`,
|
||||
AccessToken: &authToken.AccessToken.Token,
|
||||
ExpiresAt: &authToken.AccessToken.ExpiresAt,
|
||||
ExpiresIn: &expiresIn,
|
||||
IDToken: &authToken.IDToken.Token,
|
||||
User: user.AsAPIUser(),
|
||||
}
|
||||
|
||||
if authToken.RefreshToken != nil {
|
||||
res.RefreshToken = &authToken.RefreshToken.Token
|
||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
@ -123,41 +123,48 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
|
|||
hostname := utils.GetHost(gc)
|
||||
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
||||
// insert verification request
|
||||
verificationType := constants.VerificationTypeBasicAuthSignup
|
||||
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname)
|
||||
nonce, nonceHash, err := utils.GenerateNonce()
|
||||
if err != nil {
|
||||
log.Println(`error generating token`, err)
|
||||
return res, err
|
||||
}
|
||||
verificationType := constants.VerificationTypeBasicAuthSignup
|
||||
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname, nonceHash)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||
Token: verificationToken,
|
||||
Identifier: verificationType,
|
||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||
Email: params.Email,
|
||||
Nonce: nonce,
|
||||
})
|
||||
|
||||
// exec it as go routin so that we can reduce the api latency
|
||||
go func() {
|
||||
email.SendVerificationMail(params.Email, verificationToken, hostname)
|
||||
}()
|
||||
go email.SendVerificationMail(params.Email, verificationToken, hostname)
|
||||
|
||||
res = &model.AuthResponse{
|
||||
Message: `Verification email has been sent. Please check your inbox`,
|
||||
User: userToReturn,
|
||||
}
|
||||
} else {
|
||||
scope := []string{"openid", "email", "profile"}
|
||||
|
||||
authToken, err := token.CreateAuthToken(user, roles)
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
sessionstore.SetUserSession(user.ID, authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
cookie.SetCookie(gc, authToken.AccessToken.Token, authToken.RefreshToken.Token, authToken.FingerPrintHash)
|
||||
utils.SaveSessionInDB(user.ID, gc)
|
||||
|
||||
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
go utils.SaveSessionInDB(gc, user.ID)
|
||||
|
||||
expiresIn := int64(1800)
|
||||
|
||||
res = &model.AuthResponse{
|
||||
Message: `Signed up successfully.`,
|
||||
AccessToken: &authToken.AccessToken.Token,
|
||||
ExpiresAt: &authToken.AccessToken.ExpiresAt,
|
||||
ExpiresIn: &expiresIn,
|
||||
User: userToReturn,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/email"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
|
@ -28,7 +29,11 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
|
|||
return res, err
|
||||
}
|
||||
|
||||
claims, err := token.ValidateAccessToken(gc)
|
||||
accessToken, err := token.GetAccessToken(gc)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
claims, err := token.ValidateAccessToken(gc, accessToken)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
@ -38,8 +43,8 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
|
|||
return res, fmt.Errorf("please enter at least one param to update")
|
||||
}
|
||||
|
||||
userEmail := fmt.Sprintf("%v", claims["email"])
|
||||
user, err := db.Provider.GetUserByEmail(userEmail)
|
||||
userID := claims["sub"].(string)
|
||||
user, err := db.Provider.GetUserByID(userID)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
@ -108,38 +113,44 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
|
|||
newEmail := strings.ToLower(*params.Email)
|
||||
// check if user with new email exists
|
||||
_, err := db.Provider.GetUserByEmail(newEmail)
|
||||
|
||||
// err = nil means user exists
|
||||
if err == nil {
|
||||
return res, fmt.Errorf("user with this email address already exists")
|
||||
}
|
||||
|
||||
sessionstore.DeleteAllUserSession(fmt.Sprintf("%v", user.ID))
|
||||
cookie.DeleteCookie(gc)
|
||||
// TODO figure out how to delete all user sessions
|
||||
go sessionstore.DeleteAllUserSession(user.ID)
|
||||
|
||||
hostname := utils.GetHost(gc)
|
||||
cookie.DeleteSession(gc)
|
||||
user.Email = newEmail
|
||||
user.EmailVerifiedAt = nil
|
||||
hasEmailChanged = true
|
||||
// insert verification request
|
||||
verificationType := constants.VerificationTypeUpdateEmail
|
||||
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname)
|
||||
if err != nil {
|
||||
log.Println(`error generating token`, err)
|
||||
|
||||
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
||||
hostname := utils.GetHost(gc)
|
||||
user.EmailVerifiedAt = nil
|
||||
hasEmailChanged = true
|
||||
// insert verification request
|
||||
nonce, nonceHash, err := utils.GenerateNonce()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
verificationType := constants.VerificationTypeUpdateEmail
|
||||
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname, nonceHash)
|
||||
if err != nil {
|
||||
log.Println(`error generating token`, err)
|
||||
}
|
||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||
Token: verificationToken,
|
||||
Identifier: verificationType,
|
||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||
Email: newEmail,
|
||||
Nonce: nonce,
|
||||
})
|
||||
|
||||
// exec it as go routin so that we can reduce the api latency
|
||||
go email.SendVerificationMail(newEmail, verificationToken, hostname)
|
||||
|
||||
}
|
||||
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||
Token: verificationToken,
|
||||
Identifier: verificationType,
|
||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||
Email: newEmail,
|
||||
})
|
||||
|
||||
// exec it as go routin so that we can reduce the api latency
|
||||
go func() {
|
||||
email.SendVerificationMail(newEmail, verificationToken, hostname)
|
||||
}()
|
||||
}
|
||||
|
||||
_, err = db.Provider.UpdateUser(user)
|
||||
if err != nil {
|
||||
log.Println("error updating user:", err)
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/cookie"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/email"
|
||||
|
@ -95,15 +94,19 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
|
|||
return res, fmt.Errorf("user with this email address already exists")
|
||||
}
|
||||
|
||||
sessionstore.DeleteAllUserSession(fmt.Sprintf("%v", user.ID))
|
||||
cookie.DeleteCookie(gc)
|
||||
// TODO figure out how to do this
|
||||
go sessionstore.DeleteAllUserSession(user.ID)
|
||||
|
||||
hostname := utils.GetHost(gc)
|
||||
user.Email = newEmail
|
||||
user.EmailVerifiedAt = nil
|
||||
// insert verification request
|
||||
nonce, nonceHash, err := utils.GenerateNonce()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
verificationType := constants.VerificationTypeUpdateEmail
|
||||
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname)
|
||||
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname, nonceHash)
|
||||
if err != nil {
|
||||
log.Println(`error generating token`, err)
|
||||
}
|
||||
|
@ -112,12 +115,12 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
|
|||
Identifier: verificationType,
|
||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||
Email: newEmail,
|
||||
Nonce: nonce,
|
||||
})
|
||||
|
||||
// exec it as go routin so that we can reduce the api latency
|
||||
go func() {
|
||||
email.SendVerificationMail(newEmail, verificationToken, hostname)
|
||||
}()
|
||||
go email.SendVerificationMail(newEmail, verificationToken, hostname)
|
||||
|
||||
}
|
||||
|
||||
rolesToSave := ""
|
||||
|
@ -136,8 +139,7 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
|
|||
rolesToSave = strings.Join(inputRoles, ",")
|
||||
}
|
||||
|
||||
sessionstore.DeleteAllUserSession(fmt.Sprintf("%v", user.ID))
|
||||
cookie.DeleteCookie(gc)
|
||||
go sessionstore.DeleteAllUserSession(user.ID)
|
||||
}
|
||||
|
||||
if rolesToSave != "" {
|
||||
|
|
|
@ -28,12 +28,17 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
|
|||
}
|
||||
|
||||
// verify if token exists in db
|
||||
claim, err := token.ParseJWTToken(params.Token)
|
||||
hostname := utils.GetHost(gc)
|
||||
encryptedNonce, err := utils.EncryptNonce(verificationRequest.Nonce)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
claim, err := token.ParseJWTToken(params.Token, hostname, encryptedNonce, verificationRequest.Email)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf(`invalid token: %s`, err.Error())
|
||||
}
|
||||
|
||||
user, err := db.Provider.GetUserByEmail(claim["email"].(string))
|
||||
user, err := db.Provider.GetUserByEmail(claim["sub"].(string))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
@ -41,25 +46,35 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
|
|||
// update email_verified_at in users table
|
||||
now := time.Now().Unix()
|
||||
user.EmailVerifiedAt = &now
|
||||
db.Provider.UpdateUser(user)
|
||||
// delete from verification table
|
||||
db.Provider.DeleteVerificationRequest(verificationRequest)
|
||||
|
||||
roles := strings.Split(user.Roles, ",")
|
||||
authToken, err := token.CreateAuthToken(user, roles)
|
||||
user, err = db.Provider.UpdateUser(user)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
// delete from verification table
|
||||
err = db.Provider.DeleteVerificationRequest(verificationRequest)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
sessionstore.SetUserSession(user.ID, authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||
cookie.SetCookie(gc, authToken.AccessToken.Token, authToken.RefreshToken.Token, authToken.FingerPrintHash)
|
||||
utils.SaveSessionInDB(user.ID, gc)
|
||||
|
||||
roles := strings.Split(user.Roles, ",")
|
||||
scope := []string{"openid", "email", "profile"}
|
||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
||||
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||
go utils.SaveSessionInDB(gc, user.ID)
|
||||
|
||||
expiresIn := int64(1800)
|
||||
res = &model.AuthResponse{
|
||||
Message: `Email verified successfully.`,
|
||||
AccessToken: &authToken.AccessToken.Token,
|
||||
ExpiresAt: &authToken.AccessToken.ExpiresAt,
|
||||
IDToken: &authToken.IDToken.Token,
|
||||
ExpiresIn: &expiresIn,
|
||||
User: user.AsAPIUser(),
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package sessionstore
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
@ -11,42 +12,6 @@ type InMemoryStore struct {
|
|||
stateStore 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.sessionStore) >= 500 {
|
||||
c.sessionStore = map[string]map[string]string{}
|
||||
}
|
||||
// check if entry exists in map
|
||||
_, exists := c.sessionStore[userId]
|
||||
if exists {
|
||||
tempMap := c.sessionStore[userId]
|
||||
tempMap[accessToken] = refreshToken
|
||||
c.sessionStore[userId] = tempMap
|
||||
} else {
|
||||
tempMap := map[string]string{
|
||||
accessToken: refreshToken,
|
||||
}
|
||||
c.sessionStore[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.sessionStore, 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.sessionStore[userId], accessToken)
|
||||
}
|
||||
|
||||
// ClearStore clears the in-memory store.
|
||||
func (c *InMemoryStore) ClearStore() {
|
||||
c.mutex.Lock()
|
||||
|
@ -54,32 +19,29 @@ func (c *InMemoryStore) ClearStore() {
|
|||
c.sessionStore = 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.sessionStore[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.sessionStore[userId]
|
||||
if !ok {
|
||||
return nil
|
||||
res := map[string]string{}
|
||||
for k, v := range c.stateStore {
|
||||
split := strings.Split(v, "@")
|
||||
if split[1] == userId {
|
||||
res[k] = split[0]
|
||||
}
|
||||
}
|
||||
|
||||
return sessionMap
|
||||
return res
|
||||
}
|
||||
|
||||
// DeleteAllUserSession deletes all the user sessions from in-memory store.
|
||||
func (c *InMemoryStore) DeleteAllUserSession(userId string) {
|
||||
// c.mutex.Lock()
|
||||
// defer c.mutex.Unlock()
|
||||
sessions := GetUserSessions(userId)
|
||||
for k := range sessions {
|
||||
RemoveState(k)
|
||||
}
|
||||
}
|
||||
|
||||
// SetState sets the state in the in-memory store.
|
||||
|
|
|
@ -2,8 +2,8 @@ package sessionstore
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type RedisStore struct {
|
||||
|
@ -11,32 +11,6 @@ type RedisStore struct {
|
|||
store RedisSessionClient
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
@ -45,32 +19,40 @@ func (c *RedisStore) ClearStore() {
|
|||
}
|
||||
}
|
||||
|
||||
// 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()
|
||||
data, err := c.store.HGetAll(c.ctx, "*").Result()
|
||||
if err != nil {
|
||||
log.Println("error getting token from redis store:", err)
|
||||
}
|
||||
|
||||
res := map[string]string{}
|
||||
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
|
||||
func (c *RedisStore) DeleteAllUserSession(userId string) {
|
||||
sessions := GetUserSessions(userId)
|
||||
for k, v := range sessions {
|
||||
if k == "token" {
|
||||
err := c.store.Del(c.ctx, v)
|
||||
if err != nil {
|
||||
log.Println("Error deleting redis token:", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SetState sets the state in redis store.
|
||||
func (c *RedisStore) SetState(key, state string) {
|
||||
err := c.store.Set(c.ctx, key, state, 0).Err()
|
||||
func (c *RedisStore) SetState(key, value string) {
|
||||
err := c.store.Set(c.ctx, key, value, 0).Err()
|
||||
if err != nil {
|
||||
log.Fatalln("Error saving redis token:", err)
|
||||
}
|
||||
|
|
|
@ -22,26 +22,6 @@ type SessionStore struct {
|
|||
// 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 {
|
||||
|
@ -52,18 +32,6 @@ func DeleteAllUserSession(userId string) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func isValidJWTTests(t *testing.T, s TestSetup) {
|
||||
t.Helper()
|
||||
_, ctx := createContext(s)
|
||||
expiredToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhbGxvd2VkX3JvbGVzIjpbIiJdLCJiaXJ0aGRhdGUiOm51bGwsImNyZWF0ZWRfYXQiOjAsImVtYWlsIjoiam9obi5kb2VAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJleHAiOjE2NDI5NjEwMTEsImV4dHJhIjp7IngtZXh0cmEtaWQiOiJkMmNhMjQwNy05MzZmLTQwYzQtOTQ2NS05Y2M5MWYxZTJhNDQifSwiZmFtaWx5X25hbWUiOm51bGwsImdlbmRlciI6bnVsbCwiZ2l2ZW5fbmFtZSI6bnVsbCwiaWF0IjoxNjQyOTYwOTgxLCJpZCI6ImQyY2EyNDA3LTkzNmYtNDBjNC05NDY1LTljYzkxZjFlMmE0NCIsIm1pZGRsZV9uYW1lIjpudWxsLCJuaWNrbmFtZSI6bnVsbCwicGhvbmVfbnVtYmVyIjpudWxsLCJwaG9uZV9udW1iZXJfdmVyaWZpZWQiOmZhbHNlLCJwaWN0dXJlIjpudWxsLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJqb2huLmRvZUBnbWFpbC5jb20iLCJyb2xlIjpbXSwic2lnbnVwX21ldGhvZHMiOiIiLCJ0b2tlbl90eXBlIjoiYWNjZXNzX3Rva2VuIiwidXBkYXRlZF9hdCI6MH0.FrdyeOC5e8uU1SowGj0omFJuwRnh4BrEk89S_fbEkzs"
|
||||
|
||||
t.Run(`should fail for invalid jwt`, func(t *testing.T) {
|
||||
_, err := resolvers.IsValidJwtResolver(ctx, &model.IsValidJWTQueryInput{
|
||||
Jwt: &expiredToken,
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
|
||||
t.Run(`should pass with valid jwt`, func(t *testing.T) {
|
||||
authToken, err := token.CreateAuthToken(models.User{
|
||||
ID: uuid.New().String(),
|
||||
Email: "john.doe@gmail.com",
|
||||
}, []string{})
|
||||
assert.Nil(t, err)
|
||||
res, err := resolvers.IsValidJwtResolver(ctx, &model.IsValidJWTQueryInput{
|
||||
Jwt: &authToken.AccessToken.Token,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, res.Valid)
|
||||
})
|
||||
}
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/token"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
@ -18,12 +19,17 @@ func TestJwt(t *testing.T) {
|
|||
publicKey := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtPublicKey)
|
||||
privateKey := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtPrivateKey)
|
||||
clientID := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID)
|
||||
nonce := uuid.New().String()
|
||||
hostname := "localhost"
|
||||
subject := "test"
|
||||
claims := jwt.MapClaims{
|
||||
"exp": time.Now().Add(time.Minute * 30).Unix(),
|
||||
"iat": time.Now().Unix(),
|
||||
"email": "test@yopmail.com",
|
||||
"sub": "test",
|
||||
"sub": subject,
|
||||
"aud": clientID,
|
||||
"nonce": nonce,
|
||||
"iss": hostname,
|
||||
}
|
||||
|
||||
t.Run("invalid jwt type", func(t *testing.T) {
|
||||
|
@ -42,7 +48,7 @@ func TestJwt(t *testing.T) {
|
|||
}
|
||||
jwtToken, err := token.SignJWTToken(expiredClaims)
|
||||
assert.NoError(t, err)
|
||||
_, err = token.ParseJWTToken(jwtToken)
|
||||
_, err = token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||
assert.Error(t, err, err.Error(), "Token is expired")
|
||||
})
|
||||
t.Run("HMAC algorithms", func(t *testing.T) {
|
||||
|
@ -52,7 +58,7 @@ func TestJwt(t *testing.T) {
|
|||
jwtToken, err := token.SignJWTToken(claims)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c["email"].(string), claims["email"])
|
||||
})
|
||||
|
@ -61,7 +67,7 @@ func TestJwt(t *testing.T) {
|
|||
jwtToken, err := token.SignJWTToken(claims)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c["email"].(string), claims["email"])
|
||||
})
|
||||
|
@ -70,7 +76,7 @@ func TestJwt(t *testing.T) {
|
|||
jwtToken, err := token.SignJWTToken(claims)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c["email"].(string), claims["email"])
|
||||
})
|
||||
|
@ -86,7 +92,7 @@ func TestJwt(t *testing.T) {
|
|||
jwtToken, err := token.SignJWTToken(claims)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c["email"].(string), claims["email"])
|
||||
})
|
||||
|
@ -99,7 +105,7 @@ func TestJwt(t *testing.T) {
|
|||
jwtToken, err := token.SignJWTToken(claims)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c["email"].(string), claims["email"])
|
||||
})
|
||||
|
@ -112,7 +118,7 @@ func TestJwt(t *testing.T) {
|
|||
jwtToken, err := token.SignJWTToken(claims)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c["email"].(string), claims["email"])
|
||||
})
|
||||
|
@ -128,7 +134,7 @@ func TestJwt(t *testing.T) {
|
|||
jwtToken, err := token.SignJWTToken(claims)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c["email"].(string), claims["email"])
|
||||
})
|
||||
|
@ -141,7 +147,7 @@ func TestJwt(t *testing.T) {
|
|||
jwtToken, err := token.SignJWTToken(claims)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c["email"].(string), claims["email"])
|
||||
})
|
||||
|
@ -154,7 +160,7 @@ func TestJwt(t *testing.T) {
|
|||
jwtToken, err := token.SignJWTToken(claims)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken)
|
||||
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c["email"].(string), claims["email"])
|
||||
})
|
||||
|
|
|
@ -5,14 +5,17 @@ import (
|
|||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func loginTests(t *testing.T, s TestSetup) {
|
||||
t.Helper()
|
||||
t.Run(`should login`, func(t *testing.T) {
|
||||
t.Logf("=> is enabled: %v", envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification))
|
||||
_, ctx := createContext(s)
|
||||
email := "login." + s.TestInfo.Email
|
||||
_, err := resolvers.SignupResolver(ctx, model.SignUpInput{
|
||||
|
@ -21,15 +24,19 @@ func loginTests(t *testing.T, s TestSetup) {
|
|||
ConfirmPassword: s.TestInfo.Password,
|
||||
})
|
||||
|
||||
_, err = resolvers.LoginResolver(ctx, model.LoginInput{
|
||||
res, err := resolvers.LoginResolver(ctx, model.LoginInput{
|
||||
Email: email,
|
||||
Password: s.TestInfo.Password,
|
||||
})
|
||||
|
||||
assert.NotNil(t, err, "should fail because email is not verified")
|
||||
|
||||
assert.Nil(t, res)
|
||||
verificationRequest, err := db.Provider.GetVerificationRequestByEmail(email, constants.VerificationTypeBasicAuthSignup)
|
||||
res, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
|
||||
n, err := utils.EncryptNonce(verificationRequest.Nonce)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, n)
|
||||
assert.NotNil(t, verificationRequest)
|
||||
res, err = resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
|
||||
Token: verificationRequest.Token,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -2,11 +2,9 @@ package test
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/crypto"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
|
@ -30,18 +28,15 @@ func logoutTests(t *testing.T, s TestSetup) {
|
|||
Token: verificationRequest.Token,
|
||||
})
|
||||
|
||||
sessions := sessionstore.GetUserSessions(verifyRes.User.ID)
|
||||
fingerPrint := ""
|
||||
refreshToken := ""
|
||||
for key, val := range sessions {
|
||||
fingerPrint = key
|
||||
refreshToken = val
|
||||
}
|
||||
|
||||
fingerPrintHash, _ := crypto.EncryptAES([]byte(fingerPrint))
|
||||
|
||||
token := *verifyRes.AccessToken
|
||||
cookie := fmt.Sprintf("%s=%s;%s=%s;%s=%s", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".fingerprint", url.QueryEscape(string(fingerPrintHash)), envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".refresh_token", refreshToken, envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", token)
|
||||
sessions := sessionstore.GetUserSessions(verifyRes.User.ID)
|
||||
cookie := ""
|
||||
// set all they keys in cookie one of them should be session cookie
|
||||
for key := range sessions {
|
||||
if key != token {
|
||||
cookie += fmt.Sprintf("%s=%s;", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session", key)
|
||||
}
|
||||
}
|
||||
|
||||
req.Header.Set("Cookie", cookie)
|
||||
_, err = resolvers.LogoutResolver(ctx)
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -27,12 +26,13 @@ func magicLinkLoginTests(t *testing.T, s TestSetup) {
|
|||
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
|
||||
Token: verificationRequest.Token,
|
||||
})
|
||||
|
||||
token := *verifyRes.AccessToken
|
||||
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", token))
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, verifyRes.AccessToken)
|
||||
s.GinContext.Request.Header.Set("Authorization", "Bearer "+*verifyRes.AccessToken)
|
||||
ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext)
|
||||
_, err = resolvers.ProfileResolver(ctx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
s.GinContext.Request.Header.Set("Authorization", "")
|
||||
cleanData(email)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -14,7 +13,7 @@ import (
|
|||
|
||||
func profileTests(t *testing.T, s TestSetup) {
|
||||
t.Helper()
|
||||
t.Run(`should get profile only with token`, func(t *testing.T) {
|
||||
t.Run(`should get profile only access_token token`, func(t *testing.T) {
|
||||
req, ctx := createContext(s)
|
||||
email := "profile." + s.TestInfo.Email
|
||||
|
||||
|
@ -31,11 +30,14 @@ func profileTests(t *testing.T, s TestSetup) {
|
|||
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
|
||||
Token: verificationRequest.Token,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, verifyRes.AccessToken)
|
||||
|
||||
token := *verifyRes.AccessToken
|
||||
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", token))
|
||||
s.GinContext.Request.Header.Set("Authorization", "Bearer "+*verifyRes.AccessToken)
|
||||
ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext)
|
||||
profileRes, err := resolvers.ProfileResolver(ctx)
|
||||
assert.Nil(t, err)
|
||||
s.GinContext.Request.Header.Set("Authorization", "")
|
||||
|
||||
newEmail := *&profileRes.Email
|
||||
assert.Equal(t, email, newEmail, "emails should be equal")
|
||||
|
|
|
@ -15,15 +15,16 @@ func TestResolvers(t *testing.T) {
|
|||
// constants.DbTypeArangodb: "http://localhost:8529",
|
||||
// constants.DbTypeMongodb: "mongodb://localhost:27017",
|
||||
}
|
||||
envstore.EnvStoreObj.ResetStore()
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyVersion, "test")
|
||||
for dbType, dbURL := range databases {
|
||||
s := testSetup()
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseURL, dbURL)
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseType, dbType)
|
||||
|
||||
s := testSetup()
|
||||
defer s.Server.Close()
|
||||
db.InitDB()
|
||||
err := db.InitDB()
|
||||
if err != nil {
|
||||
t.Errorf("Error initializing database: %s", err)
|
||||
}
|
||||
|
||||
// clean the persisted config for test to use fresh config
|
||||
envData, err := db.Provider.GetEnv()
|
||||
|
@ -31,12 +32,10 @@ func TestResolvers(t *testing.T) {
|
|||
envData.EnvData = ""
|
||||
db.Provider.UpdateEnv(envData)
|
||||
}
|
||||
err = env.InitAllEnv()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
env.PersistEnv()
|
||||
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEnv, "test")
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.BoolStoreIdentifier, constants.EnvKeyIsProd, false)
|
||||
t.Run("should pass tests for "+dbType, func(t *testing.T) {
|
||||
// admin tests
|
||||
adminSignupTests(t, s)
|
||||
|
@ -63,7 +62,6 @@ func TestResolvers(t *testing.T) {
|
|||
magicLinkLoginTests(t, s)
|
||||
logoutTests(t, s)
|
||||
metaTests(t, s)
|
||||
isValidJWTTests(t, s)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,10 @@ package test
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/crypto"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
|
@ -36,17 +35,15 @@ func sessionTests(t *testing.T, s TestSetup) {
|
|||
})
|
||||
|
||||
sessions := sessionstore.GetUserSessions(verifyRes.User.ID)
|
||||
fingerPrint := ""
|
||||
refreshToken := ""
|
||||
for key, val := range sessions {
|
||||
fingerPrint = key
|
||||
refreshToken = val
|
||||
}
|
||||
|
||||
fingerPrintHash, _ := crypto.EncryptAES([]byte(fingerPrint))
|
||||
|
||||
cookie := ""
|
||||
token := *verifyRes.AccessToken
|
||||
cookie := fmt.Sprintf("%s=%s;%s=%s;%s=%s", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".fingerprint", url.QueryEscape(string(fingerPrintHash)), envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".refresh_token", refreshToken, envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", token)
|
||||
// set all they keys in cookie one of them should be session cookie
|
||||
for key := range sessions {
|
||||
if key != token {
|
||||
cookie += fmt.Sprintf("%s=%s;", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session", key)
|
||||
}
|
||||
}
|
||||
cookie = strings.TrimSuffix(cookie, ";")
|
||||
|
||||
req.Header.Set("Cookie", cookie)
|
||||
|
||||
|
|
|
@ -72,13 +72,13 @@ func testSetup() TestSetup {
|
|||
}
|
||||
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEnvPath, "../../.env.sample")
|
||||
env.InitRequiredEnv()
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySmtpHost, "smtp.yopmail.com")
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySmtpPort, "2525")
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySmtpUsername, "lakhan@yopmail.com")
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySmtpPassword, "test")
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySenderEmail, "info@yopmail.com")
|
||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.SliceStoreIdentifier, constants.EnvKeyProtectedRoles, []string{"admin"})
|
||||
env.InitRequiredEnv()
|
||||
db.InitDB()
|
||||
env.InitAllEnv()
|
||||
sessionstore.InitSession()
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/db"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -34,18 +33,16 @@ func updateProfileTests(t *testing.T, s TestSetup) {
|
|||
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
|
||||
Token: verificationRequest.Token,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
token := *verifyRes.AccessToken
|
||||
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", token))
|
||||
_, err = resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
|
||||
FamilyName: &fName,
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
s.GinContext.Request.Header.Set("Authorization", "Bearer "+*verifyRes.AccessToken)
|
||||
ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext)
|
||||
|
||||
newEmail := "new_" + email
|
||||
_, err = resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
|
||||
Email: &newEmail,
|
||||
})
|
||||
s.GinContext.Request.Header.Set("Authorization", "")
|
||||
assert.Nil(t, err)
|
||||
_, err = resolvers.ProfileResolver(ctx)
|
||||
assert.NotNil(t, err, "unauthorized")
|
||||
|
|
|
@ -19,12 +19,15 @@ func verificationRequestsTest(t *testing.T, s TestSetup) {
|
|||
req, ctx := createContext(s)
|
||||
|
||||
email := "verification_requests." + s.TestInfo.Email
|
||||
resolvers.SignupResolver(ctx, model.SignUpInput{
|
||||
res, err := resolvers.SignupResolver(ctx, model.SignUpInput{
|
||||
Email: email,
|
||||
Password: s.TestInfo.Password,
|
||||
ConfirmPassword: s.TestInfo.Password,
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, res)
|
||||
|
||||
limit := int64(10)
|
||||
page := int64(1)
|
||||
pagination := &model.PaginatedInput{
|
||||
|
|
|
@ -2,23 +2,23 @@ package token
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/cookie"
|
||||
"github.com/authorizerdev/authorizer/server/crypto"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/robertkrimen/otto"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/constants"
|
||||
"github.com/authorizerdev/authorizer/server/crypto"
|
||||
"github.com/authorizerdev/authorizer/server/db/models"
|
||||
"github.com/authorizerdev/authorizer/server/envstore"
|
||||
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||
"github.com/authorizerdev/authorizer/server/utils"
|
||||
)
|
||||
|
||||
// JWTToken is a struct to hold JWT token and its expiration time
|
||||
|
@ -33,60 +33,219 @@ type Token struct {
|
|||
FingerPrintHash string `json:"fingerprint_hash"`
|
||||
RefreshToken *JWTToken `json:"refresh_token"`
|
||||
AccessToken *JWTToken `json:"access_token"`
|
||||
IDToken *JWTToken `json:"id_token"`
|
||||
}
|
||||
|
||||
// SessionData
|
||||
type SessionData struct {
|
||||
Subject string `json:"sub"`
|
||||
Roles []string `json:"roles"`
|
||||
Scope []string `json:"scope"`
|
||||
Nonce string `json:"nonce"`
|
||||
IssuedAt int64 `json:"iat"`
|
||||
ExpiresAt int64 `json:"exp"`
|
||||
}
|
||||
|
||||
// CreateSessionToken creates a new session token
|
||||
func CreateSessionToken(user models.User, nonce string, roles, scope []string) (*SessionData, string, error) {
|
||||
fingerPrintMap := &SessionData{
|
||||
Nonce: nonce,
|
||||
Roles: roles,
|
||||
Subject: user.ID,
|
||||
Scope: scope,
|
||||
IssuedAt: time.Now().Unix(),
|
||||
ExpiresAt: time.Now().AddDate(1, 0, 0).Unix(),
|
||||
}
|
||||
fingerPrintBytes, _ := json.Marshal(fingerPrintMap)
|
||||
fingerPrintHash, err := crypto.EncryptAES(string(fingerPrintBytes))
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return fingerPrintMap, fingerPrintHash, nil
|
||||
}
|
||||
|
||||
// CreateAuthToken creates a new auth token when userlogs in
|
||||
func CreateAuthToken(user models.User, roles []string) (*Token, error) {
|
||||
fingerprint := uuid.NewString()
|
||||
fingerPrintHashBytes, err := crypto.EncryptAES([]byte(fingerprint))
|
||||
func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string) (*Token, error) {
|
||||
hostname := utils.GetHost(gc)
|
||||
nonce := uuid.New().String()
|
||||
_, fingerPrintHash, err := CreateSessionToken(user, nonce, roles, scope)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refreshToken, refreshTokenExpiresAt, err := CreateRefreshToken(user, roles)
|
||||
accessToken, accessTokenExpiresAt, err := CreateAccessToken(user, roles, scope, hostname, nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accessToken, accessTokenExpiresAt, err := CreateAccessToken(user, roles)
|
||||
idToken, idTokenExpiresAt, err := CreateIDToken(user, roles, hostname, nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Token{
|
||||
FingerPrint: fingerprint,
|
||||
FingerPrintHash: string(fingerPrintHashBytes),
|
||||
RefreshToken: &JWTToken{Token: refreshToken, ExpiresAt: refreshTokenExpiresAt},
|
||||
res := &Token{
|
||||
FingerPrint: nonce,
|
||||
FingerPrintHash: fingerPrintHash,
|
||||
AccessToken: &JWTToken{Token: accessToken, ExpiresAt: accessTokenExpiresAt},
|
||||
}, nil
|
||||
IDToken: &JWTToken{Token: idToken, ExpiresAt: idTokenExpiresAt},
|
||||
}
|
||||
|
||||
if utils.StringSliceContains(scope, "offline_access") {
|
||||
refreshToken, refreshTokenExpiresAt, err := CreateRefreshToken(user, roles, hostname, nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res.RefreshToken = &JWTToken{Token: refreshToken, ExpiresAt: refreshTokenExpiresAt}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// CreateRefreshToken util to create JWT token
|
||||
func CreateRefreshToken(user models.User, roles []string) (string, int64, error) {
|
||||
func CreateRefreshToken(user models.User, roles []string, hostname, nonce string) (string, int64, error) {
|
||||
// expires in 1 year
|
||||
expiryBound := time.Hour * 8760
|
||||
expiresAt := time.Now().Add(expiryBound).Unix()
|
||||
|
||||
customClaims := jwt.MapClaims{
|
||||
"iss": "",
|
||||
"aud": "",
|
||||
"iss": hostname,
|
||||
"aud": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID),
|
||||
"sub": user.ID,
|
||||
"exp": expiresAt,
|
||||
"iat": time.Now().Unix(),
|
||||
"token_type": constants.TokenTypeRefreshToken,
|
||||
"roles": roles,
|
||||
"id": user.ID,
|
||||
"nonce": nonce,
|
||||
}
|
||||
|
||||
token, err := SignJWTToken(customClaims)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
return token, expiresAt, nil
|
||||
}
|
||||
|
||||
// CreateAccessToken util to create JWT token, based on
|
||||
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
|
||||
func CreateAccessToken(user models.User, roles []string) (string, int64, error) {
|
||||
func CreateAccessToken(user models.User, roles, scopes []string, hostName, nonce string) (string, int64, error) {
|
||||
expiryBound := time.Minute * 30
|
||||
expiresAt := time.Now().Add(expiryBound).Unix()
|
||||
|
||||
customClaims := jwt.MapClaims{
|
||||
"iss": hostName,
|
||||
"aud": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID),
|
||||
"nonce": nonce,
|
||||
"sub": user.ID,
|
||||
"exp": expiresAt,
|
||||
"iat": time.Now().Unix(),
|
||||
"token_type": constants.TokenTypeAccessToken,
|
||||
"scope": scopes,
|
||||
"roles": roles,
|
||||
}
|
||||
|
||||
token, err := SignJWTToken(customClaims)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
}
|
||||
|
||||
return token, expiresAt, nil
|
||||
}
|
||||
|
||||
// GetAccessToken returns the access token from the request (either from header or cookie)
|
||||
func GetAccessToken(gc *gin.Context) (string, error) {
|
||||
// try to check in auth header for cookie
|
||||
auth := gc.Request.Header.Get("Authorization")
|
||||
if auth == "" {
|
||||
return "", fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(auth, "Bearer ") {
|
||||
return "", fmt.Errorf(`not a bearer token`)
|
||||
}
|
||||
|
||||
token := strings.TrimPrefix(auth, "Bearer ")
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// Function to validate access token for authorizer apis (profile, update_profile)
|
||||
func ValidateAccessToken(gc *gin.Context, accessToken string) (map[string]interface{}, error) {
|
||||
var res map[string]interface{}
|
||||
|
||||
if accessToken == "" {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
savedSession := sessionstore.GetState(accessToken)
|
||||
if savedSession == "" {
|
||||
return res, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
savedSessionSplit := strings.Split(savedSession, "@")
|
||||
nonce := savedSessionSplit[0]
|
||||
userID := savedSessionSplit[1]
|
||||
|
||||
hostname := utils.GetHost(gc)
|
||||
res, err := ParseJWTToken(accessToken, hostname, nonce, userID)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
if res["token_type"] != constants.TokenTypeAccessToken {
|
||||
return res, fmt.Errorf(`unauthorized: invalid token type`)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func ValidateBrowserSession(gc *gin.Context, encryptedSession string) (*SessionData, error) {
|
||||
if encryptedSession == "" {
|
||||
return nil, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
savedSession := sessionstore.GetState(encryptedSession)
|
||||
if savedSession == "" {
|
||||
return nil, fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
savedSessionSplit := strings.Split(savedSession, "@")
|
||||
nonce := savedSessionSplit[0]
|
||||
userID := savedSessionSplit[1]
|
||||
|
||||
decryptedFingerPrint, err := crypto.DecryptAES(encryptedSession)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var res SessionData
|
||||
err = json.Unmarshal([]byte(decryptedFingerPrint), &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res.Nonce != nonce {
|
||||
return nil, fmt.Errorf(`unauthorized: invalid nonce`)
|
||||
}
|
||||
|
||||
if res.Subject != userID {
|
||||
return nil, fmt.Errorf(`unauthorized: invalid user id`)
|
||||
}
|
||||
|
||||
if res.ExpiresAt < time.Now().Unix() {
|
||||
return nil, fmt.Errorf(`unauthorized: token expired`)
|
||||
}
|
||||
|
||||
// TODO validate scope
|
||||
// if !reflect.DeepEqual(res.Roles, roles) {
|
||||
// return res, "", fmt.Errorf(`unauthorized`)
|
||||
// }
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
// CreateIDToken util to create JWT token, based on
|
||||
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
|
||||
func CreateIDToken(user models.User, roles []string, hostname, nonce string) (string, int64, error) {
|
||||
expiryBound := time.Minute * 30
|
||||
expiresAt := time.Now().Add(expiryBound).Unix()
|
||||
|
||||
|
@ -97,13 +256,13 @@ func CreateAccessToken(user models.User, roles []string) (string, int64, error)
|
|||
|
||||
claimKey := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtRoleClaim)
|
||||
customClaims := jwt.MapClaims{
|
||||
"iss": "",
|
||||
"iss": hostname,
|
||||
"aud": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID),
|
||||
"nonce": "",
|
||||
"nonce": nonce,
|
||||
"sub": user.ID,
|
||||
"exp": expiresAt,
|
||||
"iat": time.Now().Unix(),
|
||||
"token_type": constants.TokenTypeAccessToken,
|
||||
"token_type": constants.TokenTypeIdentityToken,
|
||||
"allowed_roles": strings.Split(user.Roles, ","),
|
||||
claimKey: roles,
|
||||
}
|
||||
|
@ -152,58 +311,18 @@ func CreateAccessToken(user models.User, roles []string) (string, int64, error)
|
|||
return token, expiresAt, nil
|
||||
}
|
||||
|
||||
// GetAccessToken returns the access token from the request (either from header or cookie)
|
||||
func GetAccessToken(gc *gin.Context) (string, error) {
|
||||
token, err := cookie.GetAccessTokenCookie(gc)
|
||||
if err != nil || token == "" {
|
||||
// try to check in auth header for cookie
|
||||
auth := gc.Request.Header.Get("Authorization")
|
||||
if auth == "" {
|
||||
return "", fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
token = strings.TrimPrefix(auth, "Bearer ")
|
||||
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// GetRefreshToken returns the refresh token from cookie / request query url
|
||||
func GetRefreshToken(gc *gin.Context) (string, error) {
|
||||
token, err := cookie.GetRefreshTokenCookie(gc)
|
||||
|
||||
if err != nil || token == "" {
|
||||
// GetIDToken returns the id token from the request header
|
||||
func GetIDToken(gc *gin.Context) (string, error) {
|
||||
// try to check in auth header for cookie
|
||||
auth := gc.Request.Header.Get("Authorization")
|
||||
if auth == "" {
|
||||
return "", fmt.Errorf(`unauthorized`)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(auth, "Bearer ") {
|
||||
return "", fmt.Errorf(`not a bearer token`)
|
||||
}
|
||||
|
||||
token := strings.TrimPrefix(auth, "Bearer ")
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// GetFingerPrint returns the finger print from cookie
|
||||
func GetFingerPrint(gc *gin.Context) (string, error) {
|
||||
fingerPrint, err := cookie.GetFingerPrintCookie(gc)
|
||||
if err != nil || fingerPrint == "" {
|
||||
return "", fmt.Errorf(`no finger print`)
|
||||
}
|
||||
return fingerPrint, nil
|
||||
}
|
||||
|
||||
func ValidateAccessToken(gc *gin.Context) (map[string]interface{}, error) {
|
||||
token, err := GetAccessToken(gc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
claims, err := ParseJWTToken(token)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// also validate if there is user session present with access token
|
||||
sessions := sessionstore.GetUserSessions(claims["id"].(string))
|
||||
if len(sessions) == 0 {
|
||||
return nil, errors.New("unauthorized")
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ func SignJWTToken(claims jwt.MapClaims) (string, error) {
|
|||
}
|
||||
|
||||
// ParseJWTToken common util to parse jwt token
|
||||
func ParseJWTToken(token string) (jwt.MapClaims, error) {
|
||||
func ParseJWTToken(token, hostname, nonce, subject string) (jwt.MapClaims, error) {
|
||||
jwtType := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtType)
|
||||
signingMethod := jwt.GetSigningMethod(jwtType)
|
||||
|
||||
|
@ -87,5 +87,21 @@ func ParseJWTToken(token string) (jwt.MapClaims, error) {
|
|||
claims["exp"] = intExp
|
||||
claims["iat"] = intIat
|
||||
|
||||
if claims["aud"] != envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID) {
|
||||
return claims, errors.New("invalid audience")
|
||||
}
|
||||
|
||||
if claims["nonce"] != nonce {
|
||||
return claims, errors.New("invalid nonce")
|
||||
}
|
||||
|
||||
if claims["iss"] != hostname {
|
||||
return claims, errors.New("invalid issuer")
|
||||
}
|
||||
|
||||
if claims["sub"] != subject {
|
||||
return claims, errors.New("invalid subject")
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
|
|
|
@ -9,13 +9,15 @@ import (
|
|||
)
|
||||
|
||||
// CreateVerificationToken creates a verification JWT token
|
||||
func CreateVerificationToken(email, tokenType, hostname string) (string, error) {
|
||||
func CreateVerificationToken(email, tokenType, hostname, nonceHash string) (string, error) {
|
||||
claims := jwt.MapClaims{
|
||||
"iss": hostname,
|
||||
"aud": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID),
|
||||
"sub": email,
|
||||
"exp": time.Now().Add(time.Minute * 30).Unix(),
|
||||
"iat": time.Now().Unix(),
|
||||
"token_type": tokenType,
|
||||
"email": email,
|
||||
"host": hostname,
|
||||
"nonce": nonceHash,
|
||||
"redirect_url": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL),
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ func StringSliceContains(s []string, e string) bool {
|
|||
|
||||
// SaveSessionInDB saves sessions generated for a given user with meta information
|
||||
// Do not store token here as that could be security breach
|
||||
func SaveSessionInDB(userId string, c *gin.Context) {
|
||||
func SaveSessionInDB(c *gin.Context, userId string) {
|
||||
sessionData := models.Session{
|
||||
UserID: userId,
|
||||
UserAgent: GetUserAgent(c.Request),
|
||||
|
|
36
server/utils/nonce.go
Normal file
36
server/utils/nonce.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/authorizerdev/authorizer/server/crypto"
|
||||
)
|
||||
|
||||
// GenerateNonce generats random nonce string and returns
|
||||
// the nonce string, nonce hash, error
|
||||
func GenerateNonce() (string, string, error) {
|
||||
nonce := uuid.New().String()
|
||||
nonceHash, err := crypto.EncryptAES(nonce)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return nonce, nonceHash, err
|
||||
}
|
||||
|
||||
// EncryptNonce nonce string
|
||||
func EncryptNonce(nonce string) (string, error) {
|
||||
nonceHash, err := crypto.EncryptAES(nonce)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return nonceHash, err
|
||||
}
|
||||
|
||||
// DecryptNonce nonce string
|
||||
func DecryptNonce(nonceHash string) (string, error) {
|
||||
nonce, err := crypto.DecryptAES(nonceHash)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return nonce, err
|
||||
}
|
Loading…
Reference in New Issue
Block a user