This commit is contained in:
Untone 2023-12-23 10:15:52 +03:00
commit f412943105
15 changed files with 563 additions and 173 deletions

16
app/package-lock.json generated
View File

@ -9,7 +9,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-react": "^1.1.19", "@authorizerdev/authorizer-react": "^1.2.0-beta.8",
"@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",
@ -27,9 +27,9 @@
} }
}, },
"node_modules/@authorizerdev/authorizer-js": { "node_modules/@authorizerdev/authorizer-js": {
"version": "1.2.18", "version": "2.0.0-beta.3",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.2.18.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-2.0.0-beta.3.tgz",
"integrity": "sha512-9j5U/4lqaaEcG78Zli+TtLJ0migSKhFwnXXunulAGTZOzQSTCJ/CSSPip5wWNa/Mkr6gdEMwk1HYfhIdk2A9Mg==", "integrity": "sha512-cEzEVe7AewvOwOwoettiKRCq1e5Y33k9g8fJjqAoe3B/36iNN8wnZ5qgsPPZkqhv+Cvn6huj+YWtRimfVJ6d0w==",
"dependencies": { "dependencies": {
"cross-fetch": "^3.1.5" "cross-fetch": "^3.1.5"
}, },
@ -41,11 +41,11 @@
} }
}, },
"node_modules/@authorizerdev/authorizer-react": { "node_modules/@authorizerdev/authorizer-react": {
"version": "1.1.19", "version": "1.2.0-beta.8",
"resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.19.tgz", "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.2.0-beta.8.tgz",
"integrity": "sha512-hbId4mtzeWke1uUFAZrPwT45UmxgTp0QHAAsQvl/I0+mgoCJlJdAnUBCiJD6d5lVHJk41nx/ePYG4rw2Aj6HTw==", "integrity": "sha512-zPpishs4rsO98Vr5eURvlMHl+/l3yLKs1uFLlPOJxGxGx/VtX2jgHxiaUH/D0vEStm4F4Z+q1k6jGpwyALFCBA==",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-js": "^1.2.18", "@authorizerdev/authorizer-js": "^2.0.0-beta.3",
"validator": "^13.11.0" "validator": "^13.11.0"
}, },
"engines": { "engines": {

View File

@ -12,7 +12,7 @@
"author": "Lakhan Samani", "author": "Lakhan Samani",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@authorizerdev/authorizer-react": "^1.1.19", "@authorizerdev/authorizer-react": "^1.2.0-beta.8",
"@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",

View File

@ -71,6 +71,9 @@ export default function Login({ urlProps }: { urlProps: Record<string, any> }) {
...urlProps, ...urlProps,
redirect_uri: `${window.location.origin}/app/reset-password`, redirect_uri: `${window.location.origin}/app/reset-password`,
}} }}
onPasswordReset={() => {
setView(VIEW_TYPES.LOGIN);
}}
/> />
<Footer> <Footer>
<Link <Link

View File

@ -2,19 +2,19 @@
# yarn lockfile v1 # yarn lockfile v1
"@authorizerdev/authorizer-js@^1.2.18": "@authorizerdev/authorizer-js@^2.0.0-beta.3":
version "1.2.18" version "2.0.0-beta.3"
resolved "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.2.18.tgz" resolved "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-2.0.0-beta.3.tgz"
integrity sha512-9j5U/4lqaaEcG78Zli+TtLJ0migSKhFwnXXunulAGTZOzQSTCJ/CSSPip5wWNa/Mkr6gdEMwk1HYfhIdk2A9Mg== integrity sha512-cEzEVe7AewvOwOwoettiKRCq1e5Y33k9g8fJjqAoe3B/36iNN8wnZ5qgsPPZkqhv+Cvn6huj+YWtRimfVJ6d0w==
dependencies: dependencies:
cross-fetch "^3.1.5" cross-fetch "^3.1.5"
"@authorizerdev/authorizer-react@^1.1.19": "@authorizerdev/authorizer-react@^1.2.0-beta.8":
version "1.1.19" version "1.2.0-beta.8"
resolved "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.19.tgz" resolved "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.2.0-beta.8.tgz"
integrity sha512-hbId4mtzeWke1uUFAZrPwT45UmxgTp0QHAAsQvl/I0+mgoCJlJdAnUBCiJD6d5lVHJk41nx/ePYG4rw2Aj6HTw== integrity sha512-zPpishs4rsO98Vr5eURvlMHl+/l3yLKs1uFLlPOJxGxGx/VtX2jgHxiaUH/D0vEStm4F4Z+q1k6jGpwyALFCBA==
dependencies: dependencies:
"@authorizerdev/authorizer-js" "^1.2.18" "@authorizerdev/authorizer-js" "^2.0.0-beta.3"
validator "^13.11.0" validator "^13.11.0"
"@babel/code-frame@^7.22.13": "@babel/code-frame@^7.22.13":

View File

