Implement refresh token logic with fingerprint + rotation

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

11
TODO.md
View File

@ -1,5 +1,16 @@
# Task List # Task List
## Implement better way of handling jwt tokens
Check: https://hasura.io/blog/best-practices-of-using-jwt-with-graphql/#server-side-rendering-ssr
- [x] Set finger print in response cookie (https://github.com/hasura/jwt-guide/blob/60a7a86146d604fc48a799fffdee712be1c52cd0/lib/setFingerprintCookieAndSignJwt.ts#L8)
- [x] Save refresh token in session store
- [x] refresh token should be made more secure with the help of secure token rotation. Every time new token is requested new refresh token should be generated
- [x] Return jwt in response
- [x] To get session send finger print and refresh token [if they are valid -> a new access token is generated and sent to user]
- [x] Refresh token should be long living token (refresh token + finger print hash should be verified)
## Open ID compatible claims and schema ## Open ID compatible claims and schema
- [x] Rename `schema.graphqls` and re generate schema - [x] Rename `schema.graphqls` and re generate schema

View File

@ -0,0 +1,44 @@
package cookie
import (
"net/url"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin"
)
// SetAdminCookie sets the admin cookie in the response
func SetAdminCookie(gc *gin.Context, token string) {
secure := true
httpOnly := true
host, _ := utils.GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName), token, 3600, "/", host, secure, httpOnly)
}
// GetAdminCookie gets the admin cookie from the request
func GetAdminCookie(gc *gin.Context) (string, error) {
cookie, err := gc.Request.Cookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName))
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
}
// DeleteAdminCookie sets the response cookie to empty
func DeleteAdminCookie(gc *gin.Context) {
secure := true
httpOnly := true
host, _ := utils.GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName), "", -1, "/", host, secure, httpOnly)
}

101
server/cookie/cookie.go Normal file
View File

