fix: auth flow
This commit is contained in:
parent
5399ea8f32
commit
f0f2e0b6c8
|
@ -5,4 +5,6 @@ const (
|
||||||
TokenTypeRefreshToken = "refresh_token"
|
TokenTypeRefreshToken = "refresh_token"
|
||||||
// TokenTypeAccessToken is the access_token token type
|
// TokenTypeAccessToken is the access_token token type
|
||||||
TokenTypeAccessToken = "access_token"
|
TokenTypeAccessToken = "access_token"
|
||||||
|
// TokenTypeIdentityToken is the identity_token token type
|
||||||
|
TokenTypeIdentityToken = "id_token"
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,7 +2,6 @@ package cookie
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
|
@ -10,8 +9,8 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetSessionCookie sets the session cookie in the response
|
// SetSession sets the session cookie in the response
|
||||||
func SetSessionCookie(gc *gin.Context, sessionID string) {
|
func SetSession(gc *gin.Context, sessionID string) {
|
||||||
secure := true
|
secure := true
|
||||||
httpOnly := true
|
httpOnly := true
|
||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
|
@ -26,89 +25,16 @@ func SetSessionCookie(gc *gin.Context, sessionID string) {
|
||||||
|
|
||||||
gc.SetSameSite(http.SameSiteNoneMode)
|
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", 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.
|
// Fallback cookie for anomaly getection on browsers that don’t support the sameSite=None attribute.
|
||||||
gc.SetSameSite(http.SameSiteDefaultMode)
|
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_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
|
// DeleteSession sets session cookies to expire
|
||||||
// 1 COOKIE_NAME.access_token jwt token for the host (temp.abc.com)
|
func DeleteSession(gc *gin.Context) {
|
||||||
// 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) {
|
|
||||||
secure := true
|
|
||||||
httpOnly := true
|
|
||||||
hostname := utils.GetHost(gc)
|
|
||||||
host, _ := utils.GetHostParts(hostname)
|
|
||||||
domain := utils.GetDomainName(hostname)
|
|
||||||
if domain != "localhost" {
|
|
||||||
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)
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
if err != nil {
|
|
||||||
cookie, err = gc.Request.Cookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + ".access_token.domain")
|
|
||||||
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
|
secure := true
|
||||||
httpOnly := true
|
httpOnly := true
|
||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
|
@ -119,8 +45,32 @@ func DeleteCookie(gc *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
gc.SetSameSite(http.SameSiteNoneMode)
|
gc.SetSameSite(http.SameSiteNoneMode)
|
||||||
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", "", -1, "/", host, secure, httpOnly)
|
gc.SetCookie(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"_session", "", -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)+"_session_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)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) + "_session_domain")
|
||||||
|
if err != nil {
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,71 +3,40 @@ package crypto
|
||||||
import (
|
import (
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
"crypto/rand"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EncryptAES encrypts data using AES algorithm
|
var bytes = []byte{35, 46, 57, 24, 85, 35, 24, 74, 87, 35, 88, 98, 66, 32, 14, 05}
|
||||||
func EncryptAES(text []byte) ([]byte, error) {
|
|
||||||
|
// EncryptAES method is to encrypt or hide any classified text
|
||||||
|
func EncryptAES(text string) (string, error) {
|
||||||
key := []byte(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey))
|
key := []byte(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey))
|
||||||
c, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key)
|
||||||
var res []byte
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
plainText := []byte(text)
|
||||||
// gcm or Galois/Counter Mode, is a mode of operation
|
cfb := cipher.NewCFBEncrypter(block, bytes)
|
||||||
// for symmetric key cryptographic block ciphers
|
cipherText := make([]byte, len(plainText))
|
||||||
// - https://en.wikipedia.org/wiki/Galois/Counter_Mode
|
cfb.XORKeyStream(cipherText, plainText)
|
||||||
gcm, err := cipher.NewGCM(c)
|
return EncryptB64(string(cipherText)), nil
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecryptAES decrypts data using AES algorithm
|
// DecryptAES method is to extract back the encrypted text
|
||||||
func DecryptAES(ciphertext []byte) ([]byte, error) {
|
func DecryptAES(text string) (string, error) {
|
||||||
key := []byte(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey))
|
key := []byte(envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyEncryptionKey))
|
||||||
c, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key)
|
||||||
var res []byte
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
cipherText, err := DecryptB64(text)
|
||||||
gcm, err := cipher.NewGCM(c)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
cfb := cipher.NewCFBDecrypter(block, bytes)
|
||||||
nonceSize := gcm.NonceSize()
|
plainText := make([]byte, len(cipherText))
|
||||||
if len(ciphertext) < nonceSize {
|
cfb.XORKeyStream(plainText, []byte(cipherText))
|
||||||
return res, err
|
return string(plainText), nil
|
||||||
}
|
|
||||||
|
|
||||||
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
|
||||||
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return plaintext, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ func EncryptEnvData(data envstore.Store) (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
encryptedConfig, err := EncryptAES(configData)
|
encryptedConfig, err := EncryptAES(string(configData))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ type VerificationRequest struct {
|
||||||
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
CreatedAt int64 `json:"created_at" bson:"created_at"`
|
||||||
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
UpdatedAt int64 `json:"updated_at" bson:"updated_at"`
|
||||||
Email string `gorm:"uniqueIndex:idx_email_identifier" json:"email" bson:"email"`
|
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 {
|
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest {
|
||||||
|
|
|
@ -56,7 +56,10 @@ func NewProvider() (*provider, error) {
|
||||||
return nil, err
|
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{
|
return &provider{
|
||||||
db: sqlDB,
|
db: sqlDB,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
@ -31,6 +31,10 @@ func addEmailTemplate(a string, b map[string]interface{}, templateName string) s
|
||||||
|
|
||||||
// SendMail function to send mail
|
// SendMail function to send mail
|
||||||
func SendMail(to []string, Subject, bodyMessage string) error {
|
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 := gomail.NewMessage()
|
||||||
m.SetHeader("From", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeySenderEmail))
|
m.SetHeader("From", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeySenderEmail))
|
||||||
m.SetHeader("To", to...)
|
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)
|
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
||||||
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
|
|
||||||
|
decryptedConfigs, err := crypto.DecryptAES(env.EnvData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
decryptedConfigs, err := crypto.DecryptAES([]byte(b64DecryptedConfig))
|
err = json.Unmarshal([]byte(decryptedConfigs), &result)
|
||||||
if err != nil {
|
|
||||||
return result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal(decryptedConfigs, &result)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
@ -85,12 +81,8 @@ func PersistEnv() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -98,7 +90,7 @@ func PersistEnv() error {
|
||||||
// temp store variable
|
// temp store variable
|
||||||
var storeData envstore.Store
|
var storeData envstore.Store
|
||||||
|
|
||||||
err = json.Unmarshal(decryptedConfigs, &storeData)
|
err = json.Unmarshal([]byte(decryptedConfigs), &storeData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,8 +45,10 @@ type DirectiveRoot struct {
|
||||||
type ComplexityRoot struct {
|
type ComplexityRoot struct {
|
||||||
AuthResponse struct {
|
AuthResponse struct {
|
||||||
AccessToken func(childComplexity int) int
|
AccessToken func(childComplexity int) int
|
||||||
ExpiresAt func(childComplexity int) int
|
ExpiresIn func(childComplexity int) int
|
||||||
|
IDToken func(childComplexity int) int
|
||||||
Message func(childComplexity int) int
|
Message func(childComplexity int) int
|
||||||
|
RefreshToken func(childComplexity int) int
|
||||||
User func(childComplexity int) int
|
User func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +136,6 @@ type ComplexityRoot struct {
|
||||||
Query struct {
|
Query struct {
|
||||||
AdminSession func(childComplexity int) int
|
AdminSession func(childComplexity int) int
|
||||||
Env func(childComplexity int) int
|
Env func(childComplexity int) int
|
||||||
IsValidJwt func(childComplexity int, params *model.IsValidJWTQueryInput) int
|
|
||||||
Meta func(childComplexity int) int
|
Meta func(childComplexity int) int
|
||||||
Profile func(childComplexity int) int
|
Profile func(childComplexity int) int
|
||||||
Session func(childComplexity int, params *model.SessionQueryInput) int
|
Session func(childComplexity int, params *model.SessionQueryInput) int
|
||||||
|
@ -171,11 +172,6 @@ type ComplexityRoot struct {
|
||||||
Users func(childComplexity int) int
|
Users func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidJWTResponse struct {
|
|
||||||
Message func(childComplexity int) int
|
|
||||||
Valid func(childComplexity int) int
|
|
||||||
}
|
|
||||||
|
|
||||||
VerificationRequest struct {
|
VerificationRequest struct {
|
||||||
CreatedAt func(childComplexity int) int
|
CreatedAt func(childComplexity int) int
|
||||||
Email func(childComplexity int) int
|
Email func(childComplexity int) int
|
||||||
|
@ -212,7 +208,6 @@ type MutationResolver interface {
|
||||||
type QueryResolver interface {
|
type QueryResolver interface {
|
||||||
Meta(ctx context.Context) (*model.Meta, error)
|
Meta(ctx context.Context) (*model.Meta, error)
|
||||||
Session(ctx context.Context, params *model.SessionQueryInput) (*model.AuthResponse, 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)
|
Profile(ctx context.Context) (*model.User, error)
|
||||||
Users(ctx context.Context, params *model.PaginatedInput) (*model.Users, error)
|
Users(ctx context.Context, params *model.PaginatedInput) (*model.Users, error)
|
||||||
VerificationRequests(ctx context.Context, params *model.PaginatedInput) (*model.VerificationRequests, 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
|
return e.complexity.AuthResponse.AccessToken(childComplexity), true
|
||||||
|
|
||||||
case "AuthResponse.expires_at":
|
case "AuthResponse.expires_in":
|
||||||
if e.complexity.AuthResponse.ExpiresAt == nil {
|
if e.complexity.AuthResponse.ExpiresIn == nil {
|
||||||
break
|
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":
|
case "AuthResponse.message":
|
||||||
if e.complexity.AuthResponse.Message == nil {
|
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
|
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":
|
case "AuthResponse.user":
|
||||||
if e.complexity.AuthResponse.User == nil {
|
if e.complexity.AuthResponse.User == nil {
|
||||||
break
|
break
|
||||||
|
@ -804,18 +813,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.Query.Env(childComplexity), true
|
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":
|
case "Query.meta":
|
||||||
if e.complexity.Query.Meta == nil {
|
if e.complexity.Query.Meta == nil {
|
||||||
break
|
break
|
||||||
|
@ -1006,20 +1003,6 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.Users.Users(childComplexity), true
|
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":
|
case "VerificationRequest.created_at":
|
||||||
if e.complexity.VerificationRequest.CreatedAt == nil {
|
if e.complexity.VerificationRequest.CreatedAt == nil {
|
||||||
break
|
break
|
||||||
|
@ -1221,7 +1204,9 @@ type Error {
|
||||||
type AuthResponse {
|
type AuthResponse {
|
||||||
message: String!
|
message: String!
|
||||||
access_token: String
|
access_token: String
|
||||||
expires_at: Int64
|
id_token: String
|
||||||
|
refresh_token: String
|
||||||
|
expires_in: Int64
|
||||||
user: User
|
user: User
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1229,11 +1214,6 @@ type Response {
|
||||||
message: String!
|
message: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidJWTResponse {
|
|
||||||
valid: Boolean!
|
|
||||||
message: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Env {
|
type Env {
|
||||||
ADMIN_SECRET: String
|
ADMIN_SECRET: String
|
||||||
DATABASE_NAME: String!
|
DATABASE_NAME: String!
|
||||||
|
@ -1337,6 +1317,7 @@ input LoginInput {
|
||||||
email: String!
|
email: String!
|
||||||
password: String!
|
password: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
|
scope: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input VerifyEmailInput {
|
input VerifyEmailInput {
|
||||||
|
@ -1395,15 +1376,12 @@ input DeleteUserInput {
|
||||||
input MagicLinkLoginInput {
|
input MagicLinkLoginInput {
|
||||||
email: String!
|
email: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
|
scope: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input SessionQueryInput {
|
input SessionQueryInput {
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
}
|
scope: [String!]
|
||||||
|
|
||||||
input IsValidJWTQueryInput {
|
|
||||||
jwt: String
|
|
||||||
roles: [String!]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input PaginationInput {
|
input PaginationInput {
|
||||||
|
@ -1437,7 +1415,6 @@ type Mutation {
|
||||||
type Query {
|
type Query {
|
||||||
meta: Meta!
|
meta: Meta!
|
||||||
session(params: SessionQueryInput): AuthResponse!
|
session(params: SessionQueryInput): AuthResponse!
|
||||||
is_valid_jwt(params: IsValidJWTQueryInput): ValidJWTResponse!
|
|
||||||
profile: User!
|
profile: User!
|
||||||
# admin only apis
|
# admin only apis
|
||||||
_users(params: PaginatedInput): Users!
|
_users(params: PaginatedInput): Users!
|
||||||
|
@ -1693,21 +1670,6 @@ func (ec *executionContext) field_Query__verification_requests_args(ctx context.
|
||||||
return args, nil
|
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) {
|
func (ec *executionContext) field_Query_session_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
var err error
|
var err error
|
||||||
args := map[string]interface{}{}
|
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)
|
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() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
ec.Error(ctx, ec.Recover(ctx, r))
|
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)
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
ctx = rctx // use context from middleware stack in children
|
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 {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
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)
|
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) {
|
func (ec *executionContext) _Query_profile(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
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)
|
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) {
|
func (ec *executionContext) _VerificationRequest_id(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequest) (ret graphql.Marshaler) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
|
@ -6821,37 +6735,6 @@ func (ec *executionContext) unmarshalInputForgotPasswordInput(ctx context.Contex
|
||||||
return it, nil
|
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) {
|
func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj interface{}) (model.LoginInput, error) {
|
||||||
var it model.LoginInput
|
var it model.LoginInput
|
||||||
asMap := map[string]interface{}{}
|
asMap := map[string]interface{}{}
|
||||||
|
@ -6885,6 +6768,14 @@ func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj in
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it, err
|
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 {
|
if err != nil {
|
||||||
return it, err
|
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 {
|
if err != nil {
|
||||||
return it, err
|
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":
|
case "access_token":
|
||||||
out.Values[i] = ec._AuthResponse_access_token(ctx, field, obj)
|
out.Values[i] = ec._AuthResponse_access_token(ctx, field, obj)
|
||||||
case "expires_at":
|
case "id_token":
|
||||||
out.Values[i] = ec._AuthResponse_expires_at(ctx, field, obj)
|
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":
|
case "user":
|
||||||
out.Values[i] = ec._AuthResponse_user(ctx, field, obj)
|
out.Values[i] = ec._AuthResponse_user(ctx, field, obj)
|
||||||
default:
|
default:
|
||||||
|
@ -8136,20 +8047,6 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
|
||||||
}
|
}
|
||||||
return res
|
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":
|
case "profile":
|
||||||
field := field
|
field := field
|
||||||
out.Concurrently(i, func() (res graphql.Marshaler) {
|
out.Concurrently(i, func() (res graphql.Marshaler) {
|
||||||
|
@ -8365,38 +8262,6 @@ func (ec *executionContext) _Users(ctx context.Context, sel ast.SelectionSet, ob
|
||||||
return out
|
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"}
|
var verificationRequestImplementors = []string{"VerificationRequest"}
|
||||||
|
|
||||||
func (ec *executionContext) _VerificationRequest(ctx context.Context, sel ast.SelectionSet, obj *model.VerificationRequest) graphql.Marshaler {
|
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)
|
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 {
|
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))
|
ret := make(graphql.Array, len(v))
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
@ -9395,14 +9246,6 @@ func (ec *executionContext) marshalOInt642ᚖint64(ctx context.Context, sel ast.
|
||||||
return graphql.MarshalInt64(*v)
|
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) {
|
func (ec *executionContext) unmarshalOPaginatedInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐPaginatedInput(ctx context.Context, v interface{}) (*model.PaginatedInput, error) {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
|
@ -13,7 +13,9 @@ type AdminSignupInput struct {
|
||||||
type AuthResponse struct {
|
type AuthResponse struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
AccessToken *string `json:"access_token"`
|
AccessToken *string `json:"access_token"`
|
||||||
ExpiresAt *int64 `json:"expires_at"`
|
IDToken *string `json:"id_token"`
|
||||||
|
RefreshToken *string `json:"refresh_token"`
|
||||||
|
ExpiresIn *int64 `json:"expires_in"`
|
||||||
User *User `json:"user"`
|
User *User `json:"user"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,20 +72,17 @@ type ForgotPasswordInput struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type IsValidJWTQueryInput struct {
|
|
||||||
Jwt *string `json:"jwt"`
|
|
||||||
Roles []string `json:"roles"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type LoginInput struct {
|
type LoginInput struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
|
Scope []string `json:"scope"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MagicLinkLoginInput struct {
|
type MagicLinkLoginInput struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
|
Scope []string `json:"scope"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Meta struct {
|
type Meta struct {
|
||||||
|
@ -130,6 +129,7 @@ type Response struct {
|
||||||
|
|
||||||
type SessionQueryInput struct {
|
type SessionQueryInput struct {
|
||||||
Roles []string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
|
Scope []string `json:"scope"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SignUpInput struct {
|
type SignUpInput struct {
|
||||||
|
@ -238,11 +238,6 @@ type Users struct {
|
||||||
Users []*User `json:"users"`
|
Users []*User `json:"users"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidJWTResponse struct {
|
|
||||||
Valid bool `json:"valid"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type VerificationRequest struct {
|
type VerificationRequest struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Identifier *string `json:"identifier"`
|
Identifier *string `json:"identifier"`
|
||||||
|
|
|
@ -72,7 +72,9 @@ type Error {
|
||||||
type AuthResponse {
|
type AuthResponse {
|
||||||
message: String!
|
message: String!
|
||||||
access_token: String
|
access_token: String
|
||||||
expires_at: Int64
|
id_token: String
|
||||||
|
refresh_token: String
|
||||||
|
expires_in: Int64
|
||||||
user: User
|
user: User
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,11 +82,6 @@ type Response {
|
||||||
message: String!
|
message: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValidJWTResponse {
|
|
||||||
valid: Boolean!
|
|
||||||
message: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Env {
|
type Env {
|
||||||
ADMIN_SECRET: String
|
ADMIN_SECRET: String
|
||||||
DATABASE_NAME: String!
|
DATABASE_NAME: String!
|
||||||
|
@ -188,6 +185,7 @@ input LoginInput {
|
||||||
email: String!
|
email: String!
|
||||||
password: String!
|
password: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
|
scope: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input VerifyEmailInput {
|
input VerifyEmailInput {
|
||||||
|
@ -246,15 +244,12 @@ input DeleteUserInput {
|
||||||
input MagicLinkLoginInput {
|
input MagicLinkLoginInput {
|
||||||
email: String!
|
email: String!
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
|
scope: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input SessionQueryInput {
|
input SessionQueryInput {
|
||||||
roles: [String!]
|
roles: [String!]
|
||||||
}
|
scope: [String!]
|
||||||
|
|
||||||
input IsValidJWTQueryInput {
|
|
||||||
jwt: String
|
|
||||||
roles: [String!]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input PaginationInput {
|
input PaginationInput {
|
||||||
|
@ -288,7 +283,6 @@ type Mutation {
|
||||||
type Query {
|
type Query {
|
||||||
meta: Meta!
|
meta: Meta!
|
||||||
session(params: SessionQueryInput): AuthResponse!
|
session(params: SessionQueryInput): AuthResponse!
|
||||||
is_valid_jwt(params: IsValidJWTQueryInput): ValidJWTResponse!
|
|
||||||
profile: User!
|
profile: User!
|
||||||
# admin only apis
|
# admin only apis
|
||||||
_users(params: PaginatedInput): Users!
|
_users(params: PaginatedInput): Users!
|
||||||
|
|
|
@ -79,10 +79,6 @@ func (r *queryResolver) Session(ctx context.Context, params *model.SessionQueryI
|
||||||
return resolvers.SessionResolver(ctx, params)
|
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) {
|
func (r *queryResolver) Profile(ctx context.Context) (*model.User, error) {
|
||||||
return resolvers.ProfileResolver(ctx)
|
return resolvers.ProfileResolver(ctx)
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,11 +144,13 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
authToken, _ := token.CreateAuthToken(user, inputRoles)
|
// TODO use query param
|
||||||
sessionstore.SetUserSession(user.ID, authToken.FingerPrint, authToken.RefreshToken.Token)
|
scope := []string{"openid", "email", "profile"}
|
||||||
cookie.SetCookie(c, authToken.AccessToken.Token, authToken.RefreshToken.Token, authToken.FingerPrintHash)
|
authToken, _ := token.CreateAuthToken(c, user, inputRoles, scope)
|
||||||
utils.SaveSessionInDB(user.ID, c)
|
|
||||||
|
|
||||||
|
sessionstore.SetState(authToken.FingerPrint, user.ID)
|
||||||
|
cookie.SetSession(c, authToken.FingerPrintHash)
|
||||||
|
go utils.SaveSessionInDB(c, user.ID)
|
||||||
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -227,7 +229,7 @@ func processGithubUserInfo(code string) (models.User, error) {
|
||||||
GivenName: &firstName,
|
GivenName: &firstName,
|
||||||
FamilyName: &lastName,
|
FamilyName: &lastName,
|
||||||
Picture: &picture,
|
Picture: &picture,
|
||||||
Email: userRawData["email"],
|
Email: userRawData["sub"],
|
||||||
}
|
}
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
|
@ -260,7 +262,7 @@ func processFacebookUserInfo(code string) (models.User, error) {
|
||||||
userRawData := make(map[string]interface{})
|
userRawData := make(map[string]interface{})
|
||||||
json.Unmarshal(body, &userRawData)
|
json.Unmarshal(body, &userRawData)
|
||||||
|
|
||||||
email := fmt.Sprintf("%v", userRawData["email"])
|
email := fmt.Sprintf("%v", userRawData["sub"])
|
||||||
|
|
||||||
picObject := userRawData["picture"].(map[string]interface{})["data"]
|
picObject := userRawData["picture"].(map[string]interface{})["data"]
|
||||||
picDataObject := picObject.(map[string]interface{})
|
picDataObject := picObject.(map[string]interface{})
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/authorizerdev/authorizer/server/token"
|
"github.com/authorizerdev/authorizer/server/token"
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// VerifyEmailHandler handles the verify email route.
|
// VerifyEmailHandler handles the verify email route.
|
||||||
|
@ -18,7 +19,7 @@ import (
|
||||||
func VerifyEmailHandler() gin.HandlerFunc {
|
func VerifyEmailHandler() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
errorRes := gin.H{
|
errorRes := gin.H{
|
||||||
"message": "invalid token",
|
"error": "invalid token",
|
||||||
}
|
}
|
||||||
tokenInQuery := c.Query("token")
|
tokenInQuery := c.Query("token")
|
||||||
if tokenInQuery == "" {
|
if tokenInQuery == "" {
|
||||||
|
@ -33,13 +34,21 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify if token exists in db
|
// 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 {
|
if err != nil {
|
||||||
c.JSON(400, errorRes)
|
c.JSON(400, errorRes)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := db.Provider.GetUserByEmail(claim["email"].(string))
|
user, err := db.Provider.GetUserByEmail(claim["sub"].(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(400, gin.H{
|
c.JSON(400, gin.H{
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
|
@ -57,16 +66,19 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
||||||
db.Provider.DeleteVerificationRequest(verificationRequest)
|
db.Provider.DeleteVerificationRequest(verificationRequest)
|
||||||
|
|
||||||
roles := strings.Split(user.Roles, ",")
|
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 {
|
if err != nil {
|
||||||
c.JSON(400, gin.H{
|
c.JSON(400, gin.H{
|
||||||
"message": err.Error(),
|
"message": err.Error(),
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sessionstore.SetUserSession(user.ID, authToken.FingerPrint, authToken.RefreshToken.Token)
|
sessionstore.SetState(authToken, nonce+"@"+user.ID)
|
||||||
cookie.SetCookie(c, authToken.AccessToken.Token, authToken.RefreshToken.Token, authToken.FingerPrintHash)
|
cookie.SetSession(c, authToken)
|
||||||
utils.SaveSessionInDB(user.ID, c)
|
|
||||||
|
go utils.SaveSessionInDB(c, user.ID)
|
||||||
|
|
||||||
c.Redirect(http.StatusTemporaryRedirect, claim["redirect_url"].(string))
|
c.Redirect(http.StatusTemporaryRedirect, claim["redirect_url"].(string))
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ func DeleteUserResolver(ctx context.Context, params model.DeleteUserInput) (*mod
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionstore.DeleteAllUserSession(fmt.Sprintf("%x", user.ID))
|
go sessionstore.DeleteAllUserSession(fmt.Sprintf("%x", user.ID))
|
||||||
|
|
||||||
err = db.Provider.DeleteUser(user)
|
err = db.Provider.DeleteUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -39,7 +39,11 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
|
||||||
}
|
}
|
||||||
|
|
||||||
hostname := utils.GetHost(gc)
|
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 {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
|
@ -48,12 +52,11 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
|
||||||
Identifier: constants.VerificationTypeForgotPassword,
|
Identifier: constants.VerificationTypeForgotPassword,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
|
Nonce: nonce,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
go func() {
|
go email.SendForgotPasswordMail(params.Email, verificationToken, hostname)
|
||||||
email.SendForgotPasswordMail(params.Email, verificationToken, hostname)
|
|
||||||
}()
|
|
||||||
|
|
||||||
res = &model.Response{
|
res = &model.Response{
|
||||||
Message: `Please check your inbox! We have sent a password reset link.`,
|
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
|
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 {
|
if err != nil {
|
||||||
return res, err
|
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{
|
res = &model.AuthResponse{
|
||||||
Message: `Logged in successfully`,
|
Message: `Logged in successfully`,
|
||||||
AccessToken: &authToken.AccessToken.Token,
|
AccessToken: &authToken.AccessToken.Token,
|
||||||
ExpiresAt: &authToken.AccessToken.ExpiresAt,
|
IDToken: &authToken.IDToken.Token,
|
||||||
|
ExpiresIn: &expiresIn,
|
||||||
User: user.AsAPIUser(),
|
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
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
"github.com/authorizerdev/authorizer/server/crypto"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/sessionstore"
|
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||||
"github.com/authorizerdev/authorizer/server/token"
|
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,34 +18,21 @@ func LogoutResolver(ctx context.Context) (*model.Response, error) {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// get refresh token
|
|
||||||
refreshToken, err := token.GetRefreshToken(gc)
|
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// get fingerprint hash
|
// get fingerprint hash
|
||||||
fingerprintHash, err := token.GetFingerPrint(gc)
|
fingerprintHash, err := cookie.GetSession(gc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
decryptedFingerPrint, err := crypto.DecryptAES([]byte(fingerprintHash))
|
decryptedFingerPrint, err := crypto.DecryptAES(fingerprintHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fingerPrint := string(decryptedFingerPrint)
|
fingerPrint := string(decryptedFingerPrint)
|
||||||
|
|
||||||
// verify refresh token and fingerprint
|
sessionstore.RemoveState(fingerPrint)
|
||||||
claims, err := token.ParseJWTToken(refreshToken)
|
cookie.DeleteSession(gc)
|
||||||
if err != nil {
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
userID := claims["id"].(string)
|
|
||||||
sessionstore.DeleteUserSession(userID, fingerPrint)
|
|
||||||
cookie.DeleteCookie(gc)
|
|
||||||
|
|
||||||
res = &model.Response{
|
res = &model.Response{
|
||||||
Message: "Logged out successfully",
|
Message: "Logged out successfully",
|
||||||
|
|
|
@ -109,8 +109,12 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
|
||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
||||||
// insert verification request
|
// insert verification request
|
||||||
|
nonce, nonceHash, err := utils.GenerateNonce()
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
verificationType := constants.VerificationTypeMagicLinkLogin
|
verificationType := constants.VerificationTypeMagicLinkLogin
|
||||||
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname)
|
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname, nonceHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
|
@ -119,12 +123,11 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
|
||||||
Identifier: verificationType,
|
Identifier: verificationType,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
|
Nonce: nonce,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
go func() {
|
go email.SendVerificationMail(params.Email, verificationToken, hostname)
|
||||||
email.SendVerificationMail(params.Email, verificationToken, hostname)
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res = &model.Response{
|
res = &model.Response{
|
||||||
|
|
|
@ -2,7 +2,6 @@ package resolvers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
@ -18,13 +17,17 @@ func ProfileResolver(ctx context.Context) (*model.User, error) {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
claims, err := token.ValidateAccessToken(gc)
|
accessToken, err := token.GetAccessToken(gc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
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)
|
user, err := db.Provider.GetUserByID(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
|
|
|
@ -44,7 +44,11 @@ func ResendVerifyEmailResolver(ctx context.Context, params model.ResendVerifyEma
|
||||||
}
|
}
|
||||||
|
|
||||||
hostname := utils.GetHost(gc)
|
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 {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
|
@ -53,12 +57,11 @@ func ResendVerifyEmailResolver(ctx context.Context, params model.ResendVerifyEma
|
||||||
Identifier: params.Identifier,
|
Identifier: params.Identifier,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
|
Nonce: nonce,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
go func() {
|
go email.SendVerificationMail(params.Email, verificationToken, hostname)
|
||||||
email.SendVerificationMail(params.Email, verificationToken, hostname)
|
|
||||||
}()
|
|
||||||
|
|
||||||
res = &model.Response{
|
res = &model.Response{
|
||||||
Message: `Verification email has been sent. Please check your inbox`,
|
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/envstore"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/token"
|
"github.com/authorizerdev/authorizer/server/token"
|
||||||
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ResetPasswordResolver is a resolver for reset password mutation
|
// ResetPasswordResolver is a resolver for reset password mutation
|
||||||
func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error) {
|
func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error) {
|
||||||
var res *model.Response
|
var res *model.Response
|
||||||
|
gc, err := utils.GinContextFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
if envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication) {
|
if envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication) {
|
||||||
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
|
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
|
// 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 {
|
if err != nil {
|
||||||
return res, fmt.Errorf(`invalid token`)
|
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 {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/cookie"
|
"github.com/authorizerdev/authorizer/server/cookie"
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/sessionstore"
|
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||||
|
@ -14,6 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// SessionResolver is a resolver for session query
|
// 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) {
|
func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*model.AuthResponse, error) {
|
||||||
var res *model.AuthResponse
|
var res *model.AuthResponse
|
||||||
|
|
||||||
|
@ -22,48 +22,27 @@ func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*mod
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// get refresh token
|
sessionToken, err := cookie.GetSession(gc)
|
||||||
refreshToken, err := token.GetRefreshToken(gc)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// get fingerprint hash
|
// get session from cookie
|
||||||
fingerprintHash, err := token.GetFingerPrint(gc)
|
claims, err := token.ValidateBrowserSession(gc, sessionToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
userID := claims.Subject
|
||||||
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`)
|
|
||||||
}
|
|
||||||
|
|
||||||
user, err := db.Provider.GetUserByID(userID)
|
user, err := db.Provider.GetUserByID(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// refresh token has "roles" as claim
|
// refresh token has "roles" as claim
|
||||||
claimRoleInterface := claims["roles"].([]interface{})
|
claimRoleInterface := claims.Roles
|
||||||
claimRoles := []string{}
|
claimRoles := []string{}
|
||||||
for _, v := range claimRoleInterface {
|
for _, v := range claimRoleInterface {
|
||||||
claimRoles = append(claimRoles, v.(string))
|
claimRoles = append(claimRoles, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if params != nil && params.Roles != nil && len(params.Roles) > 0 {
|
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
|
scope := []string{"openid", "email", "profile"}
|
||||||
sessionstore.DeleteUserSession(userID, fingerPrint)
|
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 {
|
if err != nil {
|
||||||
return res, err
|
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{
|
res = &model.AuthResponse{
|
||||||
Message: `Session token refreshed`,
|
Message: `Session token refreshed`,
|
||||||
AccessToken: &authToken.AccessToken.Token,
|
AccessToken: &authToken.AccessToken.Token,
|
||||||
ExpiresAt: &authToken.AccessToken.ExpiresAt,
|
ExpiresIn: &expiresIn,
|
||||||
|
IDToken: &authToken.IDToken.Token,
|
||||||
User: user.AsAPIUser(),
|
User: user.AsAPIUser(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if authToken.RefreshToken != nil {
|
||||||
|
res.RefreshToken = &authToken.RefreshToken.Token
|
||||||
|
sessionstore.SetState(authToken.AccessToken.Token, authToken.FingerPrint+"@"+user.ID)
|
||||||
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,41 +123,48 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
|
||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
||||||
// insert verification request
|
// insert verification request
|
||||||
verificationType := constants.VerificationTypeBasicAuthSignup
|
nonce, nonceHash, err := utils.GenerateNonce()
|
||||||
verificationToken, err := token.CreateVerificationToken(params.Email, verificationType, hostname)
|
|
||||||
if err != nil {
|
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{
|
db.Provider.AddVerificationRequest(models.VerificationRequest{
|
||||||
Token: verificationToken,
|
Token: verificationToken,
|
||||||
Identifier: verificationType,
|
Identifier: verificationType,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: params.Email,
|
Email: params.Email,
|
||||||
|
Nonce: nonce,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
go func() {
|
go email.SendVerificationMail(params.Email, verificationToken, hostname)
|
||||||
email.SendVerificationMail(params.Email, verificationToken, hostname)
|
|
||||||
}()
|
|
||||||
|
|
||||||
res = &model.AuthResponse{
|
res = &model.AuthResponse{
|
||||||
Message: `Verification email has been sent. Please check your inbox`,
|
Message: `Verification email has been sent. Please check your inbox`,
|
||||||
User: userToReturn,
|
User: userToReturn,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
scope := []string{"openid", "email", "profile"}
|
||||||
|
|
||||||
authToken, err := token.CreateAuthToken(user, roles)
|
authToken, err := token.CreateAuthToken(gc, user, roles, scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
sessionstore.SetUserSession(user.ID, authToken.FingerPrint, authToken.RefreshToken.Token)
|
|
||||||
cookie.SetCookie(gc, authToken.AccessToken.Token, authToken.RefreshToken.Token, authToken.FingerPrintHash)
|
sessionstore.SetState(authToken.FingerPrintHash, authToken.FingerPrint+"@"+user.ID)
|
||||||
utils.SaveSessionInDB(user.ID, gc)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
|
go utils.SaveSessionInDB(gc, user.ID)
|
||||||
|
|
||||||
|
expiresIn := int64(1800)
|
||||||
|
|
||||||
res = &model.AuthResponse{
|
res = &model.AuthResponse{
|
||||||
Message: `Signed up successfully.`,
|
Message: `Signed up successfully.`,
|
||||||
AccessToken: &authToken.AccessToken.Token,
|
AccessToken: &authToken.AccessToken.Token,
|
||||||
ExpiresAt: &authToken.AccessToken.ExpiresAt,
|
ExpiresIn: &expiresIn,
|
||||||
User: userToReturn,
|
User: userToReturn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/email"
|
"github.com/authorizerdev/authorizer/server/email"
|
||||||
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/sessionstore"
|
"github.com/authorizerdev/authorizer/server/sessionstore"
|
||||||
"github.com/authorizerdev/authorizer/server/token"
|
"github.com/authorizerdev/authorizer/server/token"
|
||||||
|
@ -28,7 +29,11 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
|
||||||
return res, err
|
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 {
|
if err != nil {
|
||||||
return res, err
|
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")
|
return res, fmt.Errorf("please enter at least one param to update")
|
||||||
}
|
}
|
||||||
|
|
||||||
userEmail := fmt.Sprintf("%v", claims["email"])
|
userID := claims["sub"].(string)
|
||||||
user, err := db.Provider.GetUserByEmail(userEmail)
|
user, err := db.Provider.GetUserByID(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
@ -108,22 +113,28 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
|
||||||
newEmail := strings.ToLower(*params.Email)
|
newEmail := strings.ToLower(*params.Email)
|
||||||
// check if user with new email exists
|
// check if user with new email exists
|
||||||
_, err := db.Provider.GetUserByEmail(newEmail)
|
_, err := db.Provider.GetUserByEmail(newEmail)
|
||||||
|
|
||||||
// err = nil means user exists
|
// err = nil means user exists
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return res, fmt.Errorf("user with this email address already exists")
|
return res, fmt.Errorf("user with this email address already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionstore.DeleteAllUserSession(fmt.Sprintf("%v", user.ID))
|
// TODO figure out how to delete all user sessions
|
||||||
cookie.DeleteCookie(gc)
|
go sessionstore.DeleteAllUserSession(user.ID)
|
||||||
|
|
||||||
hostname := utils.GetHost(gc)
|
cookie.DeleteSession(gc)
|
||||||
user.Email = newEmail
|
user.Email = newEmail
|
||||||
|
|
||||||
|
if !envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
|
||||||
|
hostname := utils.GetHost(gc)
|
||||||
user.EmailVerifiedAt = nil
|
user.EmailVerifiedAt = nil
|
||||||
hasEmailChanged = true
|
hasEmailChanged = true
|
||||||
// insert verification request
|
// insert verification request
|
||||||
|
nonce, nonceHash, err := utils.GenerateNonce()
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
verificationType := constants.VerificationTypeUpdateEmail
|
verificationType := constants.VerificationTypeUpdateEmail
|
||||||
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname)
|
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname, nonceHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
|
@ -132,14 +143,14 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
|
||||||
Identifier: verificationType,
|
Identifier: verificationType,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: newEmail,
|
Email: newEmail,
|
||||||
|
Nonce: nonce,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
go func() {
|
go email.SendVerificationMail(newEmail, verificationToken, hostname)
|
||||||
email.SendVerificationMail(newEmail, verificationToken, hostname)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
_, err = db.Provider.UpdateUser(user)
|
_, err = db.Provider.UpdateUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("error updating user:", err)
|
log.Println("error updating user:", err)
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/cookie"
|
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/db/models"
|
"github.com/authorizerdev/authorizer/server/db/models"
|
||||||
"github.com/authorizerdev/authorizer/server/email"
|
"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")
|
return res, fmt.Errorf("user with this email address already exists")
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionstore.DeleteAllUserSession(fmt.Sprintf("%v", user.ID))
|
// TODO figure out how to do this
|
||||||
cookie.DeleteCookie(gc)
|
go sessionstore.DeleteAllUserSession(user.ID)
|
||||||
|
|
||||||
hostname := utils.GetHost(gc)
|
hostname := utils.GetHost(gc)
|
||||||
user.Email = newEmail
|
user.Email = newEmail
|
||||||
user.EmailVerifiedAt = nil
|
user.EmailVerifiedAt = nil
|
||||||
// insert verification request
|
// insert verification request
|
||||||
|
nonce, nonceHash, err := utils.GenerateNonce()
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
verificationType := constants.VerificationTypeUpdateEmail
|
verificationType := constants.VerificationTypeUpdateEmail
|
||||||
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname)
|
verificationToken, err := token.CreateVerificationToken(newEmail, verificationType, hostname, nonceHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(`error generating token`, err)
|
log.Println(`error generating token`, err)
|
||||||
}
|
}
|
||||||
|
@ -112,12 +115,12 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
|
||||||
Identifier: verificationType,
|
Identifier: verificationType,
|
||||||
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
|
||||||
Email: newEmail,
|
Email: newEmail,
|
||||||
|
Nonce: nonce,
|
||||||
})
|
})
|
||||||
|
|
||||||
// exec it as go routin so that we can reduce the api latency
|
// exec it as go routin so that we can reduce the api latency
|
||||||
go func() {
|
go email.SendVerificationMail(newEmail, verificationToken, hostname)
|
||||||
email.SendVerificationMail(newEmail, verificationToken, hostname)
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rolesToSave := ""
|
rolesToSave := ""
|
||||||
|
@ -136,8 +139,7 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
|
||||||
rolesToSave = strings.Join(inputRoles, ",")
|
rolesToSave = strings.Join(inputRoles, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionstore.DeleteAllUserSession(fmt.Sprintf("%v", user.ID))
|
go sessionstore.DeleteAllUserSession(user.ID)
|
||||||
cookie.DeleteCookie(gc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if rolesToSave != "" {
|
if rolesToSave != "" {
|
||||||
|
|
|
@ -28,12 +28,17 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify if token exists in db
|
// 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 {
|
if err != nil {
|
||||||
return res, fmt.Errorf(`invalid token: %s`, err.Error())
|
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 {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
@ -41,25 +46,35 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
|
||||||
// update email_verified_at in users table
|
// update email_verified_at in users table
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
user.EmailVerifiedAt = &now
|
user.EmailVerifiedAt = &now
|
||||||
db.Provider.UpdateUser(user)
|
user, err = db.Provider.UpdateUser(user)
|
||||||
// delete from verification table
|
if err != nil {
|
||||||
db.Provider.DeleteVerificationRequest(verificationRequest)
|
return res, err
|
||||||
|
}
|
||||||
roles := strings.Split(user.Roles, ",")
|
// delete from verification table
|
||||||
authToken, err := token.CreateAuthToken(user, roles)
|
err = db.Provider.DeleteVerificationRequest(verificationRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
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{
|
res = &model.AuthResponse{
|
||||||
Message: `Email verified successfully.`,
|
Message: `Email verified successfully.`,
|
||||||
AccessToken: &authToken.AccessToken.Token,
|
AccessToken: &authToken.AccessToken.Token,
|
||||||
ExpiresAt: &authToken.AccessToken.ExpiresAt,
|
IDToken: &authToken.IDToken.Token,
|
||||||
|
ExpiresIn: &expiresIn,
|
||||||
User: user.AsAPIUser(),
|
User: user.AsAPIUser(),
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package sessionstore
|
package sessionstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,42 +12,6 @@ type InMemoryStore struct {
|
||||||
stateStore map[string]string
|
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.
|
// ClearStore clears the in-memory store.
|
||||||
func (c *InMemoryStore) ClearStore() {
|
func (c *InMemoryStore) ClearStore() {
|
||||||
c.mutex.Lock()
|
c.mutex.Lock()
|
||||||
|
@ -54,32 +19,29 @@ func (c *InMemoryStore) ClearStore() {
|
||||||
c.sessionStore = map[string]map[string]string{}
|
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.
|
// GetUserSessions returns all the user session token from the in-memory store.
|
||||||
func (c *InMemoryStore) GetUserSessions(userId string) map[string]string {
|
func (c *InMemoryStore) GetUserSessions(userId string) map[string]string {
|
||||||
// c.mutex.Lock()
|
// c.mutex.Lock()
|
||||||
// defer c.mutex.Unlock()
|
// defer c.mutex.Unlock()
|
||||||
|
res := map[string]string{}
|
||||||
sessionMap, ok := c.sessionStore[userId]
|
for k, v := range c.stateStore {
|
||||||
if !ok {
|
split := strings.Split(v, "@")
|
||||||
return nil
|
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.
|
// SetState sets the state in the in-memory store.
|
||||||
|
|
|
@ -2,8 +2,8 @@ package sessionstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RedisStore struct {
|
type RedisStore struct {
|
||||||
|
@ -11,32 +11,6 @@ type RedisStore struct {
|
||||||
store RedisSessionClient
|
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
|
// ClearStore clears the redis store for authorizer related tokens
|
||||||
func (c *RedisStore) ClearStore() {
|
func (c *RedisStore) ClearStore() {
|
||||||
err := c.store.Del(c.ctx, "authorizer_*").Err()
|
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.
|
// GetUserSessions returns all the user session token from the redis store.
|
||||||
func (c *RedisStore) GetUserSessions(userID string) map[string]string {
|
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 {
|
if err != nil {
|
||||||
log.Println("error getting token from redis store:", err)
|
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
|
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.
|
// SetState sets the state in redis store.
|
||||||
func (c *RedisStore) SetState(key, state string) {
|
func (c *RedisStore) SetState(key, value string) {
|
||||||
err := c.store.Set(c.ctx, key, state, 0).Err()
|
err := c.store.Set(c.ctx, key, value, 0).Err()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("Error saving redis token:", err)
|
log.Fatalln("Error saving redis token:", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,26 +22,6 @@ type SessionStore struct {
|
||||||
// reference to various session store instances
|
// reference to various session store instances
|
||||||
var SessionStoreObj SessionStore
|
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
|
// DeleteAllSessions deletes all the sessions from the session store
|
||||||
func DeleteAllUserSession(userId string) {
|
func DeleteAllUserSession(userId string) {
|
||||||
if SessionStoreObj.RedisMemoryStoreObj != nil {
|
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
|
// GetUserSessions returns all the user sessions from the session store
|
||||||
func GetUserSessions(userId string) map[string]string {
|
func GetUserSessions(userId string) map[string]string {
|
||||||
if SessionStoreObj.RedisMemoryStoreObj != nil {
|
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/envstore"
|
||||||
"github.com/authorizerdev/authorizer/server/token"
|
"github.com/authorizerdev/authorizer/server/token"
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt"
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,12 +19,17 @@ func TestJwt(t *testing.T) {
|
||||||
publicKey := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtPublicKey)
|
publicKey := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtPublicKey)
|
||||||
privateKey := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtPrivateKey)
|
privateKey := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtPrivateKey)
|
||||||
clientID := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID)
|
clientID := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID)
|
||||||
|
nonce := uuid.New().String()
|
||||||
|
hostname := "localhost"
|
||||||
|
subject := "test"
|
||||||
claims := jwt.MapClaims{
|
claims := jwt.MapClaims{
|
||||||
"exp": time.Now().Add(time.Minute * 30).Unix(),
|
"exp": time.Now().Add(time.Minute * 30).Unix(),
|
||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"email": "test@yopmail.com",
|
"email": "test@yopmail.com",
|
||||||
"sub": "test",
|
"sub": subject,
|
||||||
"aud": clientID,
|
"aud": clientID,
|
||||||
|
"nonce": nonce,
|
||||||
|
"iss": hostname,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("invalid jwt type", func(t *testing.T) {
|
t.Run("invalid jwt type", func(t *testing.T) {
|
||||||
|
@ -42,7 +48,7 @@ func TestJwt(t *testing.T) {
|
||||||
}
|
}
|
||||||
jwtToken, err := token.SignJWTToken(expiredClaims)
|
jwtToken, err := token.SignJWTToken(expiredClaims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
_, err = token.ParseJWTToken(jwtToken)
|
_, err = token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||||
assert.Error(t, err, err.Error(), "Token is expired")
|
assert.Error(t, err, err.Error(), "Token is expired")
|
||||||
})
|
})
|
||||||
t.Run("HMAC algorithms", func(t *testing.T) {
|
t.Run("HMAC algorithms", func(t *testing.T) {
|
||||||
|
@ -52,7 +58,7 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken)
|
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
})
|
})
|
||||||
|
@ -61,7 +67,7 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken)
|
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
})
|
})
|
||||||
|
@ -70,7 +76,7 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken)
|
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
})
|
})
|
||||||
|
@ -86,7 +92,7 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken)
|
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
})
|
})
|
||||||
|
@ -99,7 +105,7 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken)
|
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
})
|
})
|
||||||
|
@ -112,7 +118,7 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken)
|
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
})
|
})
|
||||||
|
@ -128,7 +134,7 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken)
|
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
})
|
})
|
||||||
|
@ -141,7 +147,7 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken)
|
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
})
|
})
|
||||||
|
@ -154,7 +160,7 @@ func TestJwt(t *testing.T) {
|
||||||
jwtToken, err := token.SignJWTToken(claims)
|
jwtToken, err := token.SignJWTToken(claims)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, jwtToken)
|
assert.NotEmpty(t, jwtToken)
|
||||||
c, err := token.ParseJWTToken(jwtToken)
|
c, err := token.ParseJWTToken(jwtToken, hostname, nonce, subject)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, c["email"].(string), claims["email"])
|
assert.Equal(t, c["email"].(string), claims["email"])
|
||||||
})
|
})
|
||||||
|
|
|
@ -5,14 +5,17 @@ import (
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||||
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func loginTests(t *testing.T, s TestSetup) {
|
func loginTests(t *testing.T, s TestSetup) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
t.Run(`should login`, func(t *testing.T) {
|
t.Run(`should login`, func(t *testing.T) {
|
||||||
|
t.Logf("=> is enabled: %v", envstore.EnvStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification))
|
||||||
_, ctx := createContext(s)
|
_, ctx := createContext(s)
|
||||||
email := "login." + s.TestInfo.Email
|
email := "login." + s.TestInfo.Email
|
||||||
_, err := resolvers.SignupResolver(ctx, model.SignUpInput{
|
_, err := resolvers.SignupResolver(ctx, model.SignUpInput{
|
||||||
|
@ -21,15 +24,19 @@ func loginTests(t *testing.T, s TestSetup) {
|
||||||
ConfirmPassword: s.TestInfo.Password,
|
ConfirmPassword: s.TestInfo.Password,
|
||||||
})
|
})
|
||||||
|
|
||||||
_, err = resolvers.LoginResolver(ctx, model.LoginInput{
|
res, err := resolvers.LoginResolver(ctx, model.LoginInput{
|
||||||
Email: email,
|
Email: email,
|
||||||
Password: s.TestInfo.Password,
|
Password: s.TestInfo.Password,
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.NotNil(t, err, "should fail because email is not verified")
|
assert.NotNil(t, err, "should fail because email is not verified")
|
||||||
|
assert.Nil(t, res)
|
||||||
verificationRequest, err := db.Provider.GetVerificationRequestByEmail(email, constants.VerificationTypeBasicAuthSignup)
|
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,
|
Token: verificationRequest.Token,
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
|
@ -2,11 +2,9 @@ package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
@ -30,18 +28,15 @@ func logoutTests(t *testing.T, s TestSetup) {
|
||||||
Token: verificationRequest.Token,
|
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
|
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)
|
req.Header.Set("Cookie", cookie)
|
||||||
_, err = resolvers.LogoutResolver(ctx)
|
_, err = resolvers.LogoutResolver(ctx)
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
package test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -27,12 +26,13 @@ func magicLinkLoginTests(t *testing.T, s TestSetup) {
|
||||||
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
|
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
|
||||||
Token: verificationRequest.Token,
|
Token: verificationRequest.Token,
|
||||||
})
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
token := *verifyRes.AccessToken
|
assert.NotNil(t, 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)
|
||||||
_, err = resolvers.ProfileResolver(ctx)
|
_, err = resolvers.ProfileResolver(ctx)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
s.GinContext.Request.Header.Set("Authorization", "")
|
||||||
cleanData(email)
|
cleanData(email)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
package test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -14,7 +13,7 @@ import (
|
||||||
|
|
||||||
func profileTests(t *testing.T, s TestSetup) {
|
func profileTests(t *testing.T, s TestSetup) {
|
||||||
t.Helper()
|
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)
|
req, ctx := createContext(s)
|
||||||
email := "profile." + s.TestInfo.Email
|
email := "profile." + s.TestInfo.Email
|
||||||
|
|
||||||
|
@ -31,11 +30,14 @@ func profileTests(t *testing.T, s TestSetup) {
|
||||||
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
|
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
|
||||||
Token: verificationRequest.Token,
|
Token: verificationRequest.Token,
|
||||||
})
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, verifyRes.AccessToken)
|
||||||
|
|
||||||
token := *verifyRes.AccessToken
|
s.GinContext.Request.Header.Set("Authorization", "Bearer "+*verifyRes.AccessToken)
|
||||||
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", token))
|
ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext)
|
||||||
profileRes, err := resolvers.ProfileResolver(ctx)
|
profileRes, err := resolvers.ProfileResolver(ctx)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
s.GinContext.Request.Header.Set("Authorization", "")
|
||||||
|
|
||||||
newEmail := *&profileRes.Email
|
newEmail := *&profileRes.Email
|
||||||
assert.Equal(t, email, newEmail, "emails should be equal")
|
assert.Equal(t, email, newEmail, "emails should be equal")
|
||||||
|
|
|
@ -15,15 +15,16 @@ func TestResolvers(t *testing.T) {
|
||||||
// constants.DbTypeArangodb: "http://localhost:8529",
|
// constants.DbTypeArangodb: "http://localhost:8529",
|
||||||
// constants.DbTypeMongodb: "mongodb://localhost:27017",
|
// constants.DbTypeMongodb: "mongodb://localhost:27017",
|
||||||
}
|
}
|
||||||
envstore.EnvStoreObj.ResetStore()
|
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyVersion, "test")
|
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyVersion, "test")
|
||||||
for dbType, dbURL := range databases {
|
for dbType, dbURL := range databases {
|
||||||
|
s := testSetup()
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseURL, dbURL)
|
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseURL, dbURL)
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseType, dbType)
|
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyDatabaseType, dbType)
|
||||||
|
|
||||||
s := testSetup()
|
|
||||||
defer s.Server.Close()
|
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
|
// clean the persisted config for test to use fresh config
|
||||||
envData, err := db.Provider.GetEnv()
|
envData, err := db.Provider.GetEnv()
|
||||||
|
@ -31,12 +32,10 @@ func TestResolvers(t *testing.T) {
|
||||||
envData.EnvData = ""
|
envData.EnvData = ""
|
||||||
db.Provider.UpdateEnv(envData)
|
db.Provider.UpdateEnv(envData)
|
||||||
}
|
}
|
||||||
err = env.InitAllEnv()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
env.PersistEnv()
|
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) {
|
t.Run("should pass tests for "+dbType, func(t *testing.T) {
|
||||||
// admin tests
|
// admin tests
|
||||||
adminSignupTests(t, s)
|
adminSignupTests(t, s)
|
||||||
|
@ -63,7 +62,6 @@ func TestResolvers(t *testing.T) {
|
||||||
magicLinkLoginTests(t, s)
|
magicLinkLoginTests(t, s)
|
||||||
logoutTests(t, s)
|
logoutTests(t, s)
|
||||||
metaTests(t, s)
|
metaTests(t, s)
|
||||||
isValidJWTTests(t, s)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,10 @@ package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/crypto"
|
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
"github.com/authorizerdev/authorizer/server/envstore"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
@ -36,17 +35,15 @@ func sessionTests(t *testing.T, s TestSetup) {
|
||||||
})
|
})
|
||||||
|
|
||||||
sessions := sessionstore.GetUserSessions(verifyRes.User.ID)
|
sessions := sessionstore.GetUserSessions(verifyRes.User.ID)
|
||||||
fingerPrint := ""
|
cookie := ""
|
||||||
refreshToken := ""
|
|
||||||
for key, val := range sessions {
|
|
||||||
fingerPrint = key
|
|
||||||
refreshToken = val
|
|
||||||
}
|
|
||||||
|
|
||||||
fingerPrintHash, _ := crypto.EncryptAES([]byte(fingerPrint))
|
|
||||||
|
|
||||||
token := *verifyRes.AccessToken
|
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)
|
req.Header.Set("Cookie", cookie)
|
||||||
|
|
||||||
|
|
|
@ -72,13 +72,13 @@ func testSetup() TestSetup {
|
||||||
}
|
}
|
||||||
|
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEnvPath, "../../.env.sample")
|
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.EnvKeySmtpHost, "smtp.yopmail.com")
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySmtpPort, "2525")
|
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySmtpPort, "2525")
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySmtpUsername, "lakhan@yopmail.com")
|
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySmtpUsername, "lakhan@yopmail.com")
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySmtpPassword, "test")
|
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySmtpPassword, "test")
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySenderEmail, "info@yopmail.com")
|
envstore.EnvStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeySenderEmail, "info@yopmail.com")
|
||||||
envstore.EnvStoreObj.UpdateEnvVariable(constants.SliceStoreIdentifier, constants.EnvKeyProtectedRoles, []string{"admin"})
|
envstore.EnvStoreObj.UpdateEnvVariable(constants.SliceStoreIdentifier, constants.EnvKeyProtectedRoles, []string{"admin"})
|
||||||
env.InitRequiredEnv()
|
|
||||||
db.InitDB()
|
db.InitDB()
|
||||||
env.InitAllEnv()
|
env.InitAllEnv()
|
||||||
sessionstore.InitSession()
|
sessionstore.InitSession()
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
package test
|
package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/envstore"
|
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
"github.com/authorizerdev/authorizer/server/resolvers"
|
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -34,18 +33,16 @@ func updateProfileTests(t *testing.T, s TestSetup) {
|
||||||
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
|
verifyRes, err := resolvers.VerifyEmailResolver(ctx, model.VerifyEmailInput{
|
||||||
Token: verificationRequest.Token,
|
Token: verificationRequest.Token,
|
||||||
})
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
token := *verifyRes.AccessToken
|
s.GinContext.Request.Header.Set("Authorization", "Bearer "+*verifyRes.AccessToken)
|
||||||
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", token))
|
ctx = context.WithValue(req.Context(), "GinContextKey", s.GinContext)
|
||||||
_, err = resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
|
|
||||||
FamilyName: &fName,
|
|
||||||
})
|
|
||||||
assert.Nil(t, err)
|
|
||||||
|
|
||||||
newEmail := "new_" + email
|
newEmail := "new_" + email
|
||||||
_, err = resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
|
_, err = resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
|
||||||
Email: &newEmail,
|
Email: &newEmail,
|
||||||
})
|
})
|
||||||
|
s.GinContext.Request.Header.Set("Authorization", "")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
_, err = resolvers.ProfileResolver(ctx)
|
_, err = resolvers.ProfileResolver(ctx)
|
||||||
assert.NotNil(t, err, "unauthorized")
|
assert.NotNil(t, err, "unauthorized")
|
||||||
|
|
|
@ -19,12 +19,15 @@ func verificationRequestsTest(t *testing.T, s TestSetup) {
|
||||||
req, ctx := createContext(s)
|
req, ctx := createContext(s)
|
||||||
|
|
||||||
email := "verification_requests." + s.TestInfo.Email
|
email := "verification_requests." + s.TestInfo.Email
|
||||||
resolvers.SignupResolver(ctx, model.SignUpInput{
|
res, err := resolvers.SignupResolver(ctx, model.SignUpInput{
|
||||||
Email: email,
|
Email: email,
|
||||||
Password: s.TestInfo.Password,
|
Password: s.TestInfo.Password,
|
||||||
ConfirmPassword: s.TestInfo.Password,
|
ConfirmPassword: s.TestInfo.Password,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, res)
|
||||||
|
|
||||||
limit := int64(10)
|
limit := int64(10)
|
||||||
page := int64(1)
|
page := int64(1)
|
||||||
pagination := &model.PaginatedInput{
|
pagination := &model.PaginatedInput{
|
||||||
|
|
|
@ -2,23 +2,23 @@ package token
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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/gin-gonic/gin"
|
||||||
"github.com/golang-jwt/jwt"
|
"github.com/golang-jwt/jwt"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/robertkrimen/otto"
|
"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
|
// JWTToken is a struct to hold JWT token and its expiration time
|
||||||
|
@ -33,60 +33,219 @@ type Token struct {
|
||||||
FingerPrintHash string `json:"fingerprint_hash"`
|
FingerPrintHash string `json:"fingerprint_hash"`
|
||||||
RefreshToken *JWTToken `json:"refresh_token"`
|
RefreshToken *JWTToken `json:"refresh_token"`
|
||||||
AccessToken *JWTToken `json:"access_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
|
// CreateAuthToken creates a new auth token when userlogs in
|
||||||
func CreateAuthToken(user models.User, roles []string) (*Token, error) {
|
func CreateAuthToken(gc *gin.Context, user models.User, roles, scope []string) (*Token, error) {
|
||||||
fingerprint := uuid.NewString()
|
hostname := utils.GetHost(gc)
|
||||||
fingerPrintHashBytes, err := crypto.EncryptAES([]byte(fingerprint))
|
nonce := uuid.New().String()
|
||||||
|
_, fingerPrintHash, err := CreateSessionToken(user, nonce, roles, scope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
refreshToken, refreshTokenExpiresAt, err := CreateRefreshToken(user, roles)
|
accessToken, accessTokenExpiresAt, err := CreateAccessToken(user, roles, scope, hostname, nonce)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
accessToken, accessTokenExpiresAt, err := CreateAccessToken(user, roles)
|
idToken, idTokenExpiresAt, err := CreateIDToken(user, roles, hostname, nonce)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Token{
|
res := &Token{
|
||||||
FingerPrint: fingerprint,
|
FingerPrint: nonce,
|
||||||
FingerPrintHash: string(fingerPrintHashBytes),
|
FingerPrintHash: fingerPrintHash,
|
||||||
RefreshToken: &JWTToken{Token: refreshToken, ExpiresAt: refreshTokenExpiresAt},
|
|
||||||
AccessToken: &JWTToken{Token: accessToken, ExpiresAt: accessTokenExpiresAt},
|
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
|
// 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
|
// expires in 1 year
|
||||||
expiryBound := time.Hour * 8760
|
expiryBound := time.Hour * 8760
|
||||||
expiresAt := time.Now().Add(expiryBound).Unix()
|
expiresAt := time.Now().Add(expiryBound).Unix()
|
||||||
|
|
||||||
customClaims := jwt.MapClaims{
|
customClaims := jwt.MapClaims{
|
||||||
"iss": "",
|
"iss": hostname,
|
||||||
"aud": "",
|
"aud": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID),
|
||||||
"sub": user.ID,
|
"sub": user.ID,
|
||||||
"exp": expiresAt,
|
"exp": expiresAt,
|
||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"token_type": constants.TokenTypeRefreshToken,
|
"token_type": constants.TokenTypeRefreshToken,
|
||||||
"roles": roles,
|
"roles": roles,
|
||||||
"id": user.ID,
|
"nonce": nonce,
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := SignJWTToken(customClaims)
|
token, err := SignJWTToken(customClaims)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", 0, err
|
return "", 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return token, expiresAt, nil
|
return token, expiresAt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateAccessToken util to create JWT token, based on
|
// CreateAccessToken util to create JWT token, based on
|
||||||
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
|
// 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
|
expiryBound := time.Minute * 30
|
||||||
expiresAt := time.Now().Add(expiryBound).Unix()
|
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)
|
claimKey := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtRoleClaim)
|
||||||
customClaims := jwt.MapClaims{
|
customClaims := jwt.MapClaims{
|
||||||
"iss": "",
|
"iss": hostname,
|
||||||
"aud": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID),
|
"aud": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID),
|
||||||
"nonce": "",
|
"nonce": nonce,
|
||||||
"sub": user.ID,
|
"sub": user.ID,
|
||||||
"exp": expiresAt,
|
"exp": expiresAt,
|
||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"token_type": constants.TokenTypeAccessToken,
|
"token_type": constants.TokenTypeIdentityToken,
|
||||||
"allowed_roles": strings.Split(user.Roles, ","),
|
"allowed_roles": strings.Split(user.Roles, ","),
|
||||||
claimKey: roles,
|
claimKey: roles,
|
||||||
}
|
}
|
||||||
|
@ -152,58 +311,18 @@ func CreateAccessToken(user models.User, roles []string) (string, int64, error)
|
||||||
return token, expiresAt, nil
|
return token, expiresAt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAccessToken returns the access token from the request (either from header or cookie)
|
// GetIDToken returns the id token from the request header
|
||||||
func GetAccessToken(gc *gin.Context) (string, error) {
|
func GetIDToken(gc *gin.Context) (string, error) {
|
||||||
token, err := cookie.GetAccessTokenCookie(gc)
|
|
||||||
if err != nil || token == "" {
|
|
||||||
// try to check in auth header for cookie
|
// try to check in auth header for cookie
|
||||||
auth := gc.Request.Header.Get("Authorization")
|
auth := gc.Request.Header.Get("Authorization")
|
||||||
if auth == "" {
|
if auth == "" {
|
||||||
return "", fmt.Errorf(`unauthorized`)
|
return "", fmt.Errorf(`unauthorized`)
|
||||||
}
|
}
|
||||||
|
|
||||||
token = strings.TrimPrefix(auth, "Bearer ")
|
if !strings.HasPrefix(auth, "Bearer ") {
|
||||||
|
return "", fmt.Errorf(`not a bearer token`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token := strings.TrimPrefix(auth, "Bearer ")
|
||||||
return token, nil
|
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 == "" {
|
|
||||||
return "", fmt.Errorf(`unauthorized`)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
// 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)
|
jwtType := envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtType)
|
||||||
signingMethod := jwt.GetSigningMethod(jwtType)
|
signingMethod := jwt.GetSigningMethod(jwtType)
|
||||||
|
|
||||||
|
@ -87,5 +87,21 @@ func ParseJWTToken(token string) (jwt.MapClaims, error) {
|
||||||
claims["exp"] = intExp
|
claims["exp"] = intExp
|
||||||
claims["iat"] = intIat
|
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
|
return claims, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateVerificationToken creates a verification JWT token
|
// 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{
|
claims := jwt.MapClaims{
|
||||||
|
"iss": hostname,
|
||||||
|
"aud": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyClientID),
|
||||||
|
"sub": email,
|
||||||
"exp": time.Now().Add(time.Minute * 30).Unix(),
|
"exp": time.Now().Add(time.Minute * 30).Unix(),
|
||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"token_type": tokenType,
|
"token_type": tokenType,
|
||||||
"email": email,
|
"nonce": nonceHash,
|
||||||
"host": hostname,
|
|
||||||
"redirect_url": envstore.EnvStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL),
|
"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
|
// SaveSessionInDB saves sessions generated for a given user with meta information
|
||||||
// Do not store token here as that could be security breach
|
// 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{
|
sessionData := models.Session{
|
||||||
UserID: userId,
|
UserID: userId,
|
||||||
UserAgent: GetUserAgent(c.Request),
|
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