@ -150,6 +150,11 @@ type ComplexityRoot struct {
Reason func(childComplexity int) int Reason func(childComplexity int) int
} }
ForgotPasswordResponse struct {
Message func(childComplexity int) int
ShouldShowMobileOtpScreen func(childComplexity int) int
}
GenerateJWTKeysResponse struct { GenerateJWTKeysResponse struct {
PrivateKey func(childComplexity int) int PrivateKey func(childComplexity int) int
PublicKey func(childComplexity int) int PublicKey func(childComplexity int) int
@ -356,7 +361,7 @@ type MutationResolver interface {
UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error) UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error)
VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.AuthResponse, error) VerifyEmail(ctx context.Context, params model.VerifyEmailInput) (*model.AuthResponse, error)
ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error) ResendVerifyEmail(ctx context.Context, params model.ResendVerifyEmailInput) (*model.Response, error)
ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.ForgotPasswordResponse, error)
ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error) ResetPassword(ctx context.Context, params model.ResetPasswordInput) (*model.Response, error)
Revoke(ctx context.Context, params model.OAuthRevokeInput) (*model.Response, error) Revoke(ctx context.Context, params model.OAuthRevokeInput) (*model.Response, error)
VerifyOtp(ctx context.Context, params model.VerifyOTPRequest) (*model.AuthResponse, error) VerifyOtp(ctx context.Context, params model.VerifyOTPRequest) (*model.AuthResponse, error)
@ -1039,6 +1044,20 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.Error.Reason(childComplexity), true return e.complexity.Error.Reason(childComplexity), true
case "ForgotPasswordResponse.message":
if e.complexity.ForgotPasswordResponse.Message == nil {
break
}
return e.complexity.ForgotPasswordResponse.Message(childComplexity), true
case "ForgotPasswordResponse.should_show_mobile_otp_screen":
if e.complexity.ForgotPasswordResponse.ShouldShowMobileOtpScreen == nil {
break
}
return e.complexity.ForgotPasswordResponse.ShouldShowMobileOtpScreen(childComplexity), true
case "GenerateJWTKeysResponse.private_key": case "GenerateJWTKeysResponse.private_key":
if e.complexity.GenerateJWTKeysResponse.PrivateKey == nil { if e.complexity.GenerateJWTKeysResponse.PrivateKey == nil {
break break
@ -2459,6 +2478,11 @@ type Response {
message: String! message: String!
} }
type ForgotPasswordResponse {
message: String!
should_show_mobile_otp_screen: Boolean
}
type InviteMembersResponse { type InviteMembersResponse {
message: String! message: String!
Users: [User!]! Users: [User!]!
@ -2791,13 +2815,16 @@ input UpdateUserInput {
} }
input ForgotPasswordInput { input ForgotPasswordInput {
email: String! email: String
phone_number: String
state: String state: String
redirect_uri: String redirect_uri: String
} }
input ResetPasswordInput { input ResetPasswordInput {
token: String! token: String
otp: String
phone_number: String
password: String! password: String!
confirm_password: String! confirm_password: String!
} }
@ -2950,7 +2977,7 @@ type Mutation {
update_profile(params: UpdateProfileInput!): Response! update_profile(params: UpdateProfileInput!): Response!
verify_email(params: VerifyEmailInput!): AuthResponse! verify_email(params: VerifyEmailInput!): AuthResponse!
resend_verify_email(params: ResendVerifyEmailInput!): Response! resend_verify_email(params: ResendVerifyEmailInput!): Response!
forgot_password(params: ForgotPasswordInput!): Response! forgot_password(params: ForgotPasswordInput!): ForgotPasswordResponse!
reset_password(params: ResetPasswordInput!): Response! reset_password(params: ResetPasswordInput!): Response!
revoke(params: OAuthRevokeInput!): Response! revoke(params: OAuthRevokeInput!): Response!
verify_otp(params: VerifyOTPRequest!): AuthResponse! verify_otp(params: VerifyOTPRequest!): AuthResponse!
@ -7434,6 +7461,91 @@ func (ec *executionContext) fieldContext_Error_reason(ctx context.Context, field
return fc, nil return fc, nil
} }
func (ec *executionContext) _ForgotPasswordResponse_message(ctx context.Context, field graphql.CollectedField, obj *model.ForgotPasswordResponse) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ForgotPasswordResponse_message(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
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) fieldContext_ForgotPasswordResponse_message(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ForgotPasswordResponse",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type String does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _ForgotPasswordResponse_should_show_mobile_otp_screen(ctx context.Context, field graphql.CollectedField, obj *model.ForgotPasswordResponse) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_ForgotPasswordResponse_should_show_mobile_otp_screen(ctx, field)
if err != nil {
return graphql.Null
}
ctx = graphql.WithFieldContext(ctx, fc)
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
ctx = rctx // use context from middleware stack in children
return obj.ShouldShowMobileOtpScreen, nil
})
if err != nil {
ec.Error(ctx, err)
return graphql.Null
}
if resTmp == nil {
return graphql.Null
}
res := resTmp.(*bool)
fc.Result = res
return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res)
}
func (ec *executionContext) fieldContext_ForgotPasswordResponse_should_show_mobile_otp_screen(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
fc = &graphql.FieldContext{
Object: "ForgotPasswordResponse",
Field: field,
IsMethod: false,
IsResolver: false,
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
return nil, errors.New("field of type Boolean does not have child fields")
},
}
return fc, nil
}
func (ec *executionContext) _GenerateJWTKeysResponse_secret(ctx context.Context, field graphql.CollectedField, obj *model.GenerateJWTKeysResponse) (ret graphql.Marshaler) { func (ec *executionContext) _GenerateJWTKeysResponse_secret(ctx context.Context, field graphql.CollectedField, obj *model.GenerateJWTKeysResponse) (ret graphql.Marshaler) {
fc, err := ec.fieldContext_GenerateJWTKeysResponse_secret(ctx, field) fc, err := ec.fieldContext_GenerateJWTKeysResponse_secret(ctx, field)
if err != nil { if err != nil {
@ -9135,9 +9247,9 @@ func (ec *executionContext) _Mutation_forgot_password(ctx context.Context, field
} }
return graphql.Null return graphql.Null
} }
res := resTmp.(*model.Response) res := resTmp.(*model.ForgotPasswordResponse)
fc.Result = res fc.Result = res
return ec.marshalNResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐResponse(ctx, field.Selections, res) return ec.marshalNForgotPasswordResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐForgotPasswordResponse(ctx, field.Selections, res)
} }
func (ec *executionContext) fieldContext_Mutation_forgot_password(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { func (ec *executionContext) fieldContext_Mutation_forgot_password(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
@ -9149,9 +9261,11 @@ func (ec *executionContext) fieldContext_Mutation_forgot_password(ctx context.Co
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
switch field.Name { switch field.Name {
case "message": case "message":
return ec.fieldContext_Response_message(ctx, field) return ec.fieldContext_ForgotPasswordResponse_message(ctx, field)
case "should_show_mobile_otp_screen":
return ec.fieldContext_ForgotPasswordResponse_should_show_mobile_otp_screen(ctx, field)
} }
return nil, fmt.Errorf("no field named %q was found under type Response", field.Name) return nil, fmt.Errorf("no field named %q was found under type ForgotPasswordResponse", field.Name)
}, },
} }
defer func() { defer func() {
@ -16821,7 +16935,7 @@ func (ec *executionContext) unmarshalInputForgotPasswordInput(ctx context.Contex
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"email", "state", "redirect_uri"} fieldsInOrder := [...]string{"email", "phone_number", "state", "redirect_uri"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@ -16832,11 +16946,20 @@ func (ec *executionContext) unmarshalInputForgotPasswordInput(ctx context.Contex
var err error var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email"))
data, err := ec.unmarshalNString2string(ctx, v) data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
it.Email = data it.Email = data
case "phone_number":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("phone_number"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
it.PhoneNumber = data
case "state": case "state":
var err error var err error
@ -17578,7 +17701,7 @@ func (ec *executionContext) unmarshalInputResetPasswordInput(ctx context.Context
asMap[k] = v asMap[k] = v
} }
fieldsInOrder := [...]string{"token", "password", "confirm_password"} fieldsInOrder := [...]string{"token", "otp", "phone_number", "password", "confirm_password"}
for _, k := range fieldsInOrder { for _, k := range fieldsInOrder {
v, ok := asMap[k] v, ok := asMap[k]
if !ok { if !ok {
@ -17589,11 +17712,29 @@ func (ec *executionContext) unmarshalInputResetPasswordInput(ctx context.Context
var err error var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("token")) ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("token"))
data, err := ec.unmarshalNString2string(ctx, v) data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil { if err != nil {
return it, err return it, err
} }
it.Token = data it.Token = data
case "otp":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("otp"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
it.Otp = data
case "phone_number":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("phone_number"))
data, err := ec.unmarshalOString2ᚖstring(ctx, v)
if err != nil {
return it, err
}
it.PhoneNumber = data
case "password": case "password":
var err error var err error
@ -19529,6 +19670,47 @@ func (ec *executionContext) _Error(ctx context.Context, sel ast.SelectionSet, ob
return out return out
} }
var forgotPasswordResponseImplementors = []string{"ForgotPasswordResponse"}
func (ec *executionContext) _ForgotPasswordResponse(ctx context.Context, sel ast.SelectionSet, obj *model.ForgotPasswordResponse) graphql.Marshaler {
fields := graphql.CollectFields(ec.OperationContext, sel, forgotPasswordResponseImplementors)
out := graphql.NewFieldSet(fields)
deferred := make(map[string]*graphql.FieldSet)
for i, field := range fields {
switch field.Name {
case "__typename":
out.Values[i] = graphql.MarshalString("ForgotPasswordResponse")
case "message":
out.Values[i] = ec._ForgotPasswordResponse_message(ctx, field, obj)
if out.Values[i] == graphql.Null {
out.Invalids++
}
case "should_show_mobile_otp_screen":
out.Values[i] = ec._ForgotPasswordResponse_should_show_mobile_otp_screen(ctx, field, obj)
default:
panic("unknown field " + strconv.Quote(field.Name))
}
}
out.Dispatch(ctx)
if out.Invalids > 0 {
return graphql.Null
}
atomic.AddInt32(&ec.deferred, int32(len(deferred)))
for label, dfs := range deferred {
ec.processDeferredGroup(graphql.DeferredGroup{
Label: label,
Path: graphql.GetPath(ctx),
FieldSet: dfs,
Context: ctx,
})
}
return out
}
var generateJWTKeysResponseImplementors = []string{"GenerateJWTKeysResponse"} var generateJWTKeysResponseImplementors = []string{"GenerateJWTKeysResponse"}
func (ec *executionContext) _GenerateJWTKeysResponse(ctx context.Context, sel ast.SelectionSet, obj *model.GenerateJWTKeysResponse) graphql.Marshaler { func (ec *executionContext) _GenerateJWTKeysResponse(ctx context.Context, sel ast.SelectionSet, obj *model.GenerateJWTKeysResponse) graphql.Marshaler {
@ -21531,6 +21713,20 @@ func (ec *executionContext) unmarshalNForgotPasswordInput2githubᚗcomᚋauthori
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)
} }
func (ec *executionContext) marshalNForgotPasswordResponse2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐForgotPasswordResponse(ctx context.Context, sel ast.SelectionSet, v model.ForgotPasswordResponse) graphql.Marshaler {
return ec._ForgotPasswordResponse(ctx, sel, &v)
}
func (ec *executionContext) marshalNForgotPasswordResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐForgotPasswordResponse(ctx context.Context, sel ast.SelectionSet, v *model.ForgotPasswordResponse) graphql.Marshaler {
if v == nil {
if !graphql.HasFieldError(ctx, graphql.GetFieldContext(ctx)) {
ec.Errorf(ctx, "the requested element is null which the schema does not allow")
}
return graphql.Null
}
return ec._ForgotPasswordResponse(ctx, sel, v)
}
func (ec *executionContext) unmarshalNGenerateJWTKeysInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐGenerateJWTKeysInput(ctx context.Context, v interface{}) (model.GenerateJWTKeysInput, error) { func (ec *executionContext) unmarshalNGenerateJWTKeysInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐGenerateJWTKeysInput(ctx context.Context, v interface{}) (model.GenerateJWTKeysInput, error) {
res, err := ec.unmarshalInputGenerateJWTKeysInput(ctx, v) res, err := ec.unmarshalInputGenerateJWTKeysInput(ctx, v)
return res, graphql.ErrorOnPath(ctx, err) return res, graphql.ErrorOnPath(ctx, err)

View File

@ -138,11 +138,17 @@ type Error struct {
} }
type ForgotPasswordInput struct { type ForgotPasswordInput struct {
Email string `json:"email"` Email *string `json:"email,omitempty"`
PhoneNumber *string `json:"phone_number,omitempty"`
State *string `json:"state,omitempty"` State *string `json:"state,omitempty"`
RedirectURI *string `json:"redirect_uri,omitempty"` RedirectURI *string `json:"redirect_uri,omitempty"`
} }
type ForgotPasswordResponse struct {
Message string `json:"message"`
ShouldShowMobileOtpScreen *bool `json:"should_show_mobile_otp_screen,omitempty"`
}
type GenerateJWTKeysInput struct { type GenerateJWTKeysInput struct {
Type string `json:"type"` Type string `json:"type"`
} }
@ -272,7 +278,9 @@ type ResendVerifyEmailInput struct {
} }
type ResetPasswordInput struct { type ResetPasswordInput struct {
Token string `json:"token"` Token *string `json:"token,omitempty"`
Otp *string `json:"otp,omitempty"`
PhoneNumber *string `json:"phone_number,omitempty"`
Password string `json:"password"` Password string `json:"password"`
ConfirmPassword string `json:"confirm_password"` ConfirmPassword string `json:"confirm_password"`
} }

View File

@ -117,6 +117,11 @@ type Response {
message: String! message: String!
} }
type ForgotPasswordResponse {
message: String!
should_show_mobile_otp_screen: Boolean
}
type InviteMembersResponse { type InviteMembersResponse {
message: String! message: String!
Users: [User!]! Users: [User!]!
@ -449,13 +454,16 @@ input UpdateUserInput {
} }
input ForgotPasswordInput { input ForgotPasswordInput {
email: String! email: String
phone_number: String
state: String state: String
redirect_uri: String redirect_uri: String
} }
input ResetPasswordInput { input ResetPasswordInput {
token: String! token: String
otp: String
phone_number: String
password: String! password: String!
confirm_password: String! confirm_password: String!
} }
@ -608,7 +616,7 @@ type Mutation {
update_profile(params: UpdateProfileInput!): Response! update_profile(params: UpdateProfileInput!): Response!
verify_email(params: VerifyEmailInput!): AuthResponse! verify_email(params: VerifyEmailInput!): AuthResponse!
resend_verify_email(params: ResendVerifyEmailInput!): Response! resend_verify_email(params: ResendVerifyEmailInput!): Response!
forgot_password(params: ForgotPasswordInput!): Response! forgot_password(params: ForgotPasswordInput!): ForgotPasswordResponse!
reset_password(params: ResetPasswordInput!): Response! reset_password(params: ResetPasswordInput!): Response!
revoke(params: OAuthRevokeInput!): Response! revoke(params: OAuthRevokeInput!): Response!
verify_otp(params: VerifyOTPRequest!): AuthResponse! verify_otp(params: VerifyOTPRequest!): AuthResponse!

View File

@ -58,7 +58,7 @@ func (r *mutationResolver) ResendVerifyEmail(ctx context.Context, params model.R
} }
// ForgotPassword is the resolver for the forgot_password field. // ForgotPassword is the resolver for the forgot_password field.
func (r *mutationResolver) ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) { func (r *mutationResolver) ForgotPassword(ctx context.Context, params model.ForgotPasswordInput) (*model.ForgotPasswordResponse, error) {
return resolvers.ForgotPasswordResolver(ctx, params) return resolvers.ForgotPasswordResolver(ctx, params)
} }

View File

@ -6,29 +6,29 @@ import (
"strings" "strings"
"time" "time"
"github.com/google/uuid"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"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" mailService "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/memorystore" "github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/parsers" "github.com/authorizerdev/authorizer/server/parsers"
"github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/smsproviders"
"github.com/authorizerdev/authorizer/server/token" "github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
"github.com/authorizerdev/authorizer/server/validators"
) )
// ForgotPasswordResolver is a resolver for forgot password mutation // ForgotPasswordResolver is a resolver for forgot password mutation
func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInput) (*model.Response, error) { func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInput) (*model.ForgotPasswordResponse, error) {
var res *model.Response
gc, err := utils.GinContextFromContext(ctx) gc, err := utils.GinContextFromContext(ctx)
if err != nil { if err != nil {
log.Debug("Failed to get GinContext: ", err) log.Debug("Failed to get GinContext: ", err)
return res, err return nil, err
} }
isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication) isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication)
@ -36,33 +36,64 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
log.Debug("Error getting basic auth disabled: ", err) log.Debug("Error getting basic auth disabled: ", err)
isBasicAuthDisabled = true isBasicAuthDisabled = true
} }
if isBasicAuthDisabled { isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification)
log.Debug("Basic authentication is disabled")
return res, fmt.Errorf(`basic authentication is disabled for this instance`)
}
params.Email = strings.ToLower(params.Email)
if !validators.IsValidEmail(params.Email) {
log.Debug("Invalid email address: ", params.Email)
return res, fmt.Errorf("invalid email")
}
log := log.WithFields(log.Fields{
"email": params.Email,
})
user, err := db.Provider.GetUserByEmail(ctx, params.Email)
if err != nil { if err != nil {
log.Debug("User not found: ", err) log.Debug("Error getting email verification disabled: ", err)
return res, fmt.Errorf(`user with this email not found`) isEmailVerificationDisabled = true
} }
isMobileBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication)
if err != nil {
log.Debug("Error getting mobile basic auth disabled: ", err)
isMobileBasicAuthDisabled = true
}
isMobileVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisablePhoneVerification)
if err != nil {
log.Debug("Error getting mobile verification disabled: ", err)
isMobileVerificationDisabled = true
}
email := refs.StringValue(params.Email)
phoneNumber := refs.StringValue(params.PhoneNumber)
if email == "" && phoneNumber == "" {
log.Debug("Email or phone number is required")
return nil, fmt.Errorf(`email or phone number is required`)
}
log := log.WithFields(log.Fields{
"email": refs.StringValue(params.Email),
"phone_number": refs.StringValue(params.PhoneNumber),
})
isEmailLogin := email != ""
isMobileLogin := phoneNumber != ""
if isBasicAuthDisabled && isEmailLogin && !isEmailVerificationDisabled {
log.Debug("Basic authentication is disabled.")
return nil, fmt.Errorf(`basic authentication is disabled for this instance`)
}
if isMobileBasicAuthDisabled && isMobileLogin && !isMobileVerificationDisabled {
log.Debug("Mobile basic authentication is disabled.")
return nil, fmt.Errorf(`mobile basic authentication is disabled for this instance`)
}
var user *models.User
if isEmailLogin {
user, err = db.Provider.GetUserByEmail(ctx, email)
} else {
user, err = db.Provider.GetUserByPhoneNumber(ctx, phoneNumber)
}
if err != nil {
log.Debug("Failed to get user: ", err)
return nil, fmt.Errorf(`bad user credentials`)
}
hostname := parsers.GetHost(gc) hostname := parsers.GetHost(gc)
_, nonceHash, err := utils.GenerateNonce() _, nonceHash, err := utils.GenerateNonce()
if err != nil { if err != nil {
log.Debug("Failed to generate nonce: ", err) log.Debug("Failed to generate nonce: ", err)
return res, err return nil, err
} }
if user.RevokedTimestamp != nil {
log.Debug("User access is revoked")
return nil, fmt.Errorf(`user access has been revoked`)
}
if isEmailLogin {
redirectURI := "" redirectURI := ""
// give higher preference to params redirect uri // give higher preference to params redirect uri
if strings.TrimSpace(refs.StringValue(params.RedirectURI)) != "" { if strings.TrimSpace(refs.StringValue(params.RedirectURI)) != "" {
@ -75,35 +106,64 @@ func ForgotPasswordResolver(ctx context.Context, params model.ForgotPasswordInpu
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyResetPasswordURL, redirectURI) memorystore.Provider.UpdateEnvVariable(constants.EnvKeyResetPasswordURL, redirectURI)
} }
} }
verificationToken, err := token.CreateVerificationToken(email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURI)
verificationToken, err := token.CreateVerificationToken(params.Email, constants.VerificationTypeForgotPassword, hostname, nonceHash, redirectURI)
if err != nil { if err != nil {
log.Debug("Failed to create verification token", err) log.Debug("Failed to create verification token", err)
return res, err return nil, err
} }
_, err = db.Provider.AddVerificationRequest(ctx, &models.VerificationRequest{ _, err = db.Provider.AddVerificationRequest(ctx, &models.VerificationRequest{
Token: verificationToken, 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: email,
Nonce: nonceHash, Nonce: nonceHash,
RedirectURI: redirectURI, RedirectURI: redirectURI,
}) })
if err != nil { if err != nil {
log.Debug("Failed to add verification request", err) log.Debug("Failed to add verification request", err)
return res, err return nil, err
} }
// execute it as go routine so that we can reduce the api latency // execute it as go routine so that we can reduce the api latency
go email.SendEmail([]string{params.Email}, constants.VerificationTypeForgotPassword, map[string]interface{}{ go mailService.SendEmail([]string{email}, constants.VerificationTypeForgotPassword, map[string]interface{}{
"user": user.ToMap(), "user": user.ToMap(),
"organization": utils.GetOrganization(), "organization": utils.GetOrganization(),
"verification_url": utils.GetForgotPasswordURL(verificationToken, redirectURI), "verification_url": utils.GetForgotPasswordURL(verificationToken, redirectURI),
}) })
return &model.ForgotPasswordResponse{
res = &model.Response{
Message: `Please check your inbox! We have sent a password reset link.`, Message: `Please check your inbox! We have sent a password reset link.`,
}, nil
} }
if isMobileLogin {
return res, nil expiresAt := time.Now().Add(1 * time.Minute).Unix()
otp := utils.GenerateOTP()
otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{
Email: refs.StringValue(user.Email),
PhoneNumber: refs.StringValue(user.PhoneNumber),
Otp: otp,
ExpiresAt: expiresAt,
})
if err != nil {
log.Debug("Failed to add otp: ", err)
return nil, err
}
mfaSession := uuid.NewString()
err = memorystore.Provider.SetMfaSession(user.ID, mfaSession, expiresAt)
if err != nil {
log.Debug("Failed to add mfasession: ", err)
return nil, err
}
cookie.SetMfaSession(gc, mfaSession)
smsBody := strings.Builder{}
smsBody.WriteString("Your verification code is: ")
smsBody.WriteString(otpData.Otp)
if err := smsproviders.SendSMS(phoneNumber, smsBody.String()); err != nil {
log.Debug("Failed to send sms: ", err)
// continue
}
return &model.ForgotPasswordResponse{
Message: "Please enter the OTP sent to your phone number and change your password.",
ShouldShowMobileOtpScreen: refs.NewBoolRef(true),
}, nil
}
return nil, fmt.Errorf(`email or phone number verification needs to be enabled`)
} }