@ -0,0 +1,101 @@
package cookie
import (
"net/http"
"net/url"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin"
)
// SetCookie sets the cookie in the response. It sets 4 cookies
// 1 COOKIE_NAME.access_token jwt token for the host (temp.abc.com)
// 2 COOKIE_NAME.access_token.domain jwt token for the domain (abc.com).
// 3 COOKIE_NAME.fingerprint fingerprint hash for the refresh token verification.
// 4 COOKIE_NAME.refresh_token refresh token
// Note all sites don't allow 2nd type of cookie
func SetCookie(gc *gin.Context, accessToken, refreshToken, fingerprintHash string) {
secure := true
httpOnly := true
host, _ := utils.GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
domain := utils.GetDomainName(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
if domain != "localhost" {
domain = "." + domain
}
year := 60 * 60 * 24 * 365
thirtyMin := 60 * 30
gc.SetSameSite(http.SameSiteNoneMode)
// set cookie for host
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", accessToken, thirtyMin, "/", host, secure, httpOnly)
// in case of subdomain, set cookie for domain
gc.SetCookie(envstore.EnvInMemoryStoreObj.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.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".fingerprint", fingerprintHash, year, "/", host, secure, httpOnly)
// set refresh token cookie (this should be accessed via cookie only)
gc.SetCookie(envstore.EnvInMemoryStoreObj.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.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + ".access_token")
if err != nil {
cookie, err = gc.Request.Cookie(envstore.EnvInMemoryStoreObj.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.EnvInMemoryStoreObj.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.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + ".fingerprint")
if err != nil {
return "", err
}
// cookie escapes special characters like $
// hence we need to unescape before comparing
decodedValue, err := url.QueryUnescape(cookie.Value)
if err != nil {
return "", err
}
return decodedValue, nil
}
// DeleteCookie sets response cookies to expire
func DeleteCookie(gc *gin.Context) {
secure := true
httpOnly := true
host, _ := utils.GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
domain := utils.GetDomainName(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
if domain != "localhost" {
domain = "." + domain
}
gc.SetSameSite(http.SameSiteNoneMode)
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", "", -1, "/", host, secure, httpOnly)
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token.domain", "", -1, "/", domain, secure, httpOnly)
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".fingerprint", "", -1, "/", host, secure, httpOnly)
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".refresh_token", "", -1, "/", host, secure, httpOnly)
}

View File

@ -1,5 +1,11 @@
package models package models
import (
"strings"
"github.com/authorizerdev/authorizer/server/graph/model"
)
// User model for db // User model for db
type User struct { type User struct {
Key string `json:"_key,omitempty" bson:"_key"` // for arangodb Key string `json:"_key,omitempty" bson:"_key"` // for arangodb
@ -22,3 +28,27 @@ type User struct {
UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"` UpdatedAt int64 `gorm:"autoUpdateTime" json:"updated_at" bson:"updated_at"`
CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"` CreatedAt int64 `gorm:"autoCreateTime" json:"created_at" bson:"created_at"`
} }
func (user *User) AsAPIUser() *model.User {
isEmailVerified := user.EmailVerifiedAt != nil
isPhoneVerified := user.PhoneNumberVerifiedAt != nil
return &model.User{
ID: user.ID,
Email: user.Email,
EmailVerified: isEmailVerified,
SignupMethods: user.SignupMethods,
GivenName: user.GivenName,
FamilyName: user.FamilyName,
MiddleName: user.MiddleName,
Nickname: user.Nickname,
PreferredUsername: &user.Email,
Gender: user.Gender,
Birthdate: user.Birthdate,
PhoneNumber: user.PhoneNumber,
PhoneNumberVerified: &isPhoneVerified,
Picture: user.Picture,
Roles: strings.Split(user.Roles, ","),
CreatedAt: &user.CreatedAt,
UpdatedAt: &user.UpdatedAt,
}
}

22
server/env/env.go vendored
View File

@ -12,16 +12,6 @@ import (
"github.com/joho/godotenv" "github.com/joho/godotenv"
) )
// TODO move this to env store
var (
// ARG_DB_URL is the cli arg variable for the database url
ARG_DB_URL *string
// ARG_DB_TYPE is the cli arg variable for the database type
ARG_DB_TYPE *string
// ARG_ENV_FILE is the cli arg variable for the env file
ARG_ENV_FILE *string
)
// InitEnv to initialize EnvData and through error if required env are not present // InitEnv to initialize EnvData and through error if required env are not present
func InitEnv() { func InitEnv() {
// get clone of current store // get clone of current store
@ -51,8 +41,8 @@ func InitEnv() {
envData.StringEnv[constants.EnvKeyEnvPath] = `.env` envData.StringEnv[constants.EnvKeyEnvPath] = `.env`
} }
if ARG_ENV_FILE != nil && *ARG_ENV_FILE != "" { if envstore.ARG_ENV_FILE != nil && *envstore.ARG_ENV_FILE != "" {
envData.StringEnv[constants.EnvKeyEnvPath] = *ARG_ENV_FILE envData.StringEnv[constants.EnvKeyEnvPath] = *envstore.ARG_ENV_FILE
} }
err := godotenv.Load(envData.StringEnv[constants.EnvKeyEnvPath]) err := godotenv.Load(envData.StringEnv[constants.EnvKeyEnvPath])
@ -74,8 +64,8 @@ func InitEnv() {
if envData.StringEnv[constants.EnvKeyDatabaseType] == "" { if envData.StringEnv[constants.EnvKeyDatabaseType] == "" {
envData.StringEnv[constants.EnvKeyDatabaseType] = os.Getenv("DATABASE_TYPE") envData.StringEnv[constants.EnvKeyDatabaseType] = os.Getenv("DATABASE_TYPE")
if ARG_DB_TYPE != nil && *ARG_DB_TYPE != "" { if envstore.ARG_DB_TYPE != nil && *envstore.ARG_DB_TYPE != "" {
envData.StringEnv[constants.EnvKeyDatabaseType] = *ARG_DB_TYPE envData.StringEnv[constants.EnvKeyDatabaseType] = *envstore.ARG_DB_TYPE
} }
if envData.StringEnv[constants.EnvKeyDatabaseType] == "" { if envData.StringEnv[constants.EnvKeyDatabaseType] == "" {
@ -86,8 +76,8 @@ func InitEnv() {
if envData.StringEnv[constants.EnvKeyDatabaseURL] == "" { if envData.StringEnv[constants.EnvKeyDatabaseURL] == "" {
envData.StringEnv[constants.EnvKeyDatabaseURL] = os.Getenv("DATABASE_URL") envData.StringEnv[constants.EnvKeyDatabaseURL] = os.Getenv("DATABASE_URL")
if ARG_DB_URL != nil && *ARG_DB_URL != "" { if envstore.ARG_DB_URL != nil && *envstore.ARG_DB_URL != "" {
envData.StringEnv[constants.EnvKeyDatabaseURL] = *ARG_DB_URL envData.StringEnv[constants.EnvKeyDatabaseURL] = *envstore.ARG_DB_URL
} }
if envData.StringEnv[constants.EnvKeyDatabaseURL] == "" { if envData.StringEnv[constants.EnvKeyDatabaseURL] == "" {

View File

@ -6,6 +6,15 @@ import (
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
) )
var (
// ARG_DB_URL is the cli arg variable for the database url
ARG_DB_URL *string
// ARG_DB_TYPE is the cli arg variable for the database type
ARG_DB_TYPE *string
// ARG_ENV_FILE is the cli arg variable for the env file
ARG_ENV_FILE *string
)
// Store data structure // Store data structure
type Store struct { type Store struct {
StringEnv map[string]string `json:"string_env"` StringEnv map[string]string `json:"string_env"`

View File

@ -11,11 +11,13 @@ 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/envstore" "github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/oauth" "github.com/authorizerdev/authorizer/server/oauth"
"github.com/authorizerdev/authorizer/server/session" "github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
"github.com/coreos/go-oidc/v3/oidc" "github.com/coreos/go-oidc/v3/oidc"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -28,11 +30,11 @@ func OAuthCallbackHandler() gin.HandlerFunc {
provider := c.Param("oauth_provider") provider := c.Param("oauth_provider")
state := c.Request.FormValue("state") state := c.Request.FormValue("state")
sessionState := session.GetSocailLoginState(state) sessionState := sessionstore.GetSocailLoginState(state)
if sessionState == "" { if sessionState == "" {
c.JSON(400, gin.H{"error": "invalid oauth state"}) c.JSON(400, gin.H{"error": "invalid oauth state"})
} }
session.RemoveSocialLoginState(state) sessionstore.RemoveSocialLoginState(state)
// contains random token, redirect url, role // contains random token, redirect url, role
sessionSplit := strings.Split(state, "___") sessionSplit := strings.Split(state, "___")
@ -135,12 +137,10 @@ func OAuthCallbackHandler() gin.HandlerFunc {
} }
user, _ = db.Provider.GetUserByEmail(user.Email) user, _ = db.Provider.GetUserByEmail(user.Email)
userIdStr := fmt.Sprintf("%v", user.ID)
refreshToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeRefreshToken, inputRoles)
accessToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeAccessToken, inputRoles) authToken, _ := token.CreateAuthToken(user, inputRoles)
utils.SetCookie(c, accessToken) sessionstore.SetUserSession(user.ID, authToken.FingerPrint, authToken.RefreshToken.Token)
session.SetUserSession(userIdStr, accessToken, refreshToken) cookie.SetCookie(c, authToken.AccessToken.Token, authToken.RefreshToken.Token, authToken.FingerPrintHash)
utils.SaveSessionInDB(user.ID, c) utils.SaveSessionInDB(user.ID, c)
c.Redirect(http.StatusTemporaryRedirect, redirectURL) c.Redirect(http.StatusTemporaryRedirect, redirectURL)

View File

@ -7,7 +7,7 @@ import (
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore" "github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/oauth" "github.com/authorizerdev/authorizer/server/oauth"
"github.com/authorizerdev/authorizer/server/session" "github.com/authorizerdev/authorizer/server/sessionstore"
"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" "github.com/google/uuid"
@ -54,7 +54,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
isProviderConfigured = false isProviderConfigured = false
break break
} }
session.SetSocailLoginState(oauthStateString, constants.SignupMethodGoogle) sessionstore.SetSocailLoginState(oauthStateString, constants.SignupMethodGoogle)
// during the init of OAuthProvider authorizer url might be empty // during the init of OAuthProvider authorizer url might be empty
oauth.OAuthProviders.GoogleConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/google" oauth.OAuthProviders.GoogleConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/google"
url := oauth.OAuthProviders.GoogleConfig.AuthCodeURL(oauthStateString) url := oauth.OAuthProviders.GoogleConfig.AuthCodeURL(oauthStateString)
@ -64,7 +64,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
isProviderConfigured = false isProviderConfigured = false
break break
} }
session.SetSocailLoginState(oauthStateString, constants.SignupMethodGithub) sessionstore.SetSocailLoginState(oauthStateString, constants.SignupMethodGithub)
oauth.OAuthProviders.GithubConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/github" oauth.OAuthProviders.GithubConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/github"
url := oauth.OAuthProviders.GithubConfig.AuthCodeURL(oauthStateString) url := oauth.OAuthProviders.GithubConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url) c.Redirect(http.StatusTemporaryRedirect, url)
@ -73,7 +73,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
isProviderConfigured = false isProviderConfigured = false
break break
} }
session.SetSocailLoginState(oauthStateString, constants.SignupMethodFacebook) sessionstore.SetSocailLoginState(oauthStateString, constants.SignupMethodFacebook)
oauth.OAuthProviders.FacebookConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/facebook" oauth.OAuthProviders.FacebookConfig.RedirectURL = envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL) + "/oauth_callback/facebook"
url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString) url := oauth.OAuthProviders.FacebookConfig.AuthCodeURL(oauthStateString)
c.Redirect(http.StatusTemporaryRedirect, url) c.Redirect(http.StatusTemporaryRedirect, url)

View File

@ -5,9 +5,10 @@ import (
"strings" "strings"
"time" "time"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/session" "github.com/authorizerdev/authorizer/server/sessionstore"
"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"
) )
@ -19,20 +20,20 @@ func VerifyEmailHandler() gin.HandlerFunc {
errorRes := gin.H{ errorRes := gin.H{
"message": "invalid token", "message": "invalid token",
} }
token := c.Query("token") tokenInQuery := c.Query("token")
if token == "" { if tokenInQuery == "" {
c.JSON(400, errorRes) c.JSON(400, errorRes)
return return
} }
verificationRequest, err := db.Provider.GetVerificationRequestByToken(token) verificationRequest, err := db.Provider.GetVerificationRequestByToken(tokenInQuery)
if err != nil { if err != nil {
c.JSON(400, errorRes) c.JSON(400, errorRes)
return return
} }
// verify if token exists in db // verify if token exists in db
claim, err := utils.VerifyVerificationToken(token) claim, err := token.VerifyVerificationToken(tokenInQuery)
if err != nil { if err != nil {
c.JSON(400, errorRes) c.JSON(400, errorRes)
return return
@ -56,13 +57,17 @@ func VerifyEmailHandler() gin.HandlerFunc {
db.Provider.DeleteVerificationRequest(verificationRequest) db.Provider.DeleteVerificationRequest(verificationRequest)
roles := strings.Split(user.Roles, ",") roles := strings.Split(user.Roles, ",")
refreshToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeRefreshToken, roles) authToken, err := token.CreateAuthToken(user, roles)
if err != nil {
accessToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeAccessToken, roles) c.JSON(400, gin.H{
"message": err.Error(),
session.SetUserSession(user.ID, accessToken, refreshToken) })
return
}
sessionstore.SetUserSession(user.ID, authToken.FingerPrint, authToken.RefreshToken.Token)
cookie.SetCookie(c, authToken.AccessToken.Token, authToken.RefreshToken.Token, authToken.FingerPrintHash)
utils.SaveSessionInDB(user.ID, c) utils.SaveSessionInDB(user.ID, c)
utils.SetCookie(c, accessToken)
c.Redirect(http.StatusTemporaryRedirect, claim.RedirectURL) c.Redirect(http.StatusTemporaryRedirect, claim.RedirectURL)
} }
} }

View File

@ -9,15 +9,15 @@ import (
"github.com/authorizerdev/authorizer/server/envstore" "github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/oauth" "github.com/authorizerdev/authorizer/server/oauth"
"github.com/authorizerdev/authorizer/server/routes" "github.com/authorizerdev/authorizer/server/routes"
"github.com/authorizerdev/authorizer/server/session" "github.com/authorizerdev/authorizer/server/sessionstore"
) )
var VERSION string var VERSION string
func main() { func main() {
env.ARG_DB_URL = flag.String("database_url", "", "Database connection string") envstore.ARG_DB_URL = flag.String("database_url", "", "Database connection string")
env.ARG_DB_TYPE = flag.String("database_type", "", "Database type, possible values are postgres,mysql,sqlite") envstore.ARG_DB_TYPE = flag.String("database_type", "", "Database type, possible values are postgres,mysql,sqlite")
env.ARG_ENV_FILE = flag.String("env_file", "", "Env file path") envstore.ARG_ENV_FILE = flag.String("env_file", "", "Env file path")
flag.Parse() flag.Parse()
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyVersion, VERSION) envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyVersion, VERSION)
@ -26,7 +26,7 @@ func main() {
db.InitDB() db.InitDB()
env.PersistEnv() env.PersistEnv()
session.InitSession() sessionstore.InitSession()
oauth.InitOAuth() oauth.InitOAuth()
router := routes.InitRouter() router := routes.InitRouter()

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"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/utils" "github.com/authorizerdev/authorizer/server/utils"
@ -28,7 +29,7 @@ func AdminLoginResolver(ctx context.Context, params model.AdminLoginInput) (*mod
if err != nil { if err != nil {
return res, err return res, err
} }
utils.SetAdminCookie(gc, hashedKey) cookie.SetAdminCookie(gc, hashedKey)
res = &model.Response{ res = &model.Response{
Message: "admin logged in successfully", Message: "admin logged in successfully",

View File

@ -4,7 +4,9 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/authorizerdev/authorizer/server/cookie"
"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/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -17,11 +19,11 @@ func AdminLogoutResolver(ctx context.Context) (*model.Response, error) {
return res, err return res, err
} }
if !utils.IsSuperAdmin(gc) { if !token.IsSuperAdmin(gc) {
return res, fmt.Errorf("unauthorized") return res, fmt.Errorf("unauthorized")
} }
utils.DeleteAdminCookie(gc) cookie.DeleteAdminCookie(gc)
res = &model.Response{ res = &model.Response{
Message: "admin logged out successfully", Message: "admin logged out successfully",

View File

@ -5,8 +5,10 @@ import (
"fmt" "fmt"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"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/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -19,7 +21,7 @@ func AdminSessionResolver(ctx context.Context) (*model.Response, error) {
return res, err return res, err
} }
if !utils.IsSuperAdmin(gc) { if !token.IsSuperAdmin(gc) {
return res, fmt.Errorf("unauthorized") return res, fmt.Errorf("unauthorized")
} }
@ -27,7 +29,7 @@ func AdminSessionResolver(ctx context.Context) (*model.Response, error) {
if err != nil { if err != nil {
return res, err return res, err
} }
utils.SetAdminCookie(gc, hashedKey) cookie.SetAdminCookie(gc, hashedKey)
res = &model.Response{ res = &model.Response{
Message: "admin logged in successfully", Message: "admin logged in successfully",

View File

@ -7,6 +7,7 @@ import (
"strings" "strings"
"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/envstore" "github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
@ -71,7 +72,7 @@ func AdminSignupResolver(ctx context.Context, params model.AdminSignupInput) (*m
if err != nil { if err != nil {
return res, err return res, err
} }
utils.SetAdminCookie(gc, hashedKey) cookie.SetAdminCookie(gc, hashedKey)
res = &model.Response{ res = &model.Response{
Message: "admin signed up successfully", Message: "admin signed up successfully",

View File

@ -7,7 +7,8 @@ import (
"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/session" "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,7 +20,7 @@ func DeleteUserResolver(ctx context.Context, params model.DeleteUserInput) (*mod
return res, err return res, err
} }
if !utils.IsSuperAdmin(gc) { if !token.IsSuperAdmin(gc) {
return res, fmt.Errorf("unauthorized") return res, fmt.Errorf("unauthorized")
} }
@ -28,7 +29,7 @@ func DeleteUserResolver(ctx context.Context, params model.DeleteUserInput) (*mod
return res, err return res, err
} }
session.DeleteAllUserSession(fmt.Sprintf("%x", user.ID)) sessionstore.DeleteAllUserSession(fmt.Sprintf("%x", user.ID))
err = db.Provider.DeleteUser(user) err = db.Provider.DeleteUser(user)
if err != nil { if err != nil {

View File

@ -7,6 +7,7 @@ import (
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"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/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -20,7 +21,7 @@ func EnvResolver(ctx context.Context) (*model.Env, error) {
return res, err return res, err
} }
if !utils.IsSuperAdmin(gc) { if !token.IsSuperAdmin(gc) {
return res, fmt.Errorf("unauthorized") return res, fmt.Errorf("unauthorized")
} }

View File

@ -13,6 +13,7 @@ import (
"github.com/authorizerdev/authorizer/server/email" "github.com/authorizerdev/authorizer/server/email"
"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/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -38,12 +39,12 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
return res, fmt.Errorf(`user with this email not found`) return res, fmt.Errorf(`user with this email not found`)
} }
token, err := utils.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword) verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword)
if err != nil { if err != nil {
log.Println(`error generating token`, err) log.Println(`error generating token`, err)
} }
db.Provider.AddVerificationRequest(models.VerificationRequest{ db.Provider.AddVerificationRequest(models.VerificationRequest{
Token: token, Token: verificationToken,
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,
@ -51,7 +52,7 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
// 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 func() {
email.SendForgotPasswordMail(params.Email, token, host) email.SendForgotPasswordMail(params.Email, verificationToken, host)
}() }()
res = &model.Response{ res = &model.Response{

View File

@ -7,10 +7,12 @@ import (
"strings" "strings"
"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/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/session" "github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
@ -56,21 +58,21 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
roles = params.Roles roles = params.Roles
} }
refreshToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeRefreshToken, roles)
accessToken, expiresAt, _ := utils.CreateAuthToken(user, constants.TokenTypeAccessToken, roles) authToken, err := token.CreateAuthToken(user, roles)
if err != nil {
session.SetUserSession(user.ID, accessToken, refreshToken) 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) utils.SaveSessionInDB(user.ID, gc)
res = &model.AuthResponse{ res = &model.AuthResponse{
Message: `Logged in successfully`, Message: `Logged in successfully`,
AccessToken: &accessToken, AccessToken: &authToken.AccessToken.Token,
ExpiresAt: &expiresAt, ExpiresAt: &authToken.AccessToken.ExpiresAt,
User: utils.GetResponseUserData(user), User: user.AsAPIUser(),
} }
utils.SetCookie(gc, accessToken)
return res, nil return res, nil
} }

View File

@ -2,10 +2,11 @@ package resolvers
import ( import (
"context" "context"
"fmt"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/session" "github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -17,22 +18,38 @@ func LogoutResolver(ctx context.Context) (*model.Response, error) {
return res, err return res, err
} }
token, err := utils.GetAuthToken(gc) // get refresh token
refreshToken, err := token.GetRefreshToken(gc)
if err != nil { if err != nil {
return res, err return res, err
} }
claim, err := utils.VerifyAuthToken(token) // get fingerprint hash
fingerprintHash, err := token.GetFingerPrint(gc)
if err != nil { if err != nil {
return res, err return res, err
} }
userId := fmt.Sprintf("%v", claim["id"]) decryptedFingerPrint, err := utils.DecryptAES([]byte(fingerprintHash))
session.DeleteUserSession(userId, token) if err != nil {
return res, err
}
fingerPrint := string(decryptedFingerPrint)
// verify refresh token and fingerprint
claims, err := token.VerifyJWTToken(refreshToken)
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",
} }
utils.DeleteCookie(gc)
return res, nil return res, nil
} }

View File

@ -13,6 +13,7 @@ import (
"github.com/authorizerdev/authorizer/server/email" "github.com/authorizerdev/authorizer/server/email"
"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/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -104,12 +105,12 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
if !envstore.EnvInMemoryStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) { if !envstore.EnvInMemoryStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
// insert verification request // insert verification request
verificationType := constants.VerificationTypeMagicLinkLogin verificationType := constants.VerificationTypeMagicLinkLogin
token, err := utils.CreateVerificationToken(params.Email, verificationType) verificationToken, err := token.CreateVerificationToken(params.Email, verificationType)
if err != nil { if err != nil {
log.Println(`error generating token`, err) log.Println(`error generating token`, err)
} }
db.Provider.AddVerificationRequest(models.VerificationRequest{ db.Provider.AddVerificationRequest(models.VerificationRequest{
Token: token, 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,
@ -117,7 +118,7 @@ func MagicLinkLoginResolver(ctx context.Context, params model.MagicLinkLoginInpu
// 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 func() {
email.SendVerificationMail(params.Email, token) email.SendVerificationMail(params.Email, verificationToken)
}() }()
} }

View File

@ -6,7 +6,7 @@ import (
"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/session" "github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -18,30 +18,17 @@ func ProfileResolver(ctx context.Context) (*model.User, error) {
return res, err return res, err
} }
token, err := utils.GetAuthToken(gc) claims, err := token.ValidateAccessToken(gc)
if err != nil { if err != nil {
return res, err return res, err
} }
claim, err := utils.VerifyAuthToken(token) userID := fmt.Sprintf("%v", claims["id"])
user, err := db.Provider.GetUserByID(userID)
if err != nil { if err != nil {
return res, err return res, err
} }
userID := fmt.Sprintf("%v", claim["id"]) return user.AsAPIUser(), nil
email := fmt.Sprintf("%v", claim["email"])
sessionToken := session.GetUserSession(userID, token)
if sessionToken == "" {
return res, fmt.Errorf(`unauthorized`)
}
user, err := db.Provider.GetUserByEmail(email)
if err != nil {
return res, err
}
res = utils.GetResponseUserData(user)
return res, nil
} }

View File

@ -11,6 +11,7 @@ import (
"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/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -38,12 +39,12 @@ func ResendVerifyEmailResolver(ctx context.Context, params model.ResendVerifyEma
log.Println("error deleting verification request:", err) log.Println("error deleting verification request:", err)
} }
token, err := utils.CreateVerificationToken(params.Email, params.Identifier) verificationToken, err := token.CreateVerificationToken(params.Email, params.Identifier)
if err != nil { if err != nil {
log.Println(`error generating token`, err) log.Println(`error generating token`, err)
} }
db.Provider.AddVerificationRequest(models.VerificationRequest{ db.Provider.AddVerificationRequest(models.VerificationRequest{
Token: token, Token: verificationToken,
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,
@ -51,7 +52,7 @@ func ResendVerifyEmailResolver(ctx context.Context, params model.ResendVerifyEma
// 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 func() {
email.SendVerificationMail(params.Email, token) email.SendVerificationMail(params.Email, verificationToken)
}() }()
res = &model.Response{ res = &model.Response{

View File

@ -10,6 +10,7 @@ import (
"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"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -30,7 +31,7 @@ func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput)
} }
// verify if token exists in db // verify if token exists in db
claim, err := utils.VerifyVerificationToken(params.Token) claim, err := token.VerifyVerificationToken(params.Token)
if err != nil { if err != nil {
return res, fmt.Errorf(`invalid token`) return res, fmt.Errorf(`invalid token`)
} }

View File

@ -3,13 +3,12 @@ package resolvers
import ( import (
"context" "context"
"fmt" "fmt"
"time"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db"
"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/session" "github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -21,35 +20,49 @@ func SessionResolver(ctx context.Context, roles []string) (*model.AuthResponse,
if err != nil { if err != nil {
return res, err return res, err
} }
token, err := utils.GetAuthToken(gc)
// get refresh token
refreshToken, err := token.GetRefreshToken(gc)
if err != nil { if err != nil {
return res, err return res, err
} }
claim, accessTokenErr := utils.VerifyAuthToken(token) // get fingerprint hash
expiresAt := claim["exp"].(int64) fingerprintHash, err := token.GetFingerPrint(gc)
email := fmt.Sprintf("%v", claim["email"])
user, err := db.Provider.GetUserByEmail(email)
if err != nil { if err != nil {
return res, err return res, err
} }
userIdStr := fmt.Sprintf("%v", user.ID) decryptedFingerPrint, err := utils.DecryptAES([]byte(fingerprintHash))
if err != nil {
return res, err
}
sessionToken := session.GetUserSession(userIdStr, token) fingerPrint := string(decryptedFingerPrint)
if sessionToken == "" { // verify refresh token and fingerprint
claims, err := token.VerifyJWTToken(refreshToken)
if err != nil {
return res, err
}
userID := claims["id"].(string)
persistedRefresh := sessionstore.GetUserSession(userID, fingerPrint)
if refreshToken != persistedRefresh {
return res, fmt.Errorf(`unauthorized`) return res, fmt.Errorf(`unauthorized`)
} }
expiresTimeObj := time.Unix(expiresAt, 0) user, err := db.Provider.GetUserByID(userID)
currentTimeObj := time.Now() if err != nil {
return res, err
}
claimRoleInterface := claim[envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtRoleClaim)].([]interface{}) // refresh token has "roles" as claim
claimRoles := make([]string, len(claimRoleInterface)) claimRoleInterface := claims["roles"].([]interface{})
for i, v := range claimRoleInterface { claimRoles := []string{}
claimRoles[i] = v.(string) for _, v := range claimRoleInterface {
claimRoles = append(claimRoles, v.(string))
} }
if len(roles) > 0 { if len(roles) > 0 {
@ -60,23 +73,22 @@ func SessionResolver(ctx context.Context, roles []string) (*model.AuthResponse,
} }
} }
// TODO change this logic to make it more secure // delete older session
if accessTokenErr != nil || expiresTimeObj.Sub(currentTimeObj).Minutes() <= 5 { sessionstore.DeleteUserSession(userID, fingerPrint)
// if access token has expired and refresh/session token is valid
// generate new accessToken authToken, err := token.CreateAuthToken(user, claimRoles)
currentRefreshToken := session.GetUserSession(userIdStr, token) if err != nil {
session.DeleteUserSession(userIdStr, token) return res, err
token, expiresAt, _ = utils.CreateAuthToken(user, constants.TokenTypeAccessToken, claimRoles) }
session.SetUserSession(userIdStr, token, currentRefreshToken) sessionstore.SetUserSession(user.ID, authToken.FingerPrint, authToken.RefreshToken.Token)
utils.SaveSessionInDB(user.ID, gc) cookie.SetCookie(gc, authToken.AccessToken.Token, authToken.RefreshToken.Token, authToken.FingerPrintHash)
res = &model.AuthResponse{
Message: `Session token refreshed`,
AccessToken: &authToken.AccessToken.Token,
ExpiresAt: &authToken.AccessToken.ExpiresAt,
User: user.AsAPIUser(),
} }
utils.SetCookie(gc, token)
res = &model.AuthResponse{
Message: `Token verified`,
AccessToken: &token,
ExpiresAt: &expiresAt,
User: utils.GetResponseUserData(user),
}
return res, nil return res, nil
} }

View File

@ -8,12 +8,14 @@ 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"
"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/session" "github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -114,19 +116,18 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
if err != nil { if err != nil {
return res, err return res, err
} }
userIdStr := fmt.Sprintf("%v", user.ID)
roles := strings.Split(user.Roles, ",") roles := strings.Split(user.Roles, ",")
userToReturn := utils.GetResponseUserData(user) userToReturn := user.AsAPIUser()
if !envstore.EnvInMemoryStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) { if !envstore.EnvInMemoryStoreObj.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) {
// insert verification request // insert verification request
verificationType := constants.VerificationTypeBasicAuthSignup verificationType := constants.VerificationTypeBasicAuthSignup
token, err := utils.CreateVerificationToken(params.Email, verificationType) verificationToken, err := token.CreateVerificationToken(params.Email, verificationType)
if err != nil { if err != nil {
log.Println(`error generating token`, err) log.Println(`error generating token`, err)
} }
db.Provider.AddVerificationRequest(models.VerificationRequest{ db.Provider.AddVerificationRequest(models.VerificationRequest{
Token: token, 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,
@ -134,7 +135,7 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
// 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 func() {
email.SendVerificationMail(params.Email, token) email.SendVerificationMail(params.Email, verificationToken)
}() }()
res = &model.AuthResponse{ res = &model.AuthResponse{
@ -143,20 +144,20 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
} }
} else { } else {
refreshToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeRefreshToken, roles) authToken, err := token.CreateAuthToken(user, roles)
if err != nil {
accessToken, expiresAt, _ := utils.CreateAuthToken(user, constants.TokenTypeAccessToken, roles) return res, err
}
session.SetUserSession(userIdStr, accessToken, refreshToken) 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) utils.SaveSessionInDB(user.ID, gc)
res = &model.AuthResponse{ res = &model.AuthResponse{
Message: `Signed up successfully.`, Message: `Signed up successfully.`,
AccessToken: &accessToken, AccessToken: &authToken.AccessToken.Token,
ExpiresAt: &expiresAt, ExpiresAt: &authToken.AccessToken.ExpiresAt,
User: userToReturn, User: userToReturn,
} }
utils.SetCookie(gc, accessToken)
} }
return res, nil return res, nil

