feat(server): add is_valid_jwt query

This commit is contained in:
Lakhan Samani 2022-01-24 00:32:06 +05:30
parent 7f18a3f634
commit 87b1cac979
10 changed files with 465 additions and 52 deletions

58
app/package-lock.json generated
View File

@ -5,19 +5,19 @@
"requires": true, "requires": true,
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-js": { "@authorizerdev/authorizer-js": {
"version": "0.2.0", "version": "0.2.1",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.2.0.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-0.2.1.tgz",
"integrity": "sha512-T2gurEYEP1T56j9IwDIWP1PsELoWcU7TBl5G0UsMqFQhKo7T6p2hM634Z7PS1yLegU3aihuvHGI0C0n4xSo0VQ==", "integrity": "sha512-5lQlh+nc5xTsPongfTyCSX24A1WESu/BjhmZwUNuScEOGady0qPoDHE3RBf46dpi5v05wbHCDN1IFEalX5zssQ==",
"requires": { "requires": {
"node-fetch": "^2.6.1" "node-fetch": "^2.6.1"
} }
}, },
"@authorizerdev/authorizer-react": { "@authorizerdev/authorizer-react": {
"version": "0.4.0", "version": "0.4.3",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.4.0.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-0.4.3.tgz",
"integrity": "sha512-ydE7xrP3cqeTtU923bK0+OIB1fnL0VHnbThcNa41n89XUPV3VBhZ23gxMg90nqon8JFE5g2TNz7+/qBCOQ7aZw==", "integrity": "sha512-o/wWe9zZ3ARYdjbDfhGfvOxe1YQrE1YQ+UN9pcq85YSDkbfBkOfcnJ4YxlxWdL0Obd/ErDIeQ3vskyrfvRf3sA==",
"requires": { "requires": {
"@authorizerdev/authorizer-js": "^0.2.0", "@authorizerdev/authorizer-js": "^0.2.1",
"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"
@ -32,11 +32,11 @@
} }
}, },
"@babel/generator": { "@babel/generator": {
"version": "7.16.7", "version": "7.16.8",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.7.tgz", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.16.8.tgz",
"integrity": "sha512-/ST3Sg8MLGY5HVYmrjOgL60ENux/HfO/CsUh7y4MalThufhE/Ff/6EibFDHi4jiDCaWfJKoqbE6oTh21c5hrRg==", "integrity": "sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==",
"requires": { "requires": {
"@babel/types": "^7.16.7", "@babel/types": "^7.16.8",
"jsesc": "^2.5.1", "jsesc": "^2.5.1",
"source-map": "^0.5.0" "source-map": "^0.5.0"
} }
@ -105,9 +105,9 @@
"integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==" "integrity": "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="
}, },
"@babel/highlight": { "@babel/highlight": {
"version": "7.16.7", "version": "7.16.10",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.7.tgz", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.16.10.tgz",
"integrity": "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw==", "integrity": "sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw==",
"requires": { "requires": {
"@babel/helper-validator-identifier": "^7.16.7", "@babel/helper-validator-identifier": "^7.16.7",
"chalk": "^2.0.0", "chalk": "^2.0.0",
@ -115,9 +115,9 @@
} }
}, },
"@babel/parser": { "@babel/parser": {
"version": "7.16.7", "version": "7.16.12",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.7.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.16.12.tgz",
"integrity": "sha512-sR4eaSrnM7BV7QPzGfEX5paG/6wrZM3I0HDzfIAK06ESvo9oy3xBuVBxE3MbQaKNhvg8g/ixjMWo2CGpzpHsDA==" "integrity": "sha512-VfaV15po8RiZssrkPweyvbGVSe4x2y+aciFCgn0n0/SJMR22cwofRV1mtnJQYcSB1wUTaA/X1LnA3es66MCO5A=="
}, },
"@babel/runtime": { "@babel/runtime": {
"version": "7.14.8", "version": "7.14.8",
@ -138,26 +138,26 @@
} }
}, },
"@babel/traverse": { "@babel/traverse": {
"version": "7.16.7", "version": "7.16.10",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.7.tgz", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.16.10.tgz",
"integrity": "sha512-8KWJPIb8c2VvY8AJrydh6+fVRo2ODx1wYBU2398xJVq0JomuLBZmVQzLPBblJgHIGYG4znCpUZUZ0Pt2vdmVYQ==", "integrity": "sha512-yzuaYXoRJBGMlBhsMJoUW7G1UmSb/eXr/JHYM/MsOJgavJibLwASijW7oXBdw3NQ6T0bW7Ty5P/VarOs9cHmqw==",
"requires": { "requires": {
"@babel/code-frame": "^7.16.7", "@babel/code-frame": "^7.16.7",
"@babel/generator": "^7.16.7", "@babel/generator": "^7.16.8",
"@babel/helper-environment-visitor": "^7.16.7", "@babel/helper-environment-visitor": "^7.16.7",
"@babel/helper-function-name": "^7.16.7", "@babel/helper-function-name": "^7.16.7",
"@babel/helper-hoist-variables": "^7.16.7", "@babel/helper-hoist-variables": "^7.16.7",
"@babel/helper-split-export-declaration": "^7.16.7", "@babel/helper-split-export-declaration": "^7.16.7",
"@babel/parser": "^7.16.7", "@babel/parser": "^7.16.10",
"@babel/types": "^7.16.7", "@babel/types": "^7.16.8",
"debug": "^4.1.0", "debug": "^4.1.0",
"globals": "^11.1.0" "globals": "^11.1.0"
} }
}, },
"@babel/types": { "@babel/types": {
"version": "7.16.7", "version": "7.16.8",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.7.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz",
"integrity": "sha512-E8HuV7FO9qLpx6OtoGfUQ2cjIYnbFwvZWYBS+87EwtdMvmUPJSwykpovFB+8insbpF0uJcpr8KMUi64XZntZcg==", "integrity": "sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==",
"requires": { "requires": {
"@babel/helper-validator-identifier": "^7.16.7", "@babel/helper-validator-identifier": "^7.16.7",
"to-fast-properties": "^2.0.0" "to-fast-properties": "^2.0.0"
@ -420,9 +420,9 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}, },
"node-fetch": { "node-fetch": {
"version": "2.6.6", "version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
"integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
"requires": { "requires": {
"whatwg-url": "^5.0.0" "whatwg-url": "^5.0.0"
} }

View File

@ -119,9 +119,10 @@ type ComplexityRoot struct {
Query struct { Query struct {
AdminSession func(childComplexity int) int AdminSession func(childComplexity int) int
Env func(childComplexity int) int Env func(childComplexity int) int
IsValidJwt func(childComplexity int, params *model.IsValidJWTQueryInput) int
Meta func(childComplexity int) int Meta func(childComplexity int) int
Profile func(childComplexity int) int Profile func(childComplexity int) int
Session func(childComplexity int, roles []string) int Session func(childComplexity int, params *model.SessionQueryInput) int
Users func(childComplexity int) int Users func(childComplexity int) int
VerificationRequests func(childComplexity int) int VerificationRequests func(childComplexity int) int
} }
@ -150,6 +151,11 @@ type ComplexityRoot struct {
UpdatedAt func(childComplexity int) int UpdatedAt func(childComplexity int) int
} }
ValidJWTResponse struct {
Message func(childComplexity int) int
Valid func(childComplexity int) int
}
VerificationRequest struct { VerificationRequest struct {
CreatedAt func(childComplexity int) int CreatedAt func(childComplexity int) int
Email func(childComplexity int) int Email func(childComplexity int) int
@ -180,7 +186,8 @@ type MutationResolver interface {
} }
type QueryResolver interface { type QueryResolver interface {
Meta(ctx context.Context) (*model.Meta, error) Meta(ctx context.Context) (*model.Meta, error)
Session(ctx context.Context, roles []string) (*model.AuthResponse, error) Session(ctx context.Context, params *model.SessionQueryInput) (*model.AuthResponse, error)
IsValidJwt(ctx context.Context, params *model.IsValidJWTQueryInput) (*model.ValidJWTResponse, error)
Profile(ctx context.Context) (*model.User, error) Profile(ctx context.Context) (*model.User, error)
Users(ctx context.Context) ([]*model.User, error) Users(ctx context.Context) ([]*model.User, error)
VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error) VerificationRequests(ctx context.Context) ([]*model.VerificationRequest, error)
@ -688,6 +695,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Query.Env(childComplexity), true return e.complexity.Query.Env(childComplexity), true
case "Query.is_valid_jwt":
if e.complexity.Query.IsValidJwt == nil {
break
}
args, err := ec.field_Query_is_valid_jwt_args(context.TODO(), rawArgs)
if err != nil {
return 0, false
}
return e.complexity.Query.IsValidJwt(childComplexity, args["params"].(*model.IsValidJWTQueryInput)), true
case "Query.meta": case "Query.meta":
if e.complexity.Query.Meta == nil { if e.complexity.Query.Meta == nil {
break break
@ -712,7 +731,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return 0, false return 0, false
} }
return e.complexity.Query.Session(childComplexity, args["roles"].([]string)), true return e.complexity.Query.Session(childComplexity, args["params"].(*model.SessionQueryInput)), true
case "Query._users": case "Query._users":
if e.complexity.Query.Users == nil { if e.complexity.Query.Users == nil {
@ -854,6 +873,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.User.UpdatedAt(childComplexity), true return e.complexity.User.UpdatedAt(childComplexity), true
case "ValidJWTResponse.message":
if e.complexity.ValidJWTResponse.Message == nil {
break
}
return e.complexity.ValidJWTResponse.Message(childComplexity), true
case "ValidJWTResponse.valid":
if e.complexity.ValidJWTResponse.Valid == nil {
break
}
return e.complexity.ValidJWTResponse.Valid(childComplexity), true
case "VerificationRequest.created_at": case "VerificationRequest.created_at":
if e.complexity.VerificationRequest.CreatedAt == nil { if e.complexity.VerificationRequest.CreatedAt == nil {
break break
@ -1031,6 +1064,11 @@ type Response {
message: String! message: String!
} }
type ValidJWTResponse {
valid: Boolean!
message: String!
}
type Env { type Env {
ADMIN_SECRET: String ADMIN_SECRET: String
SMTP_HOST: String SMTP_HOST: String
@ -1184,6 +1222,15 @@ input MagicLinkLoginInput {
roles: [String!] roles: [String!]
} }
input SessionQueryInput {
roles: [String!]
}
input IsValidJWTQueryInput {
jwt: String!
roles: [String!]
}
type Mutation { type Mutation {
signup(params: SignUpInput!): AuthResponse! signup(params: SignUpInput!): AuthResponse!
login(params: LoginInput!): AuthResponse! login(params: LoginInput!): AuthResponse!
@ -1205,7 +1252,8 @@ type Mutation {
type Query { type Query {
meta: Meta! meta: Meta!
session(roles: [String!]): AuthResponse! session(params: SessionQueryInput): AuthResponse!
is_valid_jwt(params: IsValidJWTQueryInput): ValidJWTResponse!
profile: User! profile: User!
# admin only apis # admin only apis
_users: [User!]! _users: [User!]!
@ -1431,18 +1479,33 @@ func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs
return args, nil return args, nil
} }
func (ec *executionContext) field_Query_session_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { func (ec *executionContext) field_Query_is_valid_jwt_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 *model.IsValidJWTQueryInput
if tmp, ok := rawArgs["roles"]; ok { if tmp, ok := rawArgs["params"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roles")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
arg0, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) arg0, err = ec.unmarshalOIsValidJWTQueryInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐIsValidJWTQueryInput(ctx, tmp)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} }
args["roles"] = arg0 args["params"] = arg0
return args, nil
}
func (ec *executionContext) field_Query_session_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
var err error
args := map[string]interface{}{}
var arg0 *model.SessionQueryInput
if tmp, ok := rawArgs["params"]; ok {
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
arg0, err = ec.unmarshalOSessionQueryInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐSessionQueryInput(ctx, tmp)
if err != nil {
return nil, err
}
}
args["params"] = arg0
return args, nil return args, nil
} }
@ -3566,7 +3629,7 @@ func (ec *executionContext) _Query_session(ctx context.Context, field graphql.Co
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().Session(rctx, args["roles"].([]string)) return ec.resolvers.Query().Session(rctx, args["params"].(*model.SessionQueryInput))
}) })
if err != nil { if err != nil {
ec.Error(ctx, err) ec.Error(ctx, err)
@ -3583,6 +3646,48 @@ func (ec *executionContext) _Query_session(ctx context.Context, field graphql.Co
return ec.marshalNAuthResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAuthResponse(ctx, field.Selections, res) return ec.marshalNAuthResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAuthResponse(ctx, field.Selections, res)
} }
func (ec *executionContext) _Query_is_valid_jwt(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "Query",
Field: field,
Args: nil,
IsMethod: true,
IsResolver: true,
}
ctx = graphql.WithFieldContext(ctx, fc)
rawArgs := field.ArgumentMap(ec.Variables)
args, err := ec.field_Query_is_valid_jwt_args(ctx, rawArgs)
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
fc.Args = args
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return ec.resolvers.Query().IsValidJwt(rctx, args["params"].(*model.IsValidJWTQueryInput))
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(*model.ValidJWTResponse)
fc.Result = res
return ec.marshalNValidJWTResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐValidJWTResponse(ctx, field.Selections, res)
}
func (ec *executionContext) _Query_profile(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { func (ec *executionContext) _Query_profile(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -4423,6 +4528,76 @@ func (ec *executionContext) _User_updated_at(ctx context.Context, field graphql.
return ec.marshalOInt642ᚖint64(ctx, field.Selections, res) return ec.marshalOInt642ᚖint64(ctx, field.Selections, res)
} }
func (ec *executionContext) _ValidJWTResponse_valid(ctx context.Context, field graphql.CollectedField, obj *model.ValidJWTResponse) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "ValidJWTResponse",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Valid, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(bool)
fc.Result = res
return ec.marshalNBoolean2bool(ctx, field.Selections, res)
}
func (ec *executionContext) _ValidJWTResponse_message(ctx context.Context, field graphql.CollectedField, obj *model.ValidJWTResponse) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "ValidJWTResponse",
Field: field,
Args: nil,
IsMethod: false,
IsResolver: false,
}
ctx = graphql.WithFieldContext(ctx, fc)
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.Message, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
if !graphql.HasFieldError(ctx, fc) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
res := resTmp.(string)
fc.Result = res
return ec.marshalNString2string(ctx, field.Selections, res)
}
func (ec *executionContext) _VerificationRequest_id(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequest) (ret graphql.Marshaler) { func (ec *executionContext) _VerificationRequest_id(ctx context.Context, field graphql.CollectedField, obj *model.VerificationRequest) (ret graphql.Marshaler) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
@ -5864,6 +6039,37 @@ func (ec *executionContext) unmarshalInputForgotPasswordInput(ctx context.Contex
return it, nil return it, nil
} }
func (ec *executionContext) unmarshalInputIsValidJWTQueryInput(ctx context.Context, obj interface{}) (model.IsValidJWTQueryInput, error) {
var it model.IsValidJWTQueryInput
asMap := map[string]interface{}{}
for k, v := range obj.(map[string]interface{}) {
asMap[k] = v
}
for k, v := range asMap {
switch k {
case "jwt":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("jwt"))
it.Jwt, err = ec.unmarshalNString2string(ctx, v)
if err != nil {
return it, err
}
case "roles":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roles"))
it.Roles, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v)
if err != nil {
return it, err
}
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj interface{}) (model.LoginInput, error) { func (ec *executionContext) unmarshalInputLoginInput(ctx context.Context, obj interface{}) (model.LoginInput, error) {
var it model.LoginInput var it model.LoginInput
asMap := map[string]interface{}{} asMap := map[string]interface{}{}
@ -6004,6 +6210,29 @@ func (ec *executionContext) unmarshalInputResetPasswordInput(ctx context.Context
return it, nil return it, nil
} }
func (ec *executionContext) unmarshalInputSessionQueryInput(ctx context.Context, obj interface{}) (model.SessionQueryInput, error) {
var it model.SessionQueryInput
asMap := map[string]interface{}{}
for k, v := range obj.(map[string]interface{}) {
asMap[k] = v
}
for k, v := range asMap {
switch k {
case "roles":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("roles"))
it.Roles, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v)
if err != nil {
return it, err
}
}
}
return it, nil
}
func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj interface{}) (model.SignUpInput, error) { func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj interface{}) (model.SignUpInput, error) {
var it model.SignUpInput var it model.SignUpInput
asMap := map[string]interface{}{} asMap := map[string]interface{}{}
@ -6971,6 +7200,20 @@ func (ec *executionContext) _Query(ctx context.Context, sel ast.SelectionSet) gr
} }
return res return res
}) })
case "is_valid_jwt":
field := field
out.Concurrently(i, func() (res graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
}
}()
res = ec._Query_is_valid_jwt(ctx, field)
if res == graphql.Null {
atomic.AddUint32(&invalids, 1)
}
return res
})
case "profile": case "profile":
field := field field := field
out.Concurrently(i, func() (res graphql.Marshaler) { out.Concurrently(i, func() (res graphql.Marshaler) {
@ -7154,6 +7397,38 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
return out return out
} }
var validJWTResponseImplementors = []string{"ValidJWTResponse"}
func (ec *executionContext) _ValidJWTResponse(ctx context.Context, sel ast.SelectionSet, obj *model.ValidJWTResponse) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, validJWTResponseImplementors)
out := graphql.NewFieldSet(fields)
var invalids uint32
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("ValidJWTResponse")
case "valid":
out.Values[i] = ec._ValidJWTResponse_valid(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
case "message":
out.Values[i] = ec._ValidJWTResponse_message(ctx, field, obj)
if out.Values[i] == graphql.Null {
invalids++
}
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch()
if invalids > 0 {
return graphql.Null
}
return out
}
var verificationRequestImplementors = []string{"VerificationRequest"} var verificationRequestImplementors = []string{"VerificationRequest"}
func (ec *executionContext) _VerificationRequest(ctx context.Context, sel ast.SelectionSet, obj *model.VerificationRequest) graphql.Marshaler { func (ec *executionContext) _VerificationRequest(ctx context.Context, sel ast.SelectionSet, obj *model.VerificationRequest) graphql.Marshaler {
@ -7698,6 +7973,20 @@ func (ec *executionContext) marshalNUser2ᚖgithubᚗcomᚋauthorizerdevᚋautho
return ec._User(ctx, sel, v) return ec._User(ctx, sel, v)
} }
func (ec *executionContext) marshalNValidJWTResponse2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐValidJWTResponse(ctx context.Context, sel ast.SelectionSet, v model.ValidJWTResponse) graphql.Marshaler {
return ec._ValidJWTResponse(ctx, sel, &v)
}
func (ec *executionContext) marshalNValidJWTResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐValidJWTResponse(ctx context.Context, sel ast.SelectionSet, v *model.ValidJWTResponse) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "must not be null")
}
return graphql.Null
}
return ec._ValidJWTResponse(ctx, sel, v)
}
func (ec *executionContext) marshalNVerificationRequest2ᚕᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐVerificationRequestᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.VerificationRequest) graphql.Marshaler { func (ec *executionContext) marshalNVerificationRequest2ᚕᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐVerificationRequestᚄ(ctx context.Context, sel ast.SelectionSet, v []*model.VerificationRequest) graphql.Marshaler {
ret := make(graphql.Array, len(v)) ret := make(graphql.Array, len(v))
var wg sync.WaitGroup var wg sync.WaitGroup
@ -8053,6 +8342,22 @@ func (ec *executionContext) marshalOInt642ᚖint64(ctx context.Context, sel ast.
return graphql.MarshalInt64(*v) return graphql.MarshalInt64(*v)
} }
func (ec *executionContext) unmarshalOIsValidJWTQueryInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐIsValidJWTQueryInput(ctx context.Context, v interface{}) (*model.IsValidJWTQueryInput, error) {
if v == nil {
return nil, nil
}
res, err := ec.unmarshalInputIsValidJWTQueryInput(ctx, v)
return &res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) unmarshalOSessionQueryInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐSessionQueryInput(ctx context.Context, v interface{}) (*model.SessionQueryInput, error) {
if v == nil {
return nil, nil
}
res, err := ec.unmarshalInputSessionQueryInput(ctx, v)
return &res, graphql.ErrorOnPath(ctx, err)
}
func (ec *executionContext) unmarshalOString2string(ctx context.Context, v interface{}) (string, error) { func (ec *executionContext) unmarshalOString2string(ctx context.Context, v interface{}) (string, error) {
res, err := graphql.UnmarshalString(v) res, err := graphql.UnmarshalString(v)
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)

View File

@ -63,6 +63,11 @@ type ForgotPasswordInput struct {
Email string `json:"email"` Email string `json:"email"`
} }
type IsValidJWTQueryInput struct {
Jwt string `json:"jwt"`
Roles []string `json:"roles"`
}
type LoginInput struct { type LoginInput struct {
Email string `json:"email"` Email string `json:"email"`
Password string `json:"password"` Password string `json:"password"`
@ -99,6 +104,10 @@ type Response struct {
Message string `json:"message"` Message string `json:"message"`
} }
type SessionQueryInput struct {
Roles []string `json:"roles"`
}
type SignUpInput struct { type SignUpInput struct {
Email string `json:"email"` Email string `json:"email"`
GivenName *string `json:"given_name"` GivenName *string `json:"given_name"`
@ -197,6 +206,11 @@ type User struct {
UpdatedAt *int64 `json:"updated_at"` UpdatedAt *int64 `json:"updated_at"`
} }
type ValidJWTResponse struct {
Valid bool `json:"valid"`
Message string `json:"message"`
}
type VerificationRequest struct { type VerificationRequest struct {
ID string `json:"id"` ID string `json:"id"`
Identifier *string `json:"identifier"` Identifier *string `json:"identifier"`

View File

@ -62,6 +62,11 @@ type Response {
message: String! message: String!
} }
type ValidJWTResponse {
valid: Boolean!
message: String!
}
type Env { type Env {
ADMIN_SECRET: String ADMIN_SECRET: String
SMTP_HOST: String SMTP_HOST: String
@ -215,6 +220,15 @@ input MagicLinkLoginInput {
roles: [String!] roles: [String!]
} }
input SessionQueryInput {
roles: [String!]
}
input IsValidJWTQueryInput {
jwt: String!
roles: [String!]
}
type Mutation { type Mutation {
signup(params: SignUpInput!): AuthResponse! signup(params: SignUpInput!): AuthResponse!
login(params: LoginInput!): AuthResponse! login(params: LoginInput!): AuthResponse!
@ -236,7 +250,8 @@ type Mutation {
type Query { type Query {
meta: Meta! meta: Meta!
session(roles: [String!]): AuthResponse! session(params: SessionQueryInput): AuthResponse!
is_valid_jwt(params: IsValidJWTQueryInput): ValidJWTResponse!
profile: User! profile: User!
# admin only apis # admin only apis
_users: [User!]! _users: [User!]!

View File

@ -75,8 +75,12 @@ func (r *queryResolver) Meta(ctx context.Context) (*model.Meta, error) {
return resolvers.MetaResolver(ctx) return resolvers.MetaResolver(ctx)
} }
func (r *queryResolver) Session(ctx context.Context, roles []string) (*model.AuthResponse, error) { func (r *queryResolver) Session(ctx context.Context, params *model.SessionQueryInput) (*model.AuthResponse, error) {
return resolvers.SessionResolver(ctx, roles) return resolvers.SessionResolver(ctx, params)
}
func (r *queryResolver) IsValidJwt(ctx context.Context, params *model.IsValidJWTQueryInput) (*model.ValidJWTResponse, error) {
return resolvers.IsValidJwtResolver(ctx, params)
} }
func (r *queryResolver) Profile(ctx context.Context) (*model.User, error) { func (r *queryResolver) Profile(ctx context.Context) (*model.User, error) {

View File

@ -0,0 +1,39 @@
package resolvers
import (
"context"
"fmt"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/envstore"
"github.com/authorizerdev/authorizer/server/graph/model"
tokenHelper "github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils"
)
// IsValidJwtResolver resolver to return if given jwt is valid
func IsValidJwtResolver(ctx context.Context, params *model.IsValidJWTQueryInput) (*model.ValidJWTResponse, error) {
claims, err := tokenHelper.VerifyJWTToken(params.Jwt)
if err != nil {
return nil, err
}
claimRoleInterface := claims[envstore.EnvInMemoryStoreObj.GetStringStoreEnvVariable(constants.EnvKeyJwtRoleClaim)].([]interface{})
claimRoles := []string{}
for _, v := range claimRoleInterface {
claimRoles = append(claimRoles, v.(string))
}
if params != nil && params.Roles != nil && len(params.Roles) > 0 {
for _, v := range params.Roles {
if !utils.StringSliceContains(claimRoles, v) {
return nil, fmt.Errorf(`unauthorized`)
}
}
}
return &model.ValidJWTResponse{
Valid: true,
Message: "Valid JWT",
}, nil
}

View File

@ -13,7 +13,7 @@ import (
) )
// SessionResolver is a resolver for session query // SessionResolver is a resolver for session query
func SessionResolver(ctx context.Context, roles []string) (*model.AuthResponse, error) { func SessionResolver(ctx context.Context, params *model.SessionQueryInput) (*model.AuthResponse, error) {
var res *model.AuthResponse var res *model.AuthResponse
gc, err := utils.GinContextFromContext(ctx) gc, err := utils.GinContextFromContext(ctx)
@ -65,8 +65,8 @@ func SessionResolver(ctx context.Context, roles []string) (*model.AuthResponse,
claimRoles = append(claimRoles, v.(string)) claimRoles = append(claimRoles, v.(string))
} }
if len(roles) > 0 { if params != nil && params.Roles != nil && len(params.Roles) > 0 {
for _, v := range roles { for _, v := range params.Roles {
if !utils.StringSliceContains(claimRoles, v) { if !utils.StringSliceContains(claimRoles, v) {
return res, fmt.Errorf(`unauthorized`) return res, fmt.Errorf(`unauthorized`)
} }

View File

@ -0,0 +1,39 @@
package test
import (
"context"
"testing"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/authorizerdev/authorizer/server/token"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func isValidJWTTests(t *testing.T, s TestSetup) {
t.Helper()
ctx := context.Background()
expiredToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhbGxvd2VkX3JvbGVzIjpbIiJdLCJiaXJ0aGRhdGUiOm51bGwsImNyZWF0ZWRfYXQiOjAsImVtYWlsIjoiam9obi5kb2VAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJleHAiOjE2NDI5NjEwMTEsImV4dHJhIjp7IngtZXh0cmEtaWQiOiJkMmNhMjQwNy05MzZmLTQwYzQtOTQ2NS05Y2M5MWYxZTJhNDQifSwiZmFtaWx5X25hbWUiOm51bGwsImdlbmRlciI6bnVsbCwiZ2l2ZW5fbmFtZSI6bnVsbCwiaWF0IjoxNjQyOTYwOTgxLCJpZCI6ImQyY2EyNDA3LTkzNmYtNDBjNC05NDY1LTljYzkxZjFlMmE0NCIsIm1pZGRsZV9uYW1lIjpudWxsLCJuaWNrbmFtZSI6bnVsbCwicGhvbmVfbnVtYmVyIjpudWxsLCJwaG9uZV9udW1iZXJfdmVyaWZpZWQiOmZhbHNlLCJwaWN0dXJlIjpudWxsLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJqb2huLmRvZUBnbWFpbC5jb20iLCJyb2xlIjpbXSwic2lnbnVwX21ldGhvZHMiOiIiLCJ0b2tlbl90eXBlIjoiYWNjZXNzX3Rva2VuIiwidXBkYXRlZF9hdCI6MH0.FrdyeOC5e8uU1SowGj0omFJuwRnh4BrEk89S_fbEkzs"
t.Run(`should fail for invalid jwt`, func(t *testing.T) {
_, err := resolvers.IsValidJwtResolver(ctx, &model.IsValidJWTQueryInput{
Jwt: expiredToken,
})
assert.NotNil(t, err)
})
t.Run(`should pass with valid jwt`, func(t *testing.T) {
authToken, err := token.CreateAuthToken(models.User{
ID: uuid.New().String(),
Email: "john.doe@gmail.com",
}, []string{})
assert.Nil(t, err)
res, err := resolvers.IsValidJwtResolver(ctx, &model.IsValidJWTQueryInput{
Jwt: authToken.AccessToken.Token,
})
assert.Nil(t, err)
assert.True(t, res.Valid)
})
}

View File

@ -60,6 +60,7 @@ func TestResolvers(t *testing.T) {
magicLinkLoginTests(t, s) magicLinkLoginTests(t, s)
logoutTests(t, s) logoutTests(t, s)
metaTests(t, s) metaTests(t, s)
isValidJWTTests(t, s)
}) })
} }
} }

View File

@ -27,7 +27,7 @@ func sessionTests(t *testing.T, s TestSetup) {
ConfirmPassword: s.TestInfo.Password, ConfirmPassword: s.TestInfo.Password,
}) })
_, err := resolvers.SessionResolver(ctx, []string{}) _, err := resolvers.SessionResolver(ctx, &model.SessionQueryInput{})
assert.NotNil(t, err, "unauthorized") assert.NotNil(t, err, "unauthorized")
verificationRequest, err := db.Provider.GetVerificationRequestByEmail(email, constants.VerificationTypeBasicAuthSignup) verificationRequest, err := db.Provider.GetVerificationRequestByEmail(email, constants.VerificationTypeBasicAuthSignup)
@ -50,13 +50,9 @@ func sessionTests(t *testing.T, s TestSetup) {
req.Header.Set("Cookie", cookie) req.Header.Set("Cookie", cookie)
_, err = resolvers.SessionResolver(ctx, []string{}) _, err = resolvers.SessionResolver(ctx, &model.SessionQueryInput{})
assert.Nil(t, err) assert.Nil(t, err)
// newToken := *sessionRes.AccessToken
// assert.NotEqual(t, token, newToken, "tokens should not be equal")
cleanData(email) cleanData(email)
}) })
} }