View File

@ -16,6 +16,7 @@ import (
"github.com/authorizerdev/authorizer/server/cookie" "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"
mailService "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/memorystore" "github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/refs"
@ -23,8 +24,6 @@ import (
"github.com/authorizerdev/authorizer/server/token" "github.com/authorizerdev/authorizer/server/token"
"github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/utils"
"github.com/authorizerdev/authorizer/server/validators" "github.com/authorizerdev/authorizer/server/validators"
mailService "github.com/authorizerdev/authorizer/server/email"
) )
// LoginResolver is a resolver for login mutation // LoginResolver is a resolver for login mutation
@ -173,6 +172,7 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes
otp := utils.GenerateOTP() otp := utils.GenerateOTP()
otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{ otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{
Email: refs.StringValue(user.Email), Email: refs.StringValue(user.Email),
PhoneNumber: refs.StringValue(user.PhoneNumber),
Otp: otp, Otp: otp,
ExpiresAt: expiresAt, ExpiresAt: expiresAt,
}) })

View File

@ -9,8 +9,10 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/authorizerdev/authorizer/server/constants" "github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/cookie"
"github.com/authorizerdev/authorizer/server/crypto" "github.com/authorizerdev/authorizer/server/crypto"
"github.com/authorizerdev/authorizer/server/db" "github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/db/models"
"github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore" "github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/parsers" "github.com/authorizerdev/authorizer/server/parsers"
@ -29,36 +31,50 @@ func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput)
log.Debug("Failed to get GinContext: ", err) log.Debug("Failed to get GinContext: ", err)
return res, err return res, err
} }
verifyingToken := refs.StringValue(params.Token)
otp := refs.StringValue(params.Otp)
if verifyingToken == "" && otp == "" {
log.Debug("Token or OTP is required")
return res, fmt.Errorf(`token or otp is required`)
}
isTokenVerification := verifyingToken != ""
isOtpVerification := otp != ""
if isOtpVerification && refs.StringValue(params.PhoneNumber) == "" {
log.Debug("Phone number is required")
return res, fmt.Errorf(`phone number is required`)
}
isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication) isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableBasicAuthentication)
if err != nil { if err != nil {
log.Debug("Error getting basic auth disabled: ", err) log.Debug("Error getting basic auth disabled: ", err)
isBasicAuthDisabled = true isBasicAuthDisabled = true
} }
if isBasicAuthDisabled { isMobileBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication)
if err != nil {
log.Debug("Error getting mobile basic auth disabled: ", err)
isBasicAuthDisabled = true
}
if isTokenVerification && isBasicAuthDisabled {
log.Debug("Basic authentication is disabled") log.Debug("Basic authentication is disabled")
return res, fmt.Errorf(`basic authentication is disabled for this instance`) return res, fmt.Errorf(`basic authentication is disabled for this instance`)
} }
if isOtpVerification && isMobileBasicAuthDisabled {
verificationRequest, err := db.Provider.GetVerificationRequestByToken(ctx, params.Token) log.Debug("Mobile basic authentication is disabled")
return res, fmt.Errorf(`mobile basic authentication is disabled for this instance`)
}
email := ""
phoneNumber := refs.StringValue(params.PhoneNumber)
var user *models.User
var verificationRequest *models.VerificationRequest
var otpRequest *models.OTP
if isTokenVerification {
verificationRequest, err = db.Provider.GetVerificationRequestByToken(ctx, verifyingToken)
if err != nil { if err != nil {
log.Debug("Failed to get verification request: ", err) log.Debug("Failed to get verification request: ", err)
return res, fmt.Errorf(`invalid token`) return res, fmt.Errorf(`invalid token`)
} }
if params.Password != params.ConfirmPassword {
log.Debug("Passwords do not match")
return res, fmt.Errorf(`passwords don't match`)
}
if err := validators.IsValidPassword(params.Password); err != nil {
log.Debug("Invalid password")
return res, err
}
// verify if token exists in db // verify if token exists in db
hostname := parsers.GetHost(gc) hostname := parsers.GetHost(gc)
claim, err := token.ParseJWTToken(params.Token) claim, err := token.ParseJWTToken(verifyingToken)
if err != nil { if err != nil {
log.Debug("Failed to parse token: ", err) log.Debug("Failed to parse token: ", err)
return res, fmt.Errorf(`invalid token`) return res, fmt.Errorf(`invalid token`)
@ -68,58 +84,102 @@ func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput)
log.Debug("Failed to validate jwt claims: ", err) log.Debug("Failed to validate jwt claims: ", err)
return res, fmt.Errorf(`invalid token`) return res, fmt.Errorf(`invalid token`)
} }
email = claim["sub"].(string)
email := claim["sub"].(string) user, err = db.Provider.GetUserByEmail(ctx, email)
log := log.WithFields(log.Fields{
"email": email,
})
user, err := db.Provider.GetUserByEmail(ctx, email)
if err != nil { if err != nil {
log.Debug("Failed to get user: ", err) log.Debug("Failed to get user: ", err)
return res, err return res, err
} }
}
if isOtpVerification {
mfaSession, err := cookie.GetMfaSession(gc)
if err != nil {
log.Debug("Failed to get otp request by email: ", err)
return res, fmt.Errorf(`invalid session: %s`, err.Error())
}
// Get user by phone number
user, err = db.Provider.GetUserByPhoneNumber(ctx, phoneNumber)
if err != nil {
log.Debug("Failed to get user by phone number: ", err)
return res, fmt.Errorf(`user not found`)
}
if _, err := memorystore.Provider.GetMfaSession(user.ID, mfaSession); err != nil {
log.Debug("Failed to get mfa session: ", err)
return res, fmt.Errorf(`invalid session: %s`, err.Error())
}
otpRequest, err = db.Provider.GetOTPByPhoneNumber(ctx, phoneNumber)
if err != nil {
log.Debug("Failed to get otp request by phone number: ", err)
return res, fmt.Errorf(`invalid otp`)
}
if otpRequest.Otp != otp {
log.Debug("Failed to verify otp request: Incorrect value")
return res, fmt.Errorf(`invalid otp`)
}
}
if params.Password != params.ConfirmPassword {
log.Debug("Passwords do not match")
return res, fmt.Errorf(`passwords don't match`)
}
if err := validators.IsValidPassword(params.Password); err != nil {
log.Debug("Invalid password")
return res, err
}
log := log.WithFields(log.Fields{
"email": email,
"phone": phoneNumber,
})
password, _ := crypto.EncryptPassword(params.Password) password, _ := crypto.EncryptPassword(params.Password)
user.Password = &password user.Password = &password
signupMethod := user.SignupMethods signupMethod := user.SignupMethods
if !strings.Contains(signupMethod, constants.AuthRecipeMethodBasicAuth) { if !strings.Contains(signupMethod, constants.AuthRecipeMethodBasicAuth) && isTokenVerification {
signupMethod = signupMethod + "," + constants.AuthRecipeMethodBasicAuth signupMethod = signupMethod + "," + constants.AuthRecipeMethodBasicAuth
isMFAEnforced, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyEnforceMultiFactorAuthentication)
if err != nil {
log.Debug("MFA service not enabled: ", err)
isMFAEnforced = false
}
if isMFAEnforced {
user.IsMultiFactorAuthEnabled = refs.NewBoolRef(true)
}
}
user.SignupMethods = signupMethod
// helpful if user has not signed up with basic auth // helpful if user has not signed up with basic auth
if user.EmailVerifiedAt == nil { if user.EmailVerifiedAt == nil {
now := time.Now().Unix() now := time.Now().Unix()
user.EmailVerifiedAt = &now user.EmailVerifiedAt = &now
} }
}
if !strings.Contains(signupMethod, constants.AuthRecipeMethodMobileOTP) && isOtpVerification {
signupMethod = signupMethod + "," + constants.AuthRecipeMethodMobileOTP
// helpful if user has not signed up with basic auth
if user.PhoneNumberVerifiedAt == nil {
now := time.Now().Unix()
user.PhoneNumberVerifiedAt = &now
}
}
user.SignupMethods = signupMethod
isMFAEnforced, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyEnforceMultiFactorAuthentication)
if err != nil {
log.Debug("MFA service not enabled: ", err)
isMFAEnforced = false
}
if isMFAEnforced {
user.IsMultiFactorAuthEnabled = refs.NewBoolRef(true)
}
_, err = db.Provider.UpdateUser(ctx, user)
if err != nil {
log.Debug("Failed to update user: ", err)
return res, err
}
if isTokenVerification {
// delete from verification table // delete from verification table
err = db.Provider.DeleteVerificationRequest(ctx, verificationRequest) err = db.Provider.DeleteVerificationRequest(ctx, verificationRequest)
if err != nil { if err != nil {
log.Debug("Failed to delete verification request: ", err) log.Debug("Failed to delete verification request: ", err)
return res, err return res, err
} }
}
_, err = db.Provider.UpdateUser(ctx, user) if isOtpVerification {
// delete from otp table
err = db.Provider.DeleteOTP(ctx, otpRequest)
if err != nil { if err != nil {
log.Debug("Failed to update user: ", err) log.Debug("Failed to delete otp request: ", err)
return res, err return res, err
} }
}
res = &model.Response{ res = &model.Response{
Message: `Password updated successfully.`, Message: `Password updated successfully.`,
} }
return res, nil return res, nil
} }