View File

@ -9,9 +9,11 @@ import (
"reflect" "reflect"
"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/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/utils" "github.com/authorizerdev/authorizer/server/utils"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
@ -26,7 +28,7 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
return res, err return res, err
} }
if !utils.IsSuperAdmin(gc) { if !token.IsSuperAdmin(gc) {
return res, fmt.Errorf("unauthorized") return res, fmt.Errorf("unauthorized")
} }
@ -124,7 +126,7 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model
if err != nil { if err != nil {
return res, err return res, err
} }
utils.SetAdminCookie(gc, hashedKey) cookie.SetAdminCookie(gc, hashedKey)
} }
env.EnvData = encryptedConfig env.EnvData = encryptedConfig

View File

@ -8,11 +8,13 @@ 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"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/session" "github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
@ -25,29 +27,17 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
return res, err return res, err
} }
token, err := utils.GetAuthToken(gc) claims, err := token.ValidateAccessToken(gc)
if err != nil { if err != nil {
return res, err return res, err
} }
claim, err := utils.VerifyAuthToken(token)
if err != nil {
return res, err
}
id := fmt.Sprintf("%v", claim["id"])
sessionToken := session.GetUserSession(id, token)
if sessionToken == "" {
return res, fmt.Errorf(`unauthorized`)
}
// validate if all params are not empty // validate if all params are not empty
if params.GivenName == nil && params.FamilyName == nil && params.Picture == nil && params.MiddleName == nil && params.Nickname == nil && params.OldPassword == nil && params.Email == nil && params.Birthdate == nil && params.Gender == nil && params.PhoneNumber == nil { if params.GivenName == nil && params.FamilyName == nil && params.Picture == nil && params.MiddleName == nil && params.Nickname == nil && params.OldPassword == nil && params.Email == nil && params.Birthdate == nil && params.Gender == nil && params.PhoneNumber == nil {
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", claim["email"]) userEmail := fmt.Sprintf("%v", claims["email"])
user, err := db.Provider.GetUserByEmail(userEmail) user, err := db.Provider.GetUserByEmail(userEmail)
if err != nil { if err != nil {
return res, err return res, err
@ -123,20 +113,20 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
return res, fmt.Errorf("user with this email address already exists") return res, fmt.Errorf("user with this email address already exists")
} }
session.DeleteAllUserSession(fmt.Sprintf("%v", user.ID)) sessionstore.DeleteAllUserSession(fmt.Sprintf("%v", user.ID))
utils.DeleteCookie(gc) cookie.DeleteCookie(gc)
user.Email = newEmail user.Email = newEmail
user.EmailVerifiedAt = nil user.EmailVerifiedAt = nil
hasEmailChanged = true hasEmailChanged = true
// insert verification request // insert verification request
verificationType := constants.VerificationTypeUpdateEmail verificationType := constants.VerificationTypeUpdateEmail
token, err := utils.CreateVerificationToken(newEmail, verificationType) verificationToken, err := token.CreateVerificationToken(newEmail, verificationType)
if err != nil { if err != nil {
log.Println(`error generating token`, err) log.Println(`error generating token`, err)
} }
db.Provider.AddVerificationRequest(models.VerificationRequest{ db.Provider.AddVerificationRequest(models.VerificationRequest{
Token: token, Token: verificationToken,
Identifier: verificationType, Identifier: verificationType,
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(), ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
Email: newEmail, Email: newEmail,
@ -144,7 +134,7 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
// 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 func() {
email.SendVerificationMail(newEmail, token) email.SendVerificationMail(newEmail, verificationToken)
}() }()
} }

View File

@ -8,12 +8,14 @@ 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"
"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/session" "github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -26,7 +28,7 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
return res, err return res, err
} }
if !utils.IsSuperAdmin(gc) { if !token.IsSuperAdmin(gc) {
return res, fmt.Errorf("unauthorized") return res, fmt.Errorf("unauthorized")
} }
@ -93,19 +95,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")
} }
session.DeleteAllUserSession(fmt.Sprintf("%v", user.ID)) sessionstore.DeleteAllUserSession(fmt.Sprintf("%v", user.ID))
utils.DeleteCookie(gc) cookie.DeleteCookie(gc)
user.Email = newEmail user.Email = newEmail
user.EmailVerifiedAt = nil user.EmailVerifiedAt = nil
// insert verification request // insert verification request
verificationType := constants.VerificationTypeUpdateEmail verificationType := constants.VerificationTypeUpdateEmail
token, err := utils.CreateVerificationToken(newEmail, verificationType) verificationToken, err := token.CreateVerificationToken(newEmail, verificationType)
if err != nil { if err != nil {
log.Println(`error generating token`, err) log.Println(`error generating token`, err)
} }
db.Provider.AddVerificationRequest(models.VerificationRequest{ db.Provider.AddVerificationRequest(models.VerificationRequest{
Token: token, Token: verificationToken,
Identifier: verificationType, Identifier: verificationType,
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(), ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
Email: newEmail, Email: newEmail,
@ -113,7 +115,7 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
// 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 func() {
email.SendVerificationMail(newEmail, token) email.SendVerificationMail(newEmail, verificationToken)
}() }()
} }
@ -133,8 +135,8 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
rolesToSave = strings.Join(inputRoles, ",") rolesToSave = strings.Join(inputRoles, ",")
} }
session.DeleteAllUserSession(fmt.Sprintf("%v", user.ID)) sessionstore.DeleteAllUserSession(fmt.Sprintf("%v", user.ID))
utils.DeleteCookie(gc) cookie.DeleteCookie(gc)
} }
if rolesToSave != "" { if rolesToSave != "" {

View File

@ -6,6 +6,7 @@ import (
"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/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -18,7 +19,7 @@ func UsersResolver(ctx context.Context) ([]*model.User, error) {
return res, err return res, err
} }
if !utils.IsSuperAdmin(gc) { if !token.IsSuperAdmin(gc) {
return res, fmt.Errorf("unauthorized") return res, fmt.Errorf("unauthorized")
} }
@ -28,7 +29,7 @@ func UsersResolver(ctx context.Context) ([]*model.User, error) {
} }
for i := 0; i < len(users); i++ { for i := 0; i < len(users); i++ {
res = append(res, utils.GetResponseUserData(users[i])) res = append(res, users[i].AsAPIUser())
} }
return res, nil return res, nil

