feat: use multi roles login (#60)
* feat: use multi roles login - add support for protected roles - refactor oauth code * fix: adminUpdate role validation * fix: update app
This commit is contained in:
parent
27944cf7b5
commit
b376ee3b73
|
@ -5,6 +5,7 @@ ADMIN_SECRET=admin
|
||||||
DISABLE_EMAIL_VERIFICATION=true
|
DISABLE_EMAIL_VERIFICATION=true
|
||||||
JWT_SECRET=random_string
|
JWT_SECRET=random_string
|
||||||
JWT_TYPE=HS256
|
JWT_TYPE=HS256
|
||||||
ROLES=user,admin
|
ROLES=user
|
||||||
DEFAULT_ROLE=user
|
DEFAULT_ROLES=user
|
||||||
|
PROTECTED_ROLES=admin
|
||||||
JWT_ROLE_CLAIM=role
|
JWT_ROLE_CLAIM=role
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
30
app/package-lock.json
generated
30
app/package-lock.json
generated
|
@ -8,7 +8,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-react": "^0.1.0-beta.18",
|
"@authorizerdev/authorizer-react": "^0.1.0-beta.19",
|
||||||
"@types/react": "^17.0.15",
|
"@types/react": "^17.0.15",
|
||||||
"@types/react-dom": "^17.0.9",
|
"@types/react-dom": "^17.0.9",
|
||||||
"esbuild": "^0.12.17",
|
"esbuild": "^0.12.17",
|
||||||
|
@ -22,9 +22,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@authorizerdev/authorizer-js": {
|
"node_modules/@authorizerdev/authorizer-js": {
|
||||||
"version": "0.1.0-beta.19",
|
"version": "0.1.0-beta.22",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.19.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.22.tgz",
|
||||||
"integrity": "sha512-//uYjklwQfQKqLJHMAyjdrzh2nz6DycB3lEgl6bTXxmSbrz+l1kQyxB3y8wP/W30IrBQz8bZb+1sau+LD/FU7g==",
|
"integrity": "sha512-fHyZDL49eEsbxxIb2xxW6iLM+/N60m5e2NeVVlMFZjZFX9eUetdFatCOrPTuukGul7l8wO6YofTnwqOLOVjKrA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"node-fetch": "^2.6.1"
|
"node-fetch": "^2.6.1"
|
||||||
},
|
},
|
||||||
|
@ -33,11 +33,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@authorizerdev/authorizer-react": {
|
"node_modules/@authorizerdev/authorizer-react": {
|
||||||
"version": "0.1.0-beta.18",
|
"version": "0.1.0-beta.19",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.18.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.19.tgz",
|
||||||
"integrity": "sha512-lRWWlS9akZwwINRW1NatsbMob06NXht3HXNTUTlu1s8m1YjxmFRE/AL6UIplzAYTpR6eDWMxEEaS0qAVxovUcg==",
|
"integrity": "sha512-ScAG3Auu0KirxuxpQ25JGtYCDmiC/3DM/ngnGO+XBaW+bWUARifH2HPkOnddUxkKPAfrKyxoa/l730W8mxhy+A==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-js": "^0.1.0-beta.19",
|
"@authorizerdev/authorizer-js": "^0.1.0-beta.22",
|
||||||
"final-form": "^4.20.2",
|
"final-form": "^4.20.2",
|
||||||
"react-final-form": "^6.5.3",
|
"react-final-form": "^6.5.3",
|
||||||
"styled-components": "^5.3.0"
|
"styled-components": "^5.3.0"
|
||||||
|
@ -797,19 +797,19 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-js": {
|
"@authorizerdev/authorizer-js": {
|
||||||
"version": "0.1.0-beta.19",
|
"version": "0.1.0-beta.22",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.19.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.1.0-beta.22.tgz",
|
||||||
"integrity": "sha512-//uYjklwQfQKqLJHMAyjdrzh2nz6DycB3lEgl6bTXxmSbrz+l1kQyxB3y8wP/W30IrBQz8bZb+1sau+LD/FU7g==",
|
"integrity": "sha512-fHyZDL49eEsbxxIb2xxW6iLM+/N60m5e2NeVVlMFZjZFX9eUetdFatCOrPTuukGul7l8wO6YofTnwqOLOVjKrA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"node-fetch": "^2.6.1"
|
"node-fetch": "^2.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@authorizerdev/authorizer-react": {
|
"@authorizerdev/authorizer-react": {
|
||||||
"version": "0.1.0-beta.18",
|
"version": "0.1.0-beta.19",
|
||||||
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.18.tgz",
|
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.1.0-beta.19.tgz",
|
||||||
"integrity": "sha512-lRWWlS9akZwwINRW1NatsbMob06NXht3HXNTUTlu1s8m1YjxmFRE/AL6UIplzAYTpR6eDWMxEEaS0qAVxovUcg==",
|
"integrity": "sha512-ScAG3Auu0KirxuxpQ25JGtYCDmiC/3DM/ngnGO+XBaW+bWUARifH2HPkOnddUxkKPAfrKyxoa/l730W8mxhy+A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@authorizerdev/authorizer-js": "^0.1.0-beta.19",
|
"@authorizerdev/authorizer-js": "^0.1.0-beta.22",
|
||||||
"final-form": "^4.20.2",
|
"final-form": "^4.20.2",
|
||||||
"react-final-form": "^6.5.3",
|
"react-final-form": "^6.5.3",
|
||||||
"styled-components": "^5.3.0"
|
"styled-components": "^5.3.0"
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"author": "Lakhan Samani",
|
"author": "Lakhan Samani",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@authorizerdev/authorizer-react": "^0.1.0-beta.18",
|
"@authorizerdev/authorizer-react": "^0.1.0-beta.19",
|
||||||
"@types/react": "^17.0.15",
|
"@types/react": "^17.0.15",
|
||||||
"@types/react-dom": "^17.0.9",
|
"@types/react-dom": "^17.0.9",
|
||||||
"esbuild": "^0.12.17",
|
"esbuild": "^0.12.17",
|
||||||
|
|
|
@ -24,7 +24,8 @@ var (
|
||||||
|
|
||||||
// ROLES
|
// ROLES
|
||||||
ROLES = []string{}
|
ROLES = []string{}
|
||||||
DEFAULT_ROLE = ""
|
PROTECTED_ROLES = []string{}
|
||||||
|
DEFAULT_ROLES = []string{}
|
||||||
JWT_ROLE_CLAIM = "role"
|
JWT_ROLE_CLAIM = "role"
|
||||||
|
|
||||||
// OAuth login
|
// OAuth login
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -63,7 +64,6 @@ func InitEnv() {
|
||||||
constants.RESET_PASSWORD_URL = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/")
|
constants.RESET_PASSWORD_URL = strings.TrimPrefix(os.Getenv("RESET_PASSWORD_URL"), "/")
|
||||||
constants.DISABLE_BASIC_AUTHENTICATION = os.Getenv("DISABLE_BASIC_AUTHENTICATION")
|
constants.DISABLE_BASIC_AUTHENTICATION = os.Getenv("DISABLE_BASIC_AUTHENTICATION")
|
||||||
constants.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION")
|
constants.DISABLE_EMAIL_VERIFICATION = os.Getenv("DISABLE_EMAIL_VERIFICATION")
|
||||||
constants.DEFAULT_ROLE = os.Getenv("DEFAULT_ROLE")
|
|
||||||
constants.JWT_ROLE_CLAIM = os.Getenv("JWT_ROLE_CLAIM")
|
constants.JWT_ROLE_CLAIM = os.Getenv("JWT_ROLE_CLAIM")
|
||||||
|
|
||||||
if constants.ADMIN_SECRET == "" {
|
if constants.ADMIN_SECRET == "" {
|
||||||
|
@ -136,7 +136,26 @@ func InitEnv() {
|
||||||
|
|
||||||
rolesSplit := strings.Split(os.Getenv("ROLES"), ",")
|
rolesSplit := strings.Split(os.Getenv("ROLES"), ",")
|
||||||
roles := []string{}
|
roles := []string{}
|
||||||
defaultRole := ""
|
if len(rolesSplit) == 0 {
|
||||||
|
roles = []string{"user"}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultRoleSplit := strings.Split(os.Getenv("DEFAULT_ROLES"), ",")
|
||||||
|
defaultRoles := []string{}
|
||||||
|
|
||||||
|
if len(defaultRoleSplit) == 0 {
|
||||||
|
defaultRoles = []string{"user"}
|
||||||
|
}
|
||||||
|
|
||||||
|
protectedRolesSplit := strings.Split(os.Getenv("PROTECTED_ROLES"), ",")
|
||||||
|
protectedRoles := []string{}
|
||||||
|
|
||||||
|
if len(protectedRolesSplit) > 0 {
|
||||||
|
for _, val := range protectedRolesSplit {
|
||||||
|
trimVal := strings.TrimSpace(val)
|
||||||
|
protectedRoles = append(protectedRoles, trimVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, val := range rolesSplit {
|
for _, val := range rolesSplit {
|
||||||
trimVal := strings.TrimSpace(val)
|
trimVal := strings.TrimSpace(val)
|
||||||
|
@ -144,20 +163,18 @@ func InitEnv() {
|
||||||
roles = append(roles, trimVal)
|
roles = append(roles, trimVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
if trimVal == constants.DEFAULT_ROLE {
|
if utils.StringContains(defaultRoleSplit, trimVal) {
|
||||||
defaultRole = trimVal
|
defaultRoles = append(defaultRoles, trimVal)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(roles) > 0 && defaultRole == "" {
|
|
||||||
|
if len(roles) > 0 && len(defaultRoles) == 0 && len(defaultRoleSplit) > 0 {
|
||||||
panic(`Invalid DEFAULT_ROLE environment variable. It can be one from give ROLES environment variable value`)
|
panic(`Invalid DEFAULT_ROLE environment variable. It can be one from give ROLES environment variable value`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(roles) == 0 {
|
|
||||||
roles = []string{"user", "admin"}
|
|
||||||
constants.DEFAULT_ROLE = "user"
|
|
||||||
}
|
|
||||||
|
|
||||||
constants.ROLES = roles
|
constants.ROLES = roles
|
||||||
|
constants.DEFAULT_ROLES = defaultRoles
|
||||||
|
constants.PROTECTED_ROLES = protectedRoles
|
||||||
|
|
||||||
if constants.JWT_ROLE_CLAIM == "" {
|
if constants.JWT_ROLE_CLAIM == "" {
|
||||||
constants.JWT_ROLE_CLAIM = "role"
|
constants.JWT_ROLE_CLAIM = "role"
|
||||||
|
|
|
@ -81,7 +81,7 @@ type ComplexityRoot struct {
|
||||||
Query struct {
|
Query struct {
|
||||||
Meta func(childComplexity int) int
|
Meta func(childComplexity int) int
|
||||||
Profile func(childComplexity int) int
|
Profile func(childComplexity int) int
|
||||||
Token func(childComplexity int, role *string) int
|
Token func(childComplexity int, roles []string) int
|
||||||
Users func(childComplexity int) int
|
Users func(childComplexity int) int
|
||||||
VerificationRequests func(childComplexity int) int
|
VerificationRequests func(childComplexity int) int
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ type MutationResolver interface {
|
||||||
type QueryResolver interface {
|
type QueryResolver interface {
|
||||||
Meta(ctx context.Context) (*model.Meta, error)
|
Meta(ctx context.Context) (*model.Meta, error)
|
||||||
Users(ctx context.Context) ([]*model.User, error)
|
Users(ctx context.Context) ([]*model.User, error)
|
||||||
Token(ctx context.Context, role *string) (*model.AuthResponse, error)
|
Token(ctx context.Context, roles []string) (*model.AuthResponse, error)
|
||||||
Profile(ctx context.Context) (*model.User, error)
|
Profile(ctx context.Context) (*model.User, error)
|
||||||
VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error)
|
VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error)
|
||||||
}
|
}
|
||||||
|
@ -379,7 +379,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.complexity.Query.Token(childComplexity, args["role"].(*string)), true
|
return e.complexity.Query.Token(childComplexity, args["roles"].([]string)), true
|
||||||
|
|
||||||
case "Query.users":
|
case "Query.users":
|
||||||
if e.complexity.Query.Users == nil {
|
if e.complexity.Query.Users == nil {
|
||||||
|
@ -648,13 +648,13 @@ input SignUpInput {
|
||||||
password: String!
|
password: String!
|
||||||
confirmPassword: String!
|
confirmPassword: String!
|
||||||
image: String
|
image: String
|
||||||
roles: [String]
|
roles: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input LoginInput {
|
input LoginInput {
|
||||||
email: String!
|
email: String!
|
||||||
password: String!
|
password: String!
|
||||||
role: String
|
roles: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input VerifyEmailInput {
|
input VerifyEmailInput {
|
||||||
|
@ -715,7 +715,7 @@ type Mutation {
|
||||||
type Query {
|
type Query {
|
||||||
meta: Meta!
|
meta: Meta!
|
||||||
users: [User!]!
|
users: [User!]!
|
||||||
token(role: String): AuthResponse
|
token(roles: [String!]): AuthResponse
|
||||||
profile: User!
|
profile: User!
|
||||||
verificationRequests: [VerificationRequest!]!
|
verificationRequests: [VerificationRequest!]!
|
||||||
}
|
}
|
||||||
|
@ -880,15 +880,15 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs
|
||||||
func (ec *executionContext) field_Query_token_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (ec *executionContext) field_Query_token_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
var err error
|
var err error
|
||||||
args := map[string]interface{}{}
|
args := map[string]interface{}{}
|
||||||
var arg0 *string
|
var arg0 []string
|
||||||
if tmp, ok := rawArgs["role"]; ok {
|
if tmp, ok := rawArgs["roles"]; ok {
|
||||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("role"))
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roles"))
|
||||||
arg0, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
|
arg0, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
args["role"] = arg0
|
args["roles"] = arg0
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1884,7 +1884,7 @@ func (ec *executionContext) _Query_token(ctx context.Context, field graphql.Coll
|
||||||
fc.Args = args
|
fc.Args = args
|
||||||
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
|
||||||
ctx = rctx // use context from middleware stack in children
|
ctx = rctx // use context from middleware stack in children
|
||||||
return ec.resolvers.Query().Token(rctx, args["role"].(*string))
|
return ec.resolvers.Query().Token(rctx, args["roles"].([]string))
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
|
@ -3842,11 +3842,11 @@ func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj in
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
case "role":
|
case "roles":
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("role"))
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roles"))
|
||||||
it.Role, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
it.Roles, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
|
@ -3970,7 +3970,7 @@ func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj i
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roles"))
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roles"))
|
||||||
it.Roles, err = ec.unmarshalOString2ᚕᚖstring(ctx, v)
|
it.Roles, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return it, err
|
return it, err
|
||||||
}
|
}
|
||||||
|
@ -5280,6 +5280,42 @@ func (ec *executionContext) marshalOString2string(ctx context.Context, sel ast.S
|
||||||
return graphql.MarshalString(v)
|
return graphql.MarshalString(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalOString2ᚕstringᚄ(ctx context.Context, v interface{}) ([]string, error) {
|
||||||
|
if v == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
var vSlice []interface{}
|
||||||
|
if v != nil {
|
||||||
|
if tmp1, ok := v.([]interface{}); ok {
|
||||||
|
vSlice = tmp1
|
||||||
|
} else {
|
||||||
|
vSlice = []interface{}{v}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
res := make([]string, len(vSlice))
|
||||||
|
for i := range vSlice {
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i))
|
||||||
|
res[i], err = ec.unmarshalNString2string(ctx, vSlice[i])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) marshalOString2ᚕstringᚄ(ctx context.Context, sel ast.SelectionSet, v []string) graphql.Marshaler {
|
||||||
|
if v == nil {
|
||||||
|
return graphql.Null
|
||||||
|
}
|
||||||
|
ret := make(graphql.Array, len(v))
|
||||||
|
for i := range v {
|
||||||
|
ret[i] = ec.marshalNString2string(ctx, sel, v[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalOString2ᚕᚖstring(ctx context.Context, v interface{}) ([]*string, error) {
|
func (ec *executionContext) unmarshalOString2ᚕᚖstring(ctx context.Context, v interface{}) ([]*string, error) {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
|
@ -34,7 +34,7 @@ type ForgotPasswordInput struct {
|
||||||
type LoginInput struct {
|
type LoginInput struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Role *string `json:"role"`
|
Roles []string `json:"roles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Meta struct {
|
type Meta struct {
|
||||||
|
@ -68,7 +68,7 @@ type SignUpInput struct {
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
ConfirmPassword string `json:"confirmPassword"`
|
ConfirmPassword string `json:"confirmPassword"`
|
||||||
Image *string `json:"image"`
|
Image *string `json:"image"`
|
||||||
Roles []*string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateProfileInput struct {
|
type UpdateProfileInput struct {
|
||||||
|
|
|
@ -61,13 +61,13 @@ input SignUpInput {
|
||||||
password: String!
|
password: String!
|
||||||
confirmPassword: String!
|
confirmPassword: String!
|
||||||
image: String
|
image: String
|
||||||
roles: [String]
|
roles: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input LoginInput {
|
input LoginInput {
|
||||||
email: String!
|
email: String!
|
||||||
password: String!
|
password: String!
|
||||||
role: String
|
roles: [String!]
|
||||||
}
|
}
|
||||||
|
|
||||||
input VerifyEmailInput {
|
input VerifyEmailInput {
|
||||||
|
@ -128,7 +128,7 @@ type Mutation {
|
||||||
type Query {
|
type Query {
|
||||||
meta: Meta!
|
meta: Meta!
|
||||||
users: [User!]!
|
users: [User!]!
|
||||||
token(role: String): AuthResponse
|
token(roles: [String!]): AuthResponse
|
||||||
profile: User!
|
profile: User!
|
||||||
verificationRequests: [VerificationRequest!]!
|
verificationRequests: [VerificationRequest!]!
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,8 +59,8 @@ func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
|
||||||
return resolvers.Users(ctx)
|
return resolvers.Users(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) Token(ctx context.Context, role *string) (*model.AuthResponse, error) {
|
func (r *queryResolver) Token(ctx context.Context, roles []string) (*model.AuthResponse, error) {
|
||||||
return resolvers.Token(ctx, role)
|
return resolvers.Token(ctx, roles)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) Profile(ctx context.Context) (*model.User, error) {
|
func (r *queryResolver) Profile(ctx context.Context) (*model.User, error) {
|
||||||
|
|
|
@ -19,28 +19,29 @@ import (
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func processGoogleUserInfo(code string, role string, c *gin.Context) error {
|
func processGoogleUserInfo(code string, roles []string, c *gin.Context) (db.User, error) {
|
||||||
|
user := db.User{}
|
||||||
token, err := oauth.OAuthProvider.GoogleConfig.Exchange(oauth2.NoContext, code)
|
token, err := oauth.OAuthProvider.GoogleConfig.Exchange(oauth2.NoContext, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid google exchange code: %s", err.Error())
|
return user, fmt.Errorf("invalid google exchange code: %s", err.Error())
|
||||||
}
|
}
|
||||||
client := oauth.OAuthProvider.GoogleConfig.Client(oauth2.NoContext, token)
|
client := oauth.OAuthProvider.GoogleConfig.Client(oauth2.NoContext, token)
|
||||||
response, err := client.Get(constants.GoogleUserInfoURL)
|
response, err := client.Get(constants.GoogleUserInfoURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
body, err := ioutil.ReadAll(response.Body)
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read google response body: %s", err.Error())
|
return user, fmt.Errorf("failed to read google response body: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
userRawData := make(map[string]string)
|
userRawData := make(map[string]string)
|
||||||
json.Unmarshal(body, &userRawData)
|
json.Unmarshal(body, &userRawData)
|
||||||
|
|
||||||
existingUser, err := db.Mgr.GetUserByEmail(userRawData["email"])
|
existingUser, err := db.Mgr.GetUserByEmail(userRawData["email"])
|
||||||
user := db.User{
|
user = db.User{
|
||||||
FirstName: userRawData["given_name"],
|
FirstName: userRawData["given_name"],
|
||||||
LastName: userRawData["family_name"],
|
LastName: userRawData["family_name"],
|
||||||
Image: userRawData["picture"],
|
Image: userRawData["picture"],
|
||||||
|
@ -50,7 +51,7 @@ func processGoogleUserInfo(code string, role string, c *gin.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// user not registered, register user and generate session token
|
// user not registered, register user and generate session token
|
||||||
user.SignupMethod = enum.Google.String()
|
user.SignupMethod = enum.Google.String()
|
||||||
user.Roles = role
|
user.Roles = strings.Join(roles, ",")
|
||||||
} else {
|
} else {
|
||||||
// user exists in db, check if method was google
|
// user exists in db, check if method was google
|
||||||
// if not append google to existing signup method and save it
|
// if not append google to existing signup method and save it
|
||||||
|
@ -61,34 +62,25 @@ func processGoogleUserInfo(code string, role string, c *gin.Context) error {
|
||||||
}
|
}
|
||||||
user.SignupMethod = signupMethod
|
user.SignupMethod = signupMethod
|
||||||
user.Password = existingUser.Password
|
user.Password = existingUser.Password
|
||||||
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
|
if !utils.IsValidRoles(strings.Split(existingUser.Roles, ","), roles) {
|
||||||
return fmt.Errorf("invalid role")
|
return user, fmt.Errorf("invalid role")
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Roles = existingUser.Roles
|
user.Roles = existingUser.Roles
|
||||||
}
|
}
|
||||||
|
return user, nil
|
||||||
user, _ = db.Mgr.SaveUser(user)
|
|
||||||
user, _ = db.Mgr.GetUserByEmail(user.Email)
|
|
||||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
|
||||||
|
|
||||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
|
|
||||||
|
|
||||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
|
|
||||||
utils.SetCookie(c, accessToken)
|
|
||||||
session.SetToken(userIdStr, refreshToken)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func processGithubUserInfo(code string, role string, c *gin.Context) error {
|
func processGithubUserInfo(code string, roles []string, c *gin.Context) (db.User, error) {
|
||||||
|
user := db.User{}
|
||||||
token, err := oauth.OAuthProvider.GithubConfig.Exchange(oauth2.NoContext, code)
|
token, err := oauth.OAuthProvider.GithubConfig.Exchange(oauth2.NoContext, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid github exchange code: %s", err.Error())
|
return user, fmt.Errorf("invalid github exchange code: %s", err.Error())
|
||||||
}
|
}
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
req, err := http.NewRequest("GET", constants.GithubUserInfoURL, nil)
|
req, err := http.NewRequest("GET", constants.GithubUserInfoURL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating github user info request: %s", err.Error())
|
return user, fmt.Errorf("error creating github user info request: %s", err.Error())
|
||||||
}
|
}
|
||||||
req.Header = http.Header{
|
req.Header = http.Header{
|
||||||
"Authorization": []string{fmt.Sprintf("token %s", token.AccessToken)},
|
"Authorization": []string{fmt.Sprintf("token %s", token.AccessToken)},
|
||||||
|
@ -96,13 +88,13 @@ func processGithubUserInfo(code string, role string, c *gin.Context) error {
|
||||||
|
|
||||||
response, err := client.Do(req)
|
response, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
body, err := ioutil.ReadAll(response.Body)
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read github response body: %s", err.Error())
|
return user, fmt.Errorf("failed to read github response body: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
userRawData := make(map[string]string)
|
userRawData := make(map[string]string)
|
||||||
|
@ -118,7 +110,7 @@ func processGithubUserInfo(code string, role string, c *gin.Context) error {
|
||||||
if len(name) > 1 && strings.TrimSpace(name[1]) != "" {
|
if len(name) > 1 && strings.TrimSpace(name[1]) != "" {
|
||||||
lastName = name[0]
|
lastName = name[0]
|
||||||
}
|
}
|
||||||
user := db.User{
|
user = db.User{
|
||||||
FirstName: firstName,
|
FirstName: firstName,
|
||||||
LastName: lastName,
|
LastName: lastName,
|
||||||
Image: userRawData["avatar_url"],
|
Image: userRawData["avatar_url"],
|
||||||
|
@ -128,7 +120,7 @@ func processGithubUserInfo(code string, role string, c *gin.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// user not registered, register user and generate session token
|
// user not registered, register user and generate session token
|
||||||
user.SignupMethod = enum.Github.String()
|
user.SignupMethod = enum.Github.String()
|
||||||
user.Roles = role
|
user.Roles = strings.Join(roles, ",")
|
||||||
} else {
|
} else {
|
||||||
// user exists in db, check if method was google
|
// user exists in db, check if method was google
|
||||||
// if not append google to existing signup method and save it
|
// if not append google to existing signup method and save it
|
||||||
|
@ -140,45 +132,38 @@ func processGithubUserInfo(code string, role string, c *gin.Context) error {
|
||||||
user.SignupMethod = signupMethod
|
user.SignupMethod = signupMethod
|
||||||
user.Password = existingUser.Password
|
user.Password = existingUser.Password
|
||||||
|
|
||||||
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
|
if !utils.IsValidRoles(strings.Split(existingUser.Roles, ","), roles) {
|
||||||
return fmt.Errorf("invalid role")
|
return user, fmt.Errorf("invalid role")
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Roles = existingUser.Roles
|
user.Roles = existingUser.Roles
|
||||||
}
|
}
|
||||||
|
|
||||||
user, _ = db.Mgr.SaveUser(user)
|
return user, nil
|
||||||
user, _ = db.Mgr.GetUserByEmail(user.Email)
|
|
||||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
|
||||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
|
|
||||||
|
|
||||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
|
|
||||||
utils.SetCookie(c, accessToken)
|
|
||||||
session.SetToken(userIdStr, refreshToken)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func processFacebookUserInfo(code string, role string, c *gin.Context) error {
|
func processFacebookUserInfo(code string, roles []string, c *gin.Context) (db.User, error) {
|
||||||
|
user := db.User{}
|
||||||
token, err := oauth.OAuthProvider.FacebookConfig.Exchange(oauth2.NoContext, code)
|
token, err := oauth.OAuthProvider.FacebookConfig.Exchange(oauth2.NoContext, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid facebook exchange code: %s", err.Error())
|
return user, fmt.Errorf("invalid facebook exchange code: %s", err.Error())
|
||||||
}
|
}
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
req, err := http.NewRequest("GET", constants.FacebookUserInfoURL+token.AccessToken, nil)
|
req, err := http.NewRequest("GET", constants.FacebookUserInfoURL+token.AccessToken, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating facebook user info request: %s", err.Error())
|
return user, fmt.Errorf("error creating facebook user info request: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
response, err := client.Do(req)
|
response, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("err:", err)
|
log.Println("err:", err)
|
||||||
return err
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer response.Body.Close()
|
defer response.Body.Close()
|
||||||
body, err := ioutil.ReadAll(response.Body)
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to read facebook response body: %s", err.Error())
|
return user, fmt.Errorf("failed to read facebook response body: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
userRawData := make(map[string]interface{})
|
userRawData := make(map[string]interface{})
|
||||||
|
@ -189,7 +174,7 @@ func processFacebookUserInfo(code string, role string, c *gin.Context) error {
|
||||||
|
|
||||||
picObject := userRawData["picture"].(map[string]interface{})["data"]
|
picObject := userRawData["picture"].(map[string]interface{})["data"]
|
||||||
picDataObject := picObject.(map[string]interface{})
|
picDataObject := picObject.(map[string]interface{})
|
||||||
user := db.User{
|
user = db.User{
|
||||||
FirstName: fmt.Sprintf("%v", userRawData["first_name"]),
|
FirstName: fmt.Sprintf("%v", userRawData["first_name"]),
|
||||||
LastName: fmt.Sprintf("%v", userRawData["last_name"]),
|
LastName: fmt.Sprintf("%v", userRawData["last_name"]),
|
||||||
Image: fmt.Sprintf("%v", picDataObject["url"]),
|
Image: fmt.Sprintf("%v", picDataObject["url"]),
|
||||||
|
@ -200,7 +185,7 @@ func processFacebookUserInfo(code string, role string, c *gin.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// user not registered, register user and generate session token
|
// user not registered, register user and generate session token
|
||||||
user.SignupMethod = enum.Github.String()
|
user.SignupMethod = enum.Github.String()
|
||||||
user.Roles = role
|
user.Roles = strings.Join(roles, ",")
|
||||||
} else {
|
} else {
|
||||||
// user exists in db, check if method was google
|
// user exists in db, check if method was google
|
||||||
// if not append google to existing signup method and save it
|
// if not append google to existing signup method and save it
|
||||||
|
@ -212,22 +197,14 @@ func processFacebookUserInfo(code string, role string, c *gin.Context) error {
|
||||||
user.SignupMethod = signupMethod
|
user.SignupMethod = signupMethod
|
||||||
user.Password = existingUser.Password
|
user.Password = existingUser.Password
|
||||||
|
|
||||||
if !utils.IsValidRole(strings.Split(existingUser.Roles, ","), role) {
|
if !utils.IsValidRoles(strings.Split(existingUser.Roles, ","), roles) {
|
||||||
return fmt.Errorf("invalid role")
|
return user, fmt.Errorf("invalid role")
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Roles = existingUser.Roles
|
user.Roles = existingUser.Roles
|
||||||
}
|
}
|
||||||
|
|
||||||
user, _ = db.Mgr.SaveUser(user)
|
return user, nil
|
||||||
user, _ = db.Mgr.GetUserByEmail(user.Email)
|
|
||||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
|
||||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
|
|
||||||
|
|
||||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
|
|
||||||
utils.SetCookie(c, accessToken)
|
|
||||||
session.SetToken(userIdStr, refreshToken)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func OAuthCallbackHandler() gin.HandlerFunc {
|
func OAuthCallbackHandler() gin.HandlerFunc {
|
||||||
|
@ -249,18 +226,19 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
role := sessionSplit[2]
|
roles := strings.Split(sessionSplit[2], ",")
|
||||||
redirectURL := sessionSplit[1]
|
redirectURL := sessionSplit[1]
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
user := db.User{}
|
||||||
code := c.Request.FormValue("code")
|
code := c.Request.FormValue("code")
|
||||||
switch provider {
|
switch provider {
|
||||||
case enum.Google.String():
|
case enum.Google.String():
|
||||||
err = processGoogleUserInfo(code, role, c)
|
user, err = processGoogleUserInfo(code, roles, c)
|
||||||
case enum.Github.String():
|
case enum.Github.String():
|
||||||
err = processGithubUserInfo(code, role, c)
|
user, err = processGithubUserInfo(code, roles, c)
|
||||||
case enum.Facebook.String():
|
case enum.Facebook.String():
|
||||||
err = processFacebookUserInfo(code, role, c)
|
user, err = processFacebookUserInfo(code, roles, c)
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf(`invalid oauth provider`)
|
err = fmt.Errorf(`invalid oauth provider`)
|
||||||
}
|
}
|
||||||
|
@ -269,6 +247,16 @@ func OAuthCallbackHandler() gin.HandlerFunc {
|
||||||
c.JSON(400, gin.H{"error": err.Error()})
|
c.JSON(400, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
user, _ = db.Mgr.SaveUser(user)
|
||||||
|
user, _ = db.Mgr.GetUserByEmail(user.Email)
|
||||||
|
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||||
|
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
|
||||||
|
|
||||||
|
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||||
|
utils.SetCookie(c, accessToken)
|
||||||
|
session.SetToken(userIdStr, refreshToken)
|
||||||
|
|
||||||
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
c.Redirect(http.StatusTemporaryRedirect, redirectURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/enum"
|
"github.com/authorizerdev/authorizer/server/enum"
|
||||||
|
@ -18,7 +19,7 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
// TODO validate redirect URL
|
// TODO validate redirect URL
|
||||||
redirectURL := c.Query("redirectURL")
|
redirectURL := c.Query("redirectURL")
|
||||||
role := c.Query("role")
|
roles := c.Query("roles")
|
||||||
|
|
||||||
if redirectURL == "" {
|
if redirectURL == "" {
|
||||||
c.JSON(400, gin.H{
|
c.JSON(400, gin.H{
|
||||||
|
@ -27,20 +28,24 @@ func OAuthLoginHandler() gin.HandlerFunc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if role != "" {
|
if roles != "" {
|
||||||
// validate role
|
// validate role
|
||||||
if !utils.IsValidRole(constants.ROLES, role) {
|
rolesSplit := strings.Split(roles, ",")
|
||||||
|
|
||||||
|
// use protected roles verification for admin login only.
|
||||||
|
// though if not associated with user, it will be rejected from oauth_callback
|
||||||
|
if !utils.IsValidRoles(append([]string{}, append(constants.ROLES, constants.PROTECTED_ROLES...)...), rolesSplit) {
|
||||||
c.JSON(400, gin.H{
|
c.JSON(400, gin.H{
|
||||||
"error": "invalid role",
|
"error": "invalid role",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
role = constants.DEFAULT_ROLE
|
roles = strings.Join(constants.DEFAULT_ROLES, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
uuid := uuid.New()
|
uuid := uuid.New()
|
||||||
oauthStateString := uuid.String() + "___" + redirectURL + "___" + role
|
oauthStateString := uuid.String() + "___" + redirectURL + "___" + roles
|
||||||
|
|
||||||
provider := c.Param("oauth_provider")
|
provider := c.Param("oauth_provider")
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package handlers
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
|
@ -50,9 +51,10 @@ func VerifyEmailHandler() gin.HandlerFunc {
|
||||||
db.Mgr.DeleteToken(claim.Email)
|
db.Mgr.DeleteToken(claim.Email)
|
||||||
|
|
||||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, user.Roles)
|
roles := strings.Split(user.Roles, ",")
|
||||||
|
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
|
||||||
|
|
||||||
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, user.Roles)
|
accessToken, _, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||||
|
|
||||||
session.SetToken(userIdStr, refreshToken)
|
session.SetToken(userIdStr, refreshToken)
|
||||||
utils.SetCookie(c, accessToken)
|
utils.SetCookie(c, accessToken)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/enum"
|
"github.com/authorizerdev/authorizer/server/enum"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
@ -91,7 +92,7 @@ func AdminUpdateUser(ctx context.Context, params model.AdminUpdateUserInput) (*m
|
||||||
inputRoles = append(inputRoles, *item)
|
inputRoles = append(inputRoles, *item)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !utils.IsValidRolesArray(inputRoles) {
|
if !utils.IsValidRoles(append([]string{}, append(constants.ROLES, constants.PROTECTED_ROLES...)...), inputRoles) {
|
||||||
return res, fmt.Errorf("invalid list of roles")
|
return res, fmt.Errorf("invalid list of roles")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,19 +46,19 @@ func Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, e
|
||||||
log.Println("Compare password error:", err)
|
log.Println("Compare password error:", err)
|
||||||
return res, fmt.Errorf(`invalid password`)
|
return res, fmt.Errorf(`invalid password`)
|
||||||
}
|
}
|
||||||
role := constants.DEFAULT_ROLE
|
roles := constants.DEFAULT_ROLES
|
||||||
if params.Role != nil {
|
currentRoles := strings.Split(user.Roles, ",")
|
||||||
// validate role
|
if len(params.Roles) > 0 {
|
||||||
if !utils.IsValidRole(strings.Split(user.Roles, ","), *params.Role) {
|
if !utils.IsValidRoles(currentRoles, params.Roles) {
|
||||||
return res, fmt.Errorf(`invalid role`)
|
return res, fmt.Errorf(`invalid roles`)
|
||||||
}
|
}
|
||||||
|
|
||||||
role = *params.Role
|
roles = params.Roles
|
||||||
}
|
}
|
||||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, role)
|
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
|
||||||
|
|
||||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, role)
|
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||||
|
|
||||||
session.SetToken(userIdStr, refreshToken)
|
session.SetToken(userIdStr, refreshToken)
|
||||||
|
|
||||||
|
|
|
@ -37,16 +37,15 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||||
|
|
||||||
inputRoles := []string{}
|
inputRoles := []string{}
|
||||||
|
|
||||||
if params.Roles != nil && len(params.Roles) > 0 {
|
if len(params.Roles) > 0 {
|
||||||
// check if roles exists
|
// check if roles exists
|
||||||
for _, item := range params.Roles {
|
if !utils.IsValidRoles(constants.ROLES, params.Roles) {
|
||||||
inputRoles = append(inputRoles, *item)
|
|
||||||
}
|
|
||||||
if !utils.IsValidRolesArray(inputRoles) {
|
|
||||||
return res, fmt.Errorf(`invalid roles`)
|
return res, fmt.Errorf(`invalid roles`)
|
||||||
|
} else {
|
||||||
|
inputRoles = params.Roles
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
inputRoles = []string{constants.DEFAULT_ROLE}
|
inputRoles = constants.DEFAULT_ROLES
|
||||||
}
|
}
|
||||||
|
|
||||||
// find user with email
|
// find user with email
|
||||||
|
@ -85,6 +84,7 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||||
|
roles := strings.Split(user.Roles, ",")
|
||||||
userToReturn := &model.User{
|
userToReturn := &model.User{
|
||||||
ID: userIdStr,
|
ID: userIdStr,
|
||||||
Email: user.Email,
|
Email: user.Email,
|
||||||
|
@ -123,9 +123,9 @@ func Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, constants.DEFAULT_ROLE)
|
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
|
||||||
|
|
||||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, constants.DEFAULT_ROLE)
|
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||||
|
|
||||||
session.SetToken(userIdStr, refreshToken)
|
session.SetToken(userIdStr, refreshToken)
|
||||||
res = &model.AuthResponse{
|
res = &model.AuthResponse{
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"github.com/authorizerdev/authorizer/server/utils"
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Token(ctx context.Context, role *string) (*model.AuthResponse, error) {
|
func Token(ctx context.Context, roles []string) (*model.AuthResponse, error) {
|
||||||
var res *model.AuthResponse
|
var res *model.AuthResponse
|
||||||
|
|
||||||
gc, err := utils.GinContextFromContext(ctx)
|
gc, err := utils.GinContextFromContext(ctx)
|
||||||
|
@ -30,16 +30,11 @@ func Token(ctx context.Context, role *string) (*model.AuthResponse, error) {
|
||||||
expiresAt := claim["exp"].(int64)
|
expiresAt := claim["exp"].(int64)
|
||||||
email := fmt.Sprintf("%v", claim["email"])
|
email := fmt.Sprintf("%v", claim["email"])
|
||||||
|
|
||||||
claimRole := fmt.Sprintf("%v", claim[constants.JWT_ROLE_CLAIM])
|
|
||||||
user, err := db.Mgr.GetUserByEmail(email)
|
user, err := db.Mgr.GetUserByEmail(email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return res, err
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if role != nil && *role != claimRole {
|
|
||||||
return res, fmt.Errorf(`unauthorized`)
|
|
||||||
}
|
|
||||||
|
|
||||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||||
|
|
||||||
sessionToken := session.GetToken(userIdStr)
|
sessionToken := session.GetToken(userIdStr)
|
||||||
|
@ -47,15 +42,30 @@ func Token(ctx context.Context, role *string) (*model.AuthResponse, error) {
|
||||||
if sessionToken == "" {
|
if sessionToken == "" {
|
||||||
return res, fmt.Errorf(`unauthorized`)
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
}
|
}
|
||||||
// TODO check if refresh/session token has expired
|
|
||||||
|
|
||||||
expiresTimeObj := time.Unix(expiresAt, 0)
|
expiresTimeObj := time.Unix(expiresAt, 0)
|
||||||
currentTimeObj := time.Now()
|
currentTimeObj := time.Now()
|
||||||
|
|
||||||
|
claimRoleInterface := claim[constants.JWT_ROLE_CLAIM].([]interface{})
|
||||||
|
claimRoles := make([]string, len(claimRoleInterface))
|
||||||
|
for i, v := range claimRoleInterface {
|
||||||
|
claimRoles[i] = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(roles) > 0 {
|
||||||
|
for _, v := range roles {
|
||||||
|
if !utils.StringContains(claimRoles, v) {
|
||||||
|
return res, fmt.Errorf(`unauthorized`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if accessTokenErr != nil || expiresTimeObj.Sub(currentTimeObj).Minutes() <= 5 {
|
if accessTokenErr != nil || expiresTimeObj.Sub(currentTimeObj).Minutes() <= 5 {
|
||||||
// if access token has expired and refresh/session token is valid
|
// if access token has expired and refresh/session token is valid
|
||||||
// generate new accessToken
|
// generate new accessToken
|
||||||
token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRole)
|
token, expiresAt, _ = utils.CreateAuthToken(user, enum.AccessToken, claimRoles)
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.SetCookie(gc, token)
|
utils.SetCookie(gc, token)
|
||||||
res = &model.AuthResponse{
|
res = &model.AuthResponse{
|
||||||
Message: `Token verified`,
|
Message: `Token verified`,
|
||||||
|
|
|
@ -124,31 +124,6 @@ func UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO this idea needs to be verified otherwise every user can make themselves super admin
|
|
||||||
// rolesToSave := ""
|
|
||||||
// if params.Roles != nil && len(params.Roles) > 0 {
|
|
||||||
// currentRoles := strings.Split(user.Roles, ",")
|
|
||||||
// inputRoles := []string{}
|
|
||||||
// for _, item := range params.Roles {
|
|
||||||
// inputRoles = append(inputRoles, *item)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if !utils.IsValidRolesArray(inputRoles) {
|
|
||||||
// return res, fmt.Errorf("invalid list of roles")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if !utils.IsStringArrayEqual(inputRoles, currentRoles) {
|
|
||||||
// rolesToSave = strings.Join(inputRoles, ",")
|
|
||||||
// }
|
|
||||||
|
|
||||||
// session.DeleteToken(fmt.Sprintf("%v", user.ID))
|
|
||||||
// utils.DeleteCookie(gc)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if rolesToSave != "" {
|
|
||||||
// user.Roles = rolesToSave
|
|
||||||
// }
|
|
||||||
|
|
||||||
_, err = db.Mgr.UpdateUser(user)
|
_, err = db.Mgr.UpdateUser(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Error updating user:", err)
|
log.Println("Error updating user:", err)
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/authorizerdev/authorizer/server/constants"
|
|
||||||
"github.com/authorizerdev/authorizer/server/db"
|
"github.com/authorizerdev/authorizer/server/db"
|
||||||
"github.com/authorizerdev/authorizer/server/enum"
|
"github.com/authorizerdev/authorizer/server/enum"
|
||||||
"github.com/authorizerdev/authorizer/server/graph/model"
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
@ -43,9 +42,10 @@ func VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.Aut
|
||||||
db.Mgr.DeleteToken(claim.Email)
|
db.Mgr.DeleteToken(claim.Email)
|
||||||
|
|
||||||
userIdStr := fmt.Sprintf("%v", user.ID)
|
userIdStr := fmt.Sprintf("%v", user.ID)
|
||||||
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, constants.DEFAULT_ROLE)
|
roles := strings.Split(user.Roles, ",")
|
||||||
|
refreshToken, _, _ := utils.CreateAuthToken(user, enum.RefreshToken, roles)
|
||||||
|
|
||||||
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, constants.DEFAULT_ROLE)
|
accessToken, expiresAt, _ := utils.CreateAuthToken(user, enum.AccessToken, roles)
|
||||||
|
|
||||||
session.SetToken(userIdStr, refreshToken)
|
session.SetToken(userIdStr, refreshToken)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ type UserAuthClaim struct {
|
||||||
*JWTCustomClaim `json:"authorizer"`
|
*JWTCustomClaim `json:"authorizer"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateAuthToken(user db.User, tokenType enum.TokenType, role string) (string, int64, error) {
|
func CreateAuthToken(user db.User, tokenType enum.TokenType, roles []string) (string, int64, error) {
|
||||||
t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE))
|
t := jwt.New(jwt.GetSigningMethod(constants.JWT_TYPE))
|
||||||
expiryBound := time.Hour
|
expiryBound := time.Hour
|
||||||
if tokenType == enum.RefreshToken {
|
if tokenType == enum.RefreshToken {
|
||||||
|
@ -41,7 +41,7 @@ func CreateAuthToken(user db.User, tokenType enum.TokenType, role string) (strin
|
||||||
"email": user.Email,
|
"email": user.Email,
|
||||||
"id": user.ID,
|
"id": user.ID,
|
||||||
"allowed_roles": strings.Split(user.Roles, ","),
|
"allowed_roles": strings.Split(user.Roles, ","),
|
||||||
constants.JWT_ROLE_CLAIM: role,
|
constants.JWT_ROLE_CLAIM: roles,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Claims = &UserAuthClaim{
|
t.Claims = &UserAuthClaim{
|
||||||
|
|
|
@ -18,3 +18,12 @@ func WriteToFile(filename string, data string) error {
|
||||||
}
|
}
|
||||||
return file.Sync()
|
return file.Sync()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StringContains(s []string, e string) bool {
|
||||||
|
for _, a := range s {
|
||||||
|
if a == e {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -40,30 +40,14 @@ func IsSuperAdmin(gc *gin.Context) bool {
|
||||||
return secret == constants.ADMIN_SECRET
|
return secret == constants.ADMIN_SECRET
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsValidRolesArray(roles []string) bool {
|
func IsValidRoles(userRoles []string, roles []string) bool {
|
||||||
valid := true
|
valid := true
|
||||||
currentRoleMap := map[string]bool{}
|
for _, role := range roles {
|
||||||
|
if !StringContains(userRoles, role) {
|
||||||
for _, currentRole := range constants.ROLES {
|
|
||||||
currentRoleMap[currentRole] = true
|
|
||||||
}
|
|
||||||
for _, inputRole := range roles {
|
|
||||||
if !currentRoleMap[inputRole] {
|
|
||||||
valid = false
|
valid = false
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return valid
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsValidRole(userRoles []string, role string) bool {
|
|
||||||
valid := false
|
|
||||||
for _, currentRole := range userRoles {
|
|
||||||
if role == currentRole {
|
|
||||||
valid = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return valid
|
return valid
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user