View File

@ -0,0 +1,61 @@
package test
import (
"fmt"
"strings"
"testing"
"time"
"github.com/authorizerdev/authorizer/server/constants"
"github.com/authorizerdev/authorizer/server/db"
"github.com/authorizerdev/authorizer/server/graph/model"
"github.com/authorizerdev/authorizer/server/memorystore"
"github.com/authorizerdev/authorizer/server/refs"
"github.com/authorizerdev/authorizer/server/resolvers"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
)
func forgotPasswordMobileTest(t *testing.T, s TestSetup) {
t.Helper()
t.Run(`should run forgot password for mobile`, func(t *testing.T) {
req, ctx := createContext(s)
phoneNumber := "6240345678"
res, err := resolvers.SignupResolver(ctx, model.SignUpInput{
PhoneNumber: refs.NewStringRef(phoneNumber),
Password: s.TestInfo.Password,
ConfirmPassword: s.TestInfo.Password,
})
assert.NoError(t, err)
assert.NotNil(t, res)
forgotPasswordRes, err := resolvers.ForgotPasswordResolver(ctx, model.ForgotPasswordInput{
PhoneNumber: refs.NewStringRef(phoneNumber),
})
assert.Nil(t, err, "no errors for forgot password")
assert.NotNil(t, forgotPasswordRes)
assert.True(t, *forgotPasswordRes.ShouldShowMobileOtpScreen)
otpReq, err := db.Provider.GetOTPByPhoneNumber(ctx, phoneNumber)
assert.Nil(t, err)
mfaSession := uuid.NewString()
memorystore.Provider.SetMfaSession(res.User.ID, mfaSession, time.Now().Add(1*time.Minute).Unix())
cookie := fmt.Sprintf("%s=%s;", constants.MfaCookieName+"_session", mfaSession)
cookie = strings.TrimSuffix(cookie, ";")
req.Header.Set("Cookie", cookie)
// Reset password
resetPasswordRes, err := resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{
PhoneNumber: refs.NewStringRef(phoneNumber),
Otp: refs.NewStringRef(otpReq.Otp),
Password: s.TestInfo.Password + "test",
ConfirmPassword: s.TestInfo.Password + "test",
})
assert.Nil(t, err)
assert.NotNil(t, resetPasswordRes)
// Test login
loginRes, err := resolvers.LoginResolver(ctx, model.LoginInput{
PhoneNumber: refs.NewStringRef(phoneNumber),
Password: s.TestInfo.Password + "test",
})
assert.Nil(t, err)
assert.NotNil(t, loginRes)
})
}