View File

@ -6,6 +6,7 @@ import (
"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/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -18,7 +19,7 @@ func VerificationRequestsResolver(ctx context.Context) ([]*model.VerificationReq
return res, err return res, err
} }
if !utils.IsSuperAdmin(gc) { if !token.IsSuperAdmin(gc) {
return res, fmt.Errorf("unauthorized") return res, fmt.Errorf("unauthorized")
} }

View File

@ -6,10 +6,11 @@ import (
"strings" "strings"
"time" "time"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/session" "github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
) )
@ -27,7 +28,7 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
} }
// verify if token exists in db // verify if token exists in db
claim, err := utils.VerifyVerificationToken(params.Token) claim, err := token.VerifyVerificationToken(params.Token)
if err != nil { if err != nil {
return res, fmt.Errorf(`invalid token`) return res, fmt.Errorf(`invalid token`)
} }
@ -45,20 +46,20 @@ func VerifyEmailResolver(ctx context.Context, params model.VerifyEmailInput) (*m
db.Provider.DeleteVerificationRequest(verificationRequest) db.Provider.DeleteVerificationRequest(verificationRequest)
roles := strings.Split(user.Roles, ",") roles := strings.Split(user.Roles, ",")
refreshToken, _, _ := utils.CreateAuthToken(user, constants.TokenTypeRefreshToken, roles) authToken, err := token.CreateAuthToken(user, roles)
accessToken, expiresAt, _ := utils.CreateAuthToken(user, constants.TokenTypeAccessToken, roles) if err != nil {
return res, err
session.SetUserSession(user.ID, accessToken, refreshToken) }
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) utils.SaveSessionInDB(user.ID, gc)
res = &model.AuthResponse{ res = &model.AuthResponse{
Message: `Email verified successfully.`, Message: `Email verified successfully.`,
AccessToken: &accessToken, AccessToken: &authToken.AccessToken.Token,
ExpiresAt: &expiresAt, ExpiresAt: &authToken.AccessToken.ExpiresAt,
User: utils.GetResponseUserData(user), User: user.AsAPIUser(),
} }
utils.SetCookie(gc, accessToken)
return res, nil return res, nil
} }

View File

@ -1,4 +1,4 @@
package session package sessionstore
import ( import (
"sync" "sync"
@ -69,6 +69,19 @@ func (c *InMemoryStore) GetUserSession(userId, accessToken string) string {
return token return token
} }
// GetUserSessions returns all the user session token from the in-memory store.
func (c *InMemoryStore) GetUserSessions(userId string) map[string]string {
// c.mutex.Lock()
// defer c.mutex.Unlock()
sessionMap, ok := c.store[userId]
if !ok {
return nil
}
return sessionMap
}
// SetSocialLoginState sets the social login state in the in-memory store. // SetSocialLoginState sets the social login state in the in-memory store.
func (c *InMemoryStore) SetSocialLoginState(key, state string) { func (c *InMemoryStore) SetSocialLoginState(key, state string) {
c.mutex.Lock() c.mutex.Lock()

View File

@ -1,4 +1,4 @@
package session package sessionstore
import ( import (
"context" "context"
@ -60,6 +60,16 @@ func (c *RedisStore) GetUserSession(userId, accessToken string) string {
return token return token
} }
// GetUserSessions returns all the user session token from the redis store.
func (c *RedisStore) GetUserSessions(userID string) map[string]string {
res, err := c.store.HGetAll(c.ctx, "authorizer_"+userID).Result()
if err != nil {
log.Println("error getting token from redis store:", err)
}
return res
}
// SetSocialLoginState sets the social login state in redis store. // SetSocialLoginState sets the social login state in redis store.
func (c *RedisStore) SetSocialLoginState(key, state string) { func (c *RedisStore) SetSocialLoginState(key, state string) {
err := c.store.Set(c.ctx, key, state, 0).Err() err := c.store.Set(c.ctx, key, state, 0).Err()

View File

@ -1,4 +1,4 @@
package session package sessionstore
import ( import (
"context" "context"
@ -22,22 +22,22 @@ type SessionStore struct {
var SessionStoreObj SessionStore var SessionStoreObj SessionStore
// SetUserSession sets the user session in the session store // SetUserSession sets the user session in the session store
func SetUserSession(userId, accessToken, refreshToken string) { func SetUserSession(userId, fingerprint, refreshToken string) {
if SessionStoreObj.RedisMemoryStoreObj != nil { if SessionStoreObj.RedisMemoryStoreObj != nil {
SessionStoreObj.RedisMemoryStoreObj.AddUserSession(userId, accessToken, refreshToken) SessionStoreObj.RedisMemoryStoreObj.AddUserSession(userId, fingerprint, refreshToken)
} }
if SessionStoreObj.InMemoryStoreObj != nil { if SessionStoreObj.InMemoryStoreObj != nil {
SessionStoreObj.InMemoryStoreObj.AddUserSession(userId, accessToken, refreshToken) SessionStoreObj.InMemoryStoreObj.AddUserSession(userId, fingerprint, refreshToken)
} }
} }
// DeleteUserSession deletes the particular user session from the session store // DeleteUserSession deletes the particular user session from the session store
func DeleteUserSession(userId, accessToken string) { func DeleteUserSession(userId, fingerprint string) {
if SessionStoreObj.RedisMemoryStoreObj != nil { if SessionStoreObj.RedisMemoryStoreObj != nil {
SessionStoreObj.RedisMemoryStoreObj.DeleteUserSession(userId, accessToken) SessionStoreObj.RedisMemoryStoreObj.DeleteUserSession(userId, fingerprint)
} }
if SessionStoreObj.InMemoryStoreObj != nil { if SessionStoreObj.InMemoryStoreObj != nil {
SessionStoreObj.InMemoryStoreObj.DeleteUserSession(userId, accessToken) SessionStoreObj.InMemoryStoreObj.DeleteUserSession(userId, fingerprint)
} }
} }
@ -52,17 +52,29 @@ func DeleteAllUserSession(userId string) {
} }
// GetUserSession returns the user session from the session store // GetUserSession returns the user session from the session store
func GetUserSession(userId, accessToken string) string { func GetUserSession(userId, fingerprint string) string {
if SessionStoreObj.RedisMemoryStoreObj != nil { if SessionStoreObj.RedisMemoryStoreObj != nil {
return SessionStoreObj.RedisMemoryStoreObj.GetUserSession(userId, accessToken) return SessionStoreObj.RedisMemoryStoreObj.GetUserSession(userId, fingerprint)
} }
if SessionStoreObj.InMemoryStoreObj != nil { if SessionStoreObj.InMemoryStoreObj != nil {
return SessionStoreObj.InMemoryStoreObj.GetUserSession(userId, accessToken) return SessionStoreObj.InMemoryStoreObj.GetUserSession(userId, fingerprint)
} }
return "" return ""
} }
// GetUserSessions returns all the user sessions from the session store
func GetUserSessions(userId string) map[string]string {
if SessionStoreObj.RedisMemoryStoreObj != nil {
return SessionStoreObj.RedisMemoryStoreObj.GetUserSessions(userId)
}
if SessionStoreObj.InMemoryStoreObj != nil {
return SessionStoreObj.InMemoryStoreObj.GetUserSessions(userId)
}
return nil
}
// ClearStore clears the session store for authorizer tokens // ClearStore clears the session store for authorizer tokens
func ClearStore() { func ClearStore() {
if SessionStoreObj.RedisMemoryStoreObj != nil { if SessionStoreObj.RedisMemoryStoreObj != nil {

View File

@ -2,6 +2,7 @@ package test
import ( import (
"fmt" "fmt"
"net/url"
"testing" "testing"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
@ -9,6 +10,8 @@ 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/resolvers" "github.com/authorizerdev/authorizer/server/resolvers"
"github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -27,12 +30,22 @@ 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, _ := utils.EncryptAES([]byte(fingerPrint))
token := *verifyRes.AccessToken token := *verifyRes.AccessToken
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName), token)) cookie := fmt.Sprintf("%s=%s;%s=%s;%s=%s", envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".fingerprint", url.QueryEscape(string(fingerPrintHash)), envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".refresh_token", refreshToken, envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", token)
req.Header.Set("Cookie", cookie)
_, err = resolvers.LogoutResolver(ctx) _, err = resolvers.LogoutResolver(ctx)
assert.Nil(t, err) assert.Nil(t, err)
_, err = resolvers.ProfileResolver(ctx)
assert.NotNil(t, err, "unauthorized")
cleanData(email) cleanData(email)
}) })
} }

View File

@ -29,7 +29,7 @@ func magicLinkLoginTests(t *testing.T, s TestSetup) {
}) })
token := *verifyRes.AccessToken token := *verifyRes.AccessToken
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName), token)) req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", token))
_, err = resolvers.ProfileResolver(ctx) _, err = resolvers.ProfileResolver(ctx)
assert.Nil(t, err) assert.Nil(t, err)