View File

@ -24,7 +24,7 @@ func forgotPasswordTest(t *testing.T, s TestSetup) {
assert.NoError(t, err) assert.NoError(t, err)
assert.NotNil(t, res) assert.NotNil(t, res)
forgotPasswordRes, err := resolvers.ForgotPasswordResolver(ctx, model.ForgotPasswordInput{ forgotPasswordRes, err := resolvers.ForgotPasswordResolver(ctx, model.ForgotPasswordInput{
Email: email, Email: refs.NewStringRef(email),
}) })
assert.Nil(t, err, "no errors for forgot password") assert.Nil(t, err, "no errors for forgot password")
assert.NotNil(t, forgotPasswordRes) assert.NotNil(t, forgotPasswordRes)

View File

@ -130,6 +130,7 @@ func TestResolvers(t *testing.T) {
mobileLoginTests(t, s) mobileLoginTests(t, s)
totpLoginTest(t, s) totpLoginTest(t, s)
forgotPasswordTest(t, s) forgotPasswordTest(t, s)
forgotPasswordMobileTest(t, s)
resendVerifyEmailTests(t, s) resendVerifyEmailTests(t, s)
resetPasswordTest(t, s) resetPasswordTest(t, s)
verifyEmailTest(t, s) verifyEmailTest(t, s)

View File

@ -23,37 +23,30 @@ func resetPasswordTest(t *testing.T, s TestSetup) {
}) })
assert.NoError(t, err) assert.NoError(t, err)
_, err = resolvers.ForgotPasswordResolver(ctx, model.ForgotPasswordInput{ _, err = resolvers.ForgotPasswordResolver(ctx, model.ForgotPasswordInput{
Email: email, Email: refs.NewStringRef(email),
}) })
assert.Nil(t, err, "no errors for forgot password") assert.Nil(t, err, "no errors for forgot password")
verificationRequest, err := db.Provider.GetVerificationRequestByEmail(ctx, email, constants.VerificationTypeForgotPassword) verificationRequest, err := db.Provider.GetVerificationRequestByEmail(ctx, email, constants.VerificationTypeForgotPassword)
assert.Nil(t, err, "should get forgot password request") assert.Nil(t, err, "should get forgot password request")
assert.NotNil(t, verificationRequest) assert.NotNil(t, verificationRequest)
_, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{ _, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{
Token: verificationRequest.Token, Token: refs.NewStringRef(verificationRequest.Token),
Password: "test1", Password: "test1",
ConfirmPassword: "test", ConfirmPassword: "test",
}) })
assert.NotNil(t, err, "passwords don't match") assert.NotNil(t, err, "passwords don't match")
_, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{ _, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{
Token: verificationRequest.Token, Token: refs.NewStringRef(verificationRequest.Token),
Password: "test1", Password: "test1",
ConfirmPassword: "test1", ConfirmPassword: "test1",
}) })
assert.NotNil(t, err, "invalid password") assert.NotNil(t, err, "invalid password")
_, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{ _, err = resolvers.ResetPasswordResolver(ctx, model.ResetPasswordInput{
Token: verificationRequest.Token, Token: refs.NewStringRef(verificationRequest.Token),
Password: "Test@1234", Password: "Test@1234",
ConfirmPassword: "Test@1234", ConfirmPassword: "Test@1234",
}) })
assert.Nil(t, err, "password changed successfully") assert.Nil(t, err, "password changed successfully")
cleanData(email) cleanData(email)
}) })
} }