View File

@ -33,7 +33,7 @@ func profileTests(t *testing.T, s TestSetup) {
}) })
token := *verifyRes.AccessToken token := *verifyRes.AccessToken
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName), token)) req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", token))
profileRes, err := resolvers.ProfileResolver(ctx) profileRes, err := resolvers.ProfileResolver(ctx)
assert.Nil(t, err) assert.Nil(t, err)

View File

@ -12,8 +12,8 @@ import (
func TestResolvers(t *testing.T) { func TestResolvers(t *testing.T) {
databases := map[string]string{ databases := map[string]string{
constants.DbTypeSqlite: "../../data.db", constants.DbTypeSqlite: "../../data.db",
constants.DbTypeArangodb: "http://localhost:8529", // constants.DbTypeArangodb: "http://localhost:8529",
constants.DbTypeMongodb: "mongodb://localhost:27017", // constants.DbTypeMongodb: "mongodb://localhost:27017",
} }
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyVersion, "test") envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyVersion, "test")
for dbType, dbURL := range databases { for dbType, dbURL := range databases {

View File

@ -2,6 +2,7 @@ package test
import ( import (
"fmt" "fmt"
"net/url"
"testing" "testing"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
@ -9,6 +10,8 @@ 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/resolvers" "github.com/authorizerdev/authorizer/server/resolvers"
"github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -32,14 +35,27 @@ func sessionTests(t *testing.T, s TestSetup) {
Token: verificationRequest.Token, Token: verificationRequest.Token,
}) })
token := *verifyRes.AccessToken sessions := sessionstore.GetUserSessions(verifyRes.User.ID)
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName), token)) fingerPrint := ""
refreshToken := ""
for key, val := range sessions {
fingerPrint = key
refreshToken = val
}
sessionRes, err := resolvers.SessionResolver(ctx, []string{}) fingerPrintHash, _ := utils.EncryptAES([]byte(fingerPrint))
token := *verifyRes.AccessToken
cookie := fmt.Sprintf("%s=%s;%s=%s;%s=%s", envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".fingerprint", url.QueryEscape(string(fingerPrintHash)), envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".refresh_token", refreshToken, envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", token)
req.Header.Set("Cookie", cookie)
_, err = resolvers.SessionResolver(ctx, []string{})
assert.Nil(t, err) assert.Nil(t, err)
newToken := *sessionRes.AccessToken // newToken := *sessionRes.AccessToken
assert.Equal(t, token, newToken, "tokens should be equal")
// assert.NotEqual(t, token, newToken, "tokens should not be equal")
cleanData(email) cleanData(email)
}) })

View File

@ -13,7 +13,7 @@ import (
"github.com/authorizerdev/authorizer/server/envstore" "github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/handlers" "github.com/authorizerdev/authorizer/server/handlers"
"github.com/authorizerdev/authorizer/server/middlewares" "github.com/authorizerdev/authorizer/server/middlewares"
"github.com/authorizerdev/authorizer/server/session" "github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/gin-contrib/location" "github.com/gin-contrib/location"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -75,7 +75,7 @@ func testSetup() TestSetup {
envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEnvPath, "../../.env.sample") envstore.EnvInMemoryStoreObj.UpdateEnvVariable(constants.StringStoreIdentifier, constants.EnvKeyEnvPath, "../../.env.sample")
env.InitEnv() env.InitEnv()
session.InitSession() sessionstore.InitSession()
w := httptest.NewRecorder() w := httptest.NewRecorder()
c, r := gin.CreateTestContext(w) c, r := gin.CreateTestContext(w)

View File

@ -36,7 +36,7 @@ func updateProfileTests(t *testing.T, s TestSetup) {
}) })
token := *verifyRes.AccessToken token := *verifyRes.AccessToken
req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName), token)) req.Header.Set("Cookie", fmt.Sprintf("%s=%s", envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+".access_token", token))
_, err = resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{ _, err = resolvers.UpdateProfileResolver(ctx, model.UpdateProfileInput{
FamilyName: &fName, FamilyName: &fName,
}) })

View File

@ -0,0 +1,49 @@
package token
import (
"fmt"
"log"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt"
)
// CreateAdminAuthToken creates the admin token based on secret key
func CreateAdminAuthToken(tokenType string, c *gin.Context) (string, error) {
return utils.EncryptPassword(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret))
}
// GetAdminAuthToken helps in getting the admin token from the request cookie
func GetAdminAuthToken(gc *gin.Context) (string, error) {
token, err := cookie.GetAdminCookie(gc)
if err != nil || token == "" {
return "", fmt.Errorf("unauthorized")
}
err = bcrypt.CompareHashAndPassword([]byte(token), []byte(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret)))
log.Println("error comparing hash:", err)
if err != nil {
return "", fmt.Errorf(`unauthorized`)
}
return token, nil
}
// IsSuperAdmin checks if user is super admin
func IsSuperAdmin(gc *gin.Context) bool {
token, err := GetAdminAuthToken(gc)
if err != nil {
secret := gc.Request.Header.Get("x-authorizer-admin-secret")
if secret == "" {
return false
}
return secret == envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret)
}
return token != ""
}

238
server/token/auth_token.go Normal file
View File

@ -0,0 +1,238 @@
package token
import (
"encoding/json"
"errors"
"fmt"
"log"
"os"
"strings"
"time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/sessionstore"
"github.com/authorizerdev/authorizer/server/utils"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt"
"github.com/google/uuid"
"github.com/robertkrimen/otto"
)
// JWTToken is a struct to hold JWT token and its expiration time
type JWTToken struct {
Token string `json:"token"`
ExpiresAt int64 `json:"expires_at"`
}
// Token object to hold the finger print and refresh token information
type Token struct {
FingerPrint string `json:"fingerprint"`
FingerPrintHash string `json:"fingerprint_hash"`
RefreshToken *JWTToken `json:"refresh_token"`
AccessToken *JWTToken `json:"access_token"`
}
// CreateAuthToken creates a new auth token when userlogs in
func CreateAuthToken(user models.User, roles []string) (*Token, error) {
fingerprint := uuid.NewString()
fingerPrintHashBytes, err := utils.EncryptAES([]byte(fingerprint))
if err != nil {
return nil, err
}
refreshToken, refreshTokenExpiresAt, err := CreateRefreshToken(user, roles)
if err != nil {
return nil, err
}
accessToken, accessTokenExpiresAt, err := CreateAccessToken(user, roles)
if err != nil {
return nil, err
}
return &Token{
FingerPrint: fingerprint,
FingerPrintHash: string(fingerPrintHashBytes),
RefreshToken: &JWTToken{Token: refreshToken, ExpiresAt: refreshTokenExpiresAt},
AccessToken: &JWTToken{Token: accessToken, ExpiresAt: accessTokenExpiresAt},
}, nil
}
// CreateRefreshToken util to create JWT token
func CreateRefreshToken(user models.User, roles []string) (string, int64, error) {
t := jwt.New(jwt.GetSigningMethod(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtType)))
// expires in 1 year
expiryBound := time.Hour * 8760
expiresAt := time.Now().Add(expiryBound).Unix()
customClaims := jwt.MapClaims{
"exp": expiresAt,
"iat": time.Now().Unix(),
"token_type": constants.TokenTypeRefreshToken,
"roles": roles,
"id": user.ID,
}
t.Claims = customClaims
token, err := t.SignedString([]byte(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtSecret)))
if err != nil {
return "", 0, err
}
return token, expiresAt, nil
}
// CreateAccessToken util to create JWT token, based on
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
func CreateAccessToken(user models.User, roles []string) (string, int64, error) {
t := jwt.New(jwt.GetSigningMethod(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtType)))
expiryBound := time.Minute * 30
expiresAt := time.Now().Add(expiryBound).Unix()
resUser := user.AsAPIUser()
userBytes, _ := json.Marshal(&resUser)
var userMap map[string]interface{}
json.Unmarshal(userBytes, &userMap)
claimKey := envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtRoleClaim)
customClaims := jwt.MapClaims{
"exp": expiresAt,
"iat": time.Now().Unix(),
"token_type": constants.TokenTypeAccessToken,
"allowed_roles": strings.Split(user.Roles, ","),
claimKey: roles,
}
for k, v := range userMap {
if k != "roles" {
customClaims[k] = v
}
}
// check for the extra access token script
accessTokenScript := os.Getenv(constants.EnvKeyCustomAccessTokenScript)
if accessTokenScript != "" {
vm := otto.New()
claimBytes, _ := json.Marshal(customClaims)
vm.Run(fmt.Sprintf(`
var user = %s;
var tokenPayload = %s;
var customFunction = %s;
var functionRes = JSON.stringify(customFunction(user, tokenPayload));
`, string(userBytes), string(claimBytes), accessTokenScript))
val, err := vm.Get("functionRes")
if err != nil {
log.Println("error getting custom access token script:", err)
} else {
extraPayload := make(map[string]interface{})
err = json.Unmarshal([]byte(fmt.Sprintf("%s", val)), &extraPayload)
if err != nil {
log.Println("error converting accessTokenScript response to map:", err)
} else {
for k, v := range extraPayload {
customClaims[k] = v
}
}
}
}
t.Claims = customClaims
token, err := t.SignedString([]byte(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtSecret)))
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) {
token, err := cookie.GetAccessTokenCookie(gc)
if err != nil || token == "" {
// try to check in auth header for cookie
auth := gc.Request.Header.Get("Authorization")
if auth == "" {
return "", fmt.Errorf(`unauthorized`)
}
token = strings.TrimPrefix(auth, "Bearer ")
}
return token, nil
}
// GetRefreshToken returns the refresh token from cookie / request query url
func GetRefreshToken(gc *gin.Context) (string, error) {
token, err := cookie.GetRefreshTokenCookie(gc)
if err != nil || token == "" {
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
}
// VerifyJWTToken helps in verifying the JWT token
func VerifyJWTToken(token string) (map[string]interface{}, error) {
var res map[string]interface{}
claims := jwt.MapClaims{}
t, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
return []byte(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtSecret)), nil
})
if err != nil {
return res, err
}
if !t.Valid {
return res, fmt.Errorf(`invalid token`)
}
// claim parses exp & iat into float 64 with e^10,
// but we expect it to be int64
// hence we need to assert interface and convert to int64
intExp := int64(claims["exp"].(float64))
intIat := int64(claims["iat"].(float64))
data, _ := json.Marshal(claims)
json.Unmarshal(data, &res)
res["exp"] = intExp
res["iat"] = intIat
return res, nil
}
func ValidateAccessToken(gc *gin.Context) (map[string]interface{}, error) {
token, err := GetAccessToken(gc)
if err != nil {
return nil, err
}
claims, err := VerifyJWTToken(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
}

View File

@ -1,4 +1,4 @@
package utils package token
import ( import (
"time" "time"
@ -8,10 +8,8 @@ import (
"github.com/golang-jwt/jwt" "github.com/golang-jwt/jwt"
) )
// TODO see if we can move this to different service // VerificationRequestToken is the user info that is stored in the JWT of verification request
type VerificationRequestToken struct {
// UserInfo is the user info that is stored in the JWT of verification request
type UserInfo struct {
Email string `json:"email"` Email string `json:"email"`
Host string `json:"host"` Host string `json:"host"`
RedirectURL string `json:"redirect_url"` RedirectURL string `json:"redirect_url"`
@ -21,7 +19,7 @@ type UserInfo struct {
type CustomClaim struct { type CustomClaim struct {
*jwt.StandardClaims *jwt.StandardClaims
TokenType string `json:"token_type"` TokenType string `json:"token_type"`
UserInfo VerificationRequestToken
} }
// CreateVerificationToken creates a verification JWT token // CreateVerificationToken creates a verification JWT token
@ -33,7 +31,7 @@ func CreateVerificationToken(email string, tokenType string) (string, error) {
ExpiresAt: time.Now().Add(time.Minute * 30).Unix(), ExpiresAt: time.Now().Add(time.Minute * 30).Unix(),
}, },
tokenType, tokenType,
UserInfo{Email: email, Host: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL), RedirectURL: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL)}, VerificationRequestToken{Email: email, Host: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL), RedirectURL: envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAppURL)},
} }
return t.SignedString([]byte(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtSecret))) return t.SignedString([]byte(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtSecret)))

View File

@ -1,161 +0,0 @@
package utils
import (
"encoding/json"
"fmt"
"log"
"net/url"
"os"
"strings"
"time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt"
"github.com/robertkrimen/otto"
"golang.org/x/crypto/bcrypt"
)
// CreateAuthToken util to create JWT token, based on
// user information, roles config and CUSTOM_ACCESS_TOKEN_SCRIPT
func CreateAuthToken(user models.User, tokenType string, roles []string) (string, int64, error) {
t := jwt.New(jwt.GetSigningMethod(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtType)))
expiryBound := time.Hour
if tokenType == constants.TokenTypeRefreshToken {
// expires in 1 year
expiryBound = time.Hour * 8760
}
expiresAt := time.Now().Add(expiryBound).Unix()
resUser := GetResponseUserData(user)
userBytes, _ := json.Marshal(&resUser)
var userMap map[string]interface{}
json.Unmarshal(userBytes, &userMap)
claimKey := envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtRoleClaim)
customClaims := jwt.MapClaims{
"exp": expiresAt,
"iat": time.Now().Unix(),
"token_type": tokenType,
"allowed_roles": strings.Split(user.Roles, ","),
claimKey: roles,
}
for k, v := range userMap {
if k != "roles" {
customClaims[k] = v
}
}
// check for the extra access token script
accessTokenScript := os.Getenv(constants.EnvKeyCustomAccessTokenScript)
if accessTokenScript != "" {
vm := otto.New()
claimBytes, _ := json.Marshal(customClaims)
vm.Run(fmt.Sprintf(`
var user = %s;
var tokenPayload = %s;
var customFunction = %s;
var functionRes = JSON.stringify(customFunction(user, tokenPayload));
`, string(userBytes), string(claimBytes), accessTokenScript))
val, err := vm.Get("functionRes")
if err != nil {
log.Println("error getting custom access token script:", err)
} else {
extraPayload := make(map[string]interface{})
err = json.Unmarshal([]byte(fmt.Sprintf("%s", val)), &extraPayload)
if err != nil {
log.Println("error converting accessTokenScript response to map:", err)
} else {
for k, v := range extraPayload {
customClaims[k] = v
}
}
}
}
t.Claims = customClaims
token, err := t.SignedString([]byte(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtSecret)))
if err != nil {
return "", 0, err
}
return token, expiresAt, nil
}
// GetAuthToken helps in getting the JWT token from the
// request cookie or authorization header
func GetAuthToken(gc *gin.Context) (string, error) {
token, err := GetCookie(gc)
if err != nil || token == "" {
// try to check in auth header for cookie
auth := gc.Request.Header.Get("Authorization")
if auth == "" {
return "", fmt.Errorf(`unauthorized`)
}
token = strings.TrimPrefix(auth, "Bearer ")
}
return token, nil
}
// VerifyAuthToken helps in verifying the JWT token
func VerifyAuthToken(token string) (map[string]interface{}, error) {
var res map[string]interface{}
claims := jwt.MapClaims{}
_, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {
return []byte(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtSecret)), nil
})
if err != nil {
return res, err
}
// claim parses exp & iat into float 64 with e^10,
// but we expect it to be int64
// hence we need to assert interface and convert to int64
intExp := int64(claims["exp"].(float64))
intIat := int64(claims["iat"].(float64))
data, _ := json.Marshal(claims)
json.Unmarshal(data, &res)
res["exp"] = intExp
res["iat"] = intIat
return res, nil
}
// CreateAdminAuthToken creates the admin token based on secret key
func CreateAdminAuthToken(tokenType string, c *gin.Context) (string, error) {
return EncryptPassword(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret))
}
// GetAdminAuthToken helps in getting the admin token from the request cookie
func GetAdminAuthToken(gc *gin.Context) (string, error) {
token, err := GetAdminCookie(gc)
if err != nil || token == "" {
return "", fmt.Errorf("unauthorized")
}
// cookie escapes special characters like $
// hence we need to unescape before comparing
decodedValue, err := url.QueryUnescape(token)
if err != nil {
return "", err
}
err = bcrypt.CompareHashAndPassword([]byte(decodedValue), []byte(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret)))
log.Println("error comparing hash:", err)
if err != nil {
return "", fmt.Errorf(`unauthorized`)
}
return token, nil
}

View File

@ -1,81 +0,0 @@
package utils
import (
"net/http"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/gin-gonic/gin"
)
// SetCookie sets the cookie in the response. It sets 2 cookies
// 1 COOKIE_NAME for the host (abc.com)
// 2 COOKIE_NAME-client for the domain (sub.abc.com).
// Note all sites don't allow 2nd type of cookie
func SetCookie(gc *gin.Context, token string) {
secure := true
httpOnly := true
host, _ := GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
domain := GetDomainName(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
if domain != "localhost" {
domain = "." + domain
}
gc.SetSameSite(http.SameSiteNoneMode)
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName), token, 3600, "/", host, secure, httpOnly)
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"-client", token, 3600, "/", domain, secure, httpOnly)
}
// GetCookie gets the cookie from the request
func GetCookie(gc *gin.Context) (string, error) {
cookie, err := gc.Request.Cookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName))
if err != nil {
cookie, err = gc.Request.Cookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName) + "-client")
if err != nil {
return "", err
}
}
return cookie.Value, nil
}
// DeleteCookie sets the cookie value as empty to make it expired
func DeleteCookie(gc *gin.Context) {
secure := true
httpOnly := true
host, _ := GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
domain := GetDomainName(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
if domain != "localhost" {
domain = "." + domain
}
gc.SetSameSite(http.SameSiteNoneMode)
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName), "", -1, "/", host, secure, httpOnly)
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyCookieName)+"-client", "", -1, "/", domain, secure, httpOnly)
}
// SetAdminCookie sets the admin cookie in the response
func SetAdminCookie(gc *gin.Context, token string) {
secure := true
httpOnly := true
host, _ := GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName), token, 3600, "/", host, secure, httpOnly)
}
func GetAdminCookie(gc *gin.Context) (string, error) {
cookie, err := gc.Request.Cookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName))
if err != nil {
return "", err
}
return cookie.Value, nil
}
func DeleteAdminCookie(gc *gin.Context) {
secure := true
httpOnly := true
host, _ := GetHostParts(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAuthorizerURL))
gc.SetCookie(envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminCookieName), "", -1, "/", host, secure, httpOnly)
}

View File

@ -1,32 +0,0 @@
package utils
import (
"encoding/json"
"github.com/authorizerdev/authorizer/server/envstore"
)
func EncryptConfig(data map[string]interface{}) ([]byte, error) {
jsonBytes, err := json.Marshal(data)
if err != nil {
return []byte{}, err
}
envData := envstore.EnvInMemoryStoreObj.GetEnvStoreClone()
err = json.Unmarshal(jsonBytes, &envData)
if err != nil {
return []byte{}, err
}
configData, err := json.Marshal(envData)
if err != nil {
return []byte{}, err
}
encryptedConfig, err := EncryptAES(configData)
if err != nil {
return []byte{}, err
}
return encryptedConfig, nil
}

View File

@ -1,35 +0,0 @@
package utils
import (
"strings"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
)
// TODO move this to provider
// rename it to AsAPIUser
func GetResponseUserData(user models.User) *model.User {
isEmailVerified := user.EmailVerifiedAt != nil
isPhoneVerified := user.PhoneNumberVerifiedAt != nil
return &model.User{
ID: user.ID,
Email: user.Email,
EmailVerified: isEmailVerified,
SignupMethods: user.SignupMethods,
GivenName: user.GivenName,
FamilyName: user.FamilyName,
MiddleName: user.MiddleName,
Nickname: user.Nickname,
PreferredUsername: &user.Email,
Gender: user.Gender,
Birthdate: user.Birthdate,
PhoneNumber: user.PhoneNumber,
PhoneNumberVerified: &isPhoneVerified,
Picture: user.Picture,
Roles: strings.Split(user.Roles, ","),
CreatedAt: &user.CreatedAt,
UpdatedAt: &user.UpdatedAt,
}
}

View File

@ -8,7 +8,7 @@ import (
// GetHostName function returns hostname and port // GetHostName function returns hostname and port
func GetHostParts(uri string) (string, string) { func GetHostParts(uri string) (string, string) {
tempURI := uri tempURI := uri
if !strings.HasPrefix(tempURI, "http") && strings.HasPrefix(tempURI, "https") { if !strings.HasPrefix(tempURI, "http://") && !strings.HasPrefix(tempURI, "https://") {
tempURI = "https://" + tempURI tempURI = "https://" + tempURI
} }
@ -26,7 +26,7 @@ func GetHostParts(uri string) (string, string) {
// GetDomainName function to get domain name // GetDomainName function to get domain name
func GetDomainName(uri string) string { func GetDomainName(uri string) string {
tempURI := uri tempURI := uri
if !strings.HasPrefix(tempURI, "http") && strings.HasPrefix(tempURI, "https") { if !strings.HasPrefix(tempURI, "http://") && !strings.HasPrefix(tempURI, "https://") {
tempURI = "https://" + tempURI tempURI = "https://" + tempURI
} }

View File

@ -7,7 +7,6 @@ import (
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore" "github.com/authorizerdev/authorizer/server/envstore"
"github.com/gin-gonic/gin"
) )
// IsValidEmail validates email // IsValidEmail validates email
@ -52,21 +51,6 @@ func IsValidOrigin(url string) bool {
return hasValidURL return hasValidURL
} }
// IsSuperAdmin checks if user is super admin
func IsSuperAdmin(gc *gin.Context) bool {
token, err := GetAdminAuthToken(gc)
if err != nil {
secret := gc.Request.Header.Get("x-authorizer-admin-secret")
if secret == "" {
return false
}
return secret == envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyAdminSecret)
}
return token != ""
}
// IsValidRoles validates roles // IsValidRoles validates roles
func IsValidRoles(userRoles []string, roles []string) bool { func IsValidRoles(userRoles []string, roles []string) bool {
valid := true valid := true