feat: add signup + login using mobile
This commit is contained in:
parent
1eb8965f98
commit
313b510ba1
|
@ -25,7 +25,7 @@ type User struct {
|
||||||
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname" dynamo:"nickname"`
|
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname" dynamo:"nickname"`
|
||||||
Gender *string `json:"gender" bson:"gender" cql:"gender" dynamo:"gender"`
|
Gender *string `json:"gender" bson:"gender" cql:"gender" dynamo:"gender"`
|
||||||
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate" dynamo:"birthdate"`
|
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate" dynamo:"birthdate"`
|
||||||
PhoneNumber *string `gorm:"index" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number" index:"phone_number,hash"`
|
PhoneNumber *string `gorm:"index" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number"`
|
||||||
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at" dynamo:"phone_number_verified_at"`
|
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at" dynamo:"phone_number_verified_at"`
|
||||||
Picture *string `json:"picture" bson:"picture" cql:"picture" dynamo:"picture"`
|
Picture *string `json:"picture" bson:"picture" cql:"picture" dynamo:"picture"`
|
||||||
Roles string `json:"roles" bson:"roles" cql:"roles" dynamo:"roles"`
|
Roles string `json:"roles" bson:"roles" cql:"roles" dynamo:"roles"`
|
||||||
|
|
|
@ -90,12 +90,6 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
|
||||||
func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.User, error) {
|
func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.User, error) {
|
||||||
user.UpdatedAt = time.Now().Unix()
|
user.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
|
|
||||||
if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil && u.ID != user.ID {
|
|
||||||
return user, fmt.Errorf("user with given phone number already exists")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes, err := json.Marshal(user)
|
bytes, err := json.Marshal(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return user, err
|
return user, err
|
||||||
|
|
|
@ -34,7 +34,6 @@ func (p *provider) AddEnv(ctx context.Context, env models.Env) (models.Env, erro
|
||||||
|
|
||||||
// UpdateEnv to update environment information in database
|
// UpdateEnv to update environment information in database
|
||||||
func (p *provider) UpdateEnv(ctx context.Context, env models.Env) (models.Env, error) {
|
func (p *provider) UpdateEnv(ctx context.Context, env models.Env) (models.Env, error) {
|
||||||
|
|
||||||
collection := p.db.Table(models.Collections.Env)
|
collection := p.db.Table(models.Collections.Env)
|
||||||
env.UpdatedAt = time.Now().Unix()
|
env.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
|
|
|
@ -58,12 +58,6 @@ func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.Use
|
||||||
|
|
||||||
user.UpdatedAt = time.Now().Unix()
|
user.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
|
|
||||||
if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil && u.ID != user.ID {
|
|
||||||
return user, fmt.Errorf("user with given phone number already exists")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := UpdateByHashKey(collection, "id", user.ID, user)
|
err := UpdateByHashKey(collection, "id", user.ID, user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return user, err
|
return user, err
|
||||||
|
@ -215,7 +209,7 @@ func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string)
|
||||||
var user models.User
|
var user models.User
|
||||||
|
|
||||||
collection := p.db.Table(models.Collections.User)
|
collection := p.db.Table(models.Collections.User)
|
||||||
err := collection.Scan().Index("phone_number").Filter("'phone_number' = ?", phoneNumber).AllWithContext(ctx, &users)
|
err := collection.Scan().Filter("'phone_number' = ?", phoneNumber).AllWithContext(ctx, &users)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -56,12 +56,6 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
|
||||||
func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.User, error) {
|
func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.User, error) {
|
||||||
user.UpdatedAt = time.Now().Unix()
|
user.UpdatedAt = time.Now().Unix()
|
||||||
|
|
||||||
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
|
|
||||||
if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil && u.ID != user.ID {
|
|
||||||
return user, fmt.Errorf("user with given phone number already exists")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result := p.db.Save(&user)
|
result := p.db.Save(&user)
|
||||||
|
|
||||||
if result.Error != nil {
|
if result.Error != nil {
|
||||||
|
|
1
server/env/persist_env.go
vendored
1
server/env/persist_env.go
vendored
|
@ -75,7 +75,6 @@ func GetEnvData() (map[string]interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyEncryptionKey, decryptedEncryptionKey)
|
||||||
|
|
||||||
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
|
b64DecryptedConfig, err := crypto.DecryptB64(env.EnvData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Error while decrypting env data from B64: ", err)
|
log.Debug("Error while decrypting env data from B64: ", err)
|
||||||
|
|
|
@ -171,7 +171,8 @@ type ComplexityRoot struct {
|
||||||
Login func(childComplexity int, params model.LoginInput) int
|
Login func(childComplexity int, params model.LoginInput) int
|
||||||
Logout func(childComplexity int) int
|
Logout func(childComplexity int) int
|
||||||
MagicLinkLogin func(childComplexity int, params model.MagicLinkLoginInput) int
|
MagicLinkLogin func(childComplexity int, params model.MagicLinkLoginInput) int
|
||||||
MobileBasicAuthSignup func(childComplexity int, params *model.MobileBasicAuthSignUpUpInput) int
|
MobileLogin func(childComplexity int, params model.MobileLoginInput) int
|
||||||
|
MobileSignup func(childComplexity int, params *model.MobileSignUpInput) int
|
||||||
ResendOtp func(childComplexity int, params model.ResendOTPRequest) int
|
ResendOtp func(childComplexity int, params model.ResendOTPRequest) int
|
||||||
ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int
|
ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int
|
||||||
ResetPassword func(childComplexity int, params model.ResetPasswordInput) int
|
ResetPassword func(childComplexity int, params model.ResetPasswordInput) int
|
||||||
|
@ -301,8 +302,9 @@ type ComplexityRoot struct {
|
||||||
|
|
||||||
type MutationResolver interface {
|
type MutationResolver interface {
|
||||||
Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, error)
|
Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, error)
|
||||||
MobileBasicAuthSignup(ctx context.Context, params *model.MobileBasicAuthSignUpUpInput) (*model.AuthResponse, error)
|
MobileSignup(ctx context.Context, params *model.MobileSignUpInput) (*model.AuthResponse, error)
|
||||||
Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, error)
|
Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, error)
|
||||||
|
MobileLogin(ctx context.Context, params model.MobileLoginInput) (*model.AuthResponse, error)
|
||||||
MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error)
|
MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error)
|
||||||
Logout(ctx context.Context) (*model.Response, error)
|
Logout(ctx context.Context) (*model.Response, error)
|
||||||
UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error)
|
UpdateProfile(ctx context.Context, params model.UpdateProfileInput) (*model.Response, error)
|
||||||
|
@ -1161,17 +1163,29 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
|
||||||
|
|
||||||
return e.complexity.Mutation.MagicLinkLogin(childComplexity, args["params"].(model.MagicLinkLoginInput)), true
|
return e.complexity.Mutation.MagicLinkLogin(childComplexity, args["params"].(model.MagicLinkLoginInput)), true
|
||||||
|
|
||||||
case "Mutation.mobile_basic_auth_signup":
|
case "Mutation.mobile_login":
|
||||||
if e.complexity.Mutation.MobileBasicAuthSignup == nil {
|
if e.complexity.Mutation.MobileLogin == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
args, err := ec.field_Mutation_mobile_basic_auth_signup_args(context.TODO(), rawArgs)
|
args, err := ec.field_Mutation_mobile_login_args(context.TODO(), rawArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.complexity.Mutation.MobileBasicAuthSignup(childComplexity, args["params"].(*model.MobileBasicAuthSignUpUpInput)), true
|
return e.complexity.Mutation.MobileLogin(childComplexity, args["params"].(model.MobileLoginInput)), true
|
||||||
|
|
||||||
|
case "Mutation.mobile_signup":
|
||||||
|
if e.complexity.Mutation.MobileSignup == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
args, err := ec.field_Mutation_mobile_signup_args(context.TODO(), rawArgs)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.complexity.Mutation.MobileSignup(childComplexity, args["params"].(*model.MobileSignUpInput)), true
|
||||||
|
|
||||||
case "Mutation.resend_otp":
|
case "Mutation.resend_otp":
|
||||||
if e.complexity.Mutation.ResendOtp == nil {
|
if e.complexity.Mutation.ResendOtp == nil {
|
||||||
|
@ -1898,7 +1912,8 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler {
|
||||||
ec.unmarshalInputListWebhookLogRequest,
|
ec.unmarshalInputListWebhookLogRequest,
|
||||||
ec.unmarshalInputLoginInput,
|
ec.unmarshalInputLoginInput,
|
||||||
ec.unmarshalInputMagicLinkLoginInput,
|
ec.unmarshalInputMagicLinkLoginInput,
|
||||||
ec.unmarshalInputMobileBasicAuthSignUpUpInput,
|
ec.unmarshalInputMobileLoginInput,
|
||||||
|
ec.unmarshalInputMobileSignUpInput,
|
||||||
ec.unmarshalInputOAuthRevokeInput,
|
ec.unmarshalInputOAuthRevokeInput,
|
||||||
ec.unmarshalInputPaginatedInput,
|
ec.unmarshalInputPaginatedInput,
|
||||||
ec.unmarshalInputPaginationInput,
|
ec.unmarshalInputPaginationInput,
|
||||||
|
@ -2249,7 +2264,7 @@ input AdminSignupInput {
|
||||||
admin_secret: String!
|
admin_secret: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input MobileBasicAuthSignUpUpInput {
|
input MobileSignUpInput {
|
||||||
email: String
|
email: String
|
||||||
given_name: String
|
given_name: String
|
||||||
family_name: String
|
family_name: String
|
||||||
|
@ -2304,6 +2319,17 @@ input LoginInput {
|
||||||
state: String
|
state: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input MobileLoginInput {
|
||||||
|
phone_number: String!
|
||||||
|
password: String!
|
||||||
|
roles: [String!]
|
||||||
|
scope: [String!]
|
||||||
|
# state is used for authorization code grant flow
|
||||||
|
# it is used to get code for an on-going auth process during login
|
||||||
|
# and use that code for setting ` + "`" + `c_hash` + "`" + ` in id_token
|
||||||
|
state: String
|
||||||
|
}
|
||||||
|
|
||||||
input VerifyEmailInput {
|
input VerifyEmailInput {
|
||||||
token: String!
|
token: String!
|
||||||
# state is used for authorization code grant flow
|
# state is used for authorization code grant flow
|
||||||
|
@ -2486,8 +2512,9 @@ input ResendOTPRequest {
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
signup(params: SignUpInput!): AuthResponse!
|
signup(params: SignUpInput!): AuthResponse!
|
||||||
mobile_basic_auth_signup(params: MobileBasicAuthSignUpUpInput): AuthResponse!
|
mobile_signup(params: MobileSignUpInput): AuthResponse!
|
||||||
login(params: LoginInput!): AuthResponse!
|
login(params: LoginInput!): AuthResponse!
|
||||||
|
mobile_login(params: MobileLoginInput!): AuthResponse!
|
||||||
magic_link_login(params: MagicLinkLoginInput!): Response!
|
magic_link_login(params: MagicLinkLoginInput!): Response!
|
||||||
logout: Response!
|
logout: Response!
|
||||||
update_profile(params: UpdateProfileInput!): Response!
|
update_profile(params: UpdateProfileInput!): Response!
|
||||||
|
@ -2826,13 +2853,28 @@ func (ec *executionContext) field_Mutation_magic_link_login_args(ctx context.Con
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) field_Mutation_mobile_basic_auth_signup_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
func (ec *executionContext) field_Mutation_mobile_login_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 *model.MobileBasicAuthSignUpUpInput
|
var arg0 model.MobileLoginInput
|
||||||
if tmp, ok := rawArgs["params"]; ok {
|
if tmp, ok := rawArgs["params"]; ok {
|
||||||
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
|
||||||
arg0, err = ec.unmarshalOMobileBasicAuthSignUpUpInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐMobileBasicAuthSignUpUpInput(ctx, tmp)
|
arg0, err = ec.unmarshalNMobileLoginInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐMobileLoginInput(ctx, tmp)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args["params"] = arg0
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) field_Mutation_mobile_signup_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
var err error
|
||||||
|
args := map[string]interface{}{}
|
||||||
|
var arg0 *model.MobileSignUpInput
|
||||||
|
if tmp, ok := rawArgs["params"]; ok {
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params"))
|
||||||
|
arg0, err = ec.unmarshalOMobileSignUpInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐMobileSignUpInput(ctx, tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -7072,8 +7114,8 @@ func (ec *executionContext) fieldContext_Mutation_signup(ctx context.Context, fi
|
||||||
return fc, nil
|
return fc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Mutation_mobile_basic_auth_signup(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Mutation_mobile_signup(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
fc, err := ec.fieldContext_Mutation_mobile_basic_auth_signup(ctx, field)
|
fc, err := ec.fieldContext_Mutation_mobile_signup(ctx, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return graphql.Null
|
return graphql.Null
|
||||||
}
|
}
|
||||||
|
@ -7086,7 +7128,7 @@ func (ec *executionContext) _Mutation_mobile_basic_auth_signup(ctx context.Conte
|
||||||
}()
|
}()
|
||||||
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.Mutation().MobileBasicAuthSignup(rctx, fc.Args["params"].(*model.MobileBasicAuthSignUpUpInput))
|
return ec.resolvers.Mutation().MobileSignup(rctx, fc.Args["params"].(*model.MobileSignUpInput))
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
|
@ -7103,7 +7145,7 @@ func (ec *executionContext) _Mutation_mobile_basic_auth_signup(ctx context.Conte
|
||||||
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) fieldContext_Mutation_mobile_basic_auth_signup(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
func (ec *executionContext) fieldContext_Mutation_mobile_signup(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||||
fc = &graphql.FieldContext{
|
fc = &graphql.FieldContext{
|
||||||
Object: "Mutation",
|
Object: "Mutation",
|
||||||
Field: field,
|
Field: field,
|
||||||
|
@ -7136,7 +7178,7 @@ func (ec *executionContext) fieldContext_Mutation_mobile_basic_auth_signup(ctx c
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
ctx = graphql.WithFieldContext(ctx, fc)
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
if fc.Args, err = ec.field_Mutation_mobile_basic_auth_signup_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
|
if fc.Args, err = ec.field_Mutation_mobile_signup_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
|
||||||
ec.Error(ctx, err)
|
ec.Error(ctx, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -7214,6 +7256,77 @@ func (ec *executionContext) fieldContext_Mutation_login(ctx context.Context, fie
|
||||||
return fc, nil
|
return fc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) _Mutation_mobile_login(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
|
fc, err := ec.fieldContext_Mutation_mobile_login(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 ec.resolvers.Mutation().MobileLogin(rctx, fc.Args["params"].(model.MobileLoginInput))
|
||||||
|
})
|
||||||
|
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.AuthResponse)
|
||||||
|
fc.Result = res
|
||||||
|
return ec.marshalNAuthResponse2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐAuthResponse(ctx, field.Selections, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) fieldContext_Mutation_mobile_login(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) {
|
||||||
|
fc = &graphql.FieldContext{
|
||||||
|
Object: "Mutation",
|
||||||
|
Field: field,
|
||||||
|
IsMethod: true,
|
||||||
|
IsResolver: true,
|
||||||
|
Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) {
|
||||||
|
switch field.Name {
|
||||||
|
case "message":
|
||||||
|
return ec.fieldContext_AuthResponse_message(ctx, field)
|
||||||
|
case "should_show_otp_screen":
|
||||||
|
return ec.fieldContext_AuthResponse_should_show_otp_screen(ctx, field)
|
||||||
|
case "access_token":
|
||||||
|
return ec.fieldContext_AuthResponse_access_token(ctx, field)
|
||||||
|
case "id_token":
|
||||||
|
return ec.fieldContext_AuthResponse_id_token(ctx, field)
|
||||||
|
case "refresh_token":
|
||||||
|
return ec.fieldContext_AuthResponse_refresh_token(ctx, field)
|
||||||
|
case "expires_in":
|
||||||
|
return ec.fieldContext_AuthResponse_expires_in(ctx, field)
|
||||||
|
case "user":
|
||||||
|
return ec.fieldContext_AuthResponse_user(ctx, field)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("no field named %q was found under type AuthResponse", field.Name)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err = ec.Recover(ctx, r)
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
ctx = graphql.WithFieldContext(ctx, fc)
|
||||||
|
if fc.Args, err = ec.field_Mutation_mobile_login_args(ctx, field.ArgumentMap(ec.Variables)); err != nil {
|
||||||
|
ec.Error(ctx, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return fc, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) _Mutation_magic_link_login(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
func (ec *executionContext) _Mutation_magic_link_login(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) {
|
||||||
fc, err := ec.fieldContext_Mutation_magic_link_login(ctx, field)
|
fc, err := ec.fieldContext_Mutation_magic_link_login(ctx, field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -14720,8 +14833,68 @@ func (ec *executionContext) unmarshalInputMagicLinkLoginInput(ctx context.Contex
|
||||||
return it, nil
|
return it, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalInputMobileBasicAuthSignUpUpInput(ctx context.Context, obj interface{}) (model.MobileBasicAuthSignUpUpInput, error) {
|
func (ec *executionContext) unmarshalInputMobileLoginInput(ctx context.Context, obj interface{}) (model.MobileLoginInput, error) {
|
||||||
var it model.MobileBasicAuthSignUpUpInput
|
var it model.MobileLoginInput
|
||||||
|
asMap := map[string]interface{}{}
|
||||||
|
for k, v := range obj.(map[string]interface{}) {
|
||||||
|
asMap[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldsInOrder := [...]string{"phone_number", "password", "roles", "scope", "state"}
|
||||||
|
for _, k := range fieldsInOrder {
|
||||||
|
v, ok := asMap[k]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch k {
|
||||||
|
case "phone_number":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("phone_number"))
|
||||||
|
it.PhoneNumber, err = ec.unmarshalNString2string(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
case "password":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("password"))
|
||||||
|
it.Password, 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
|
||||||
|
}
|
||||||
|
case "scope":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("scope"))
|
||||||
|
it.Scope, err = ec.unmarshalOString2ᚕstringᚄ(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
case "state":
|
||||||
|
var err error
|
||||||
|
|
||||||
|
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("state"))
|
||||||
|
it.State, err = ec.unmarshalOString2ᚖstring(ctx, v)
|
||||||
|
if err != nil {
|
||||||
|
return it, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return it, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalInputMobileSignUpInput(ctx context.Context, obj interface{}) (model.MobileSignUpInput, error) {
|
||||||
|
var it model.MobileSignUpInput
|
||||||
asMap := map[string]interface{}{}
|
asMap := map[string]interface{}{}
|
||||||
for k, v := range obj.(map[string]interface{}) {
|
for k, v := range obj.(map[string]interface{}) {
|
||||||
asMap[k] = v
|
asMap[k] = v
|
||||||
|
@ -16902,10 +17075,10 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
case "mobile_basic_auth_signup":
|
case "mobile_signup":
|
||||||
|
|
||||||
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
|
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
|
||||||
return ec._Mutation_mobile_basic_auth_signup(ctx, field)
|
return ec._Mutation_mobile_signup(ctx, field)
|
||||||
})
|
})
|
||||||
|
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
|
@ -16917,6 +17090,15 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet)
|
||||||
return ec._Mutation_login(ctx, field)
|
return ec._Mutation_login(ctx, field)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if out.Values[i] == graphql.Null {
|
||||||
|
invalids++
|
||||||
|
}
|
||||||
|
case "mobile_login":
|
||||||
|
|
||||||
|
out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) {
|
||||||
|
return ec._Mutation_mobile_login(ctx, field)
|
||||||
|
})
|
||||||
|
|
||||||
if out.Values[i] == graphql.Null {
|
if out.Values[i] == graphql.Null {
|
||||||
invalids++
|
invalids++
|
||||||
}
|
}
|
||||||
|
@ -18588,6 +18770,11 @@ func (ec *executionContext) marshalNMeta2ᚖgithubᚗcomᚋauthorizerdevᚋautho
|
||||||
return ec._Meta(ctx, sel, v)
|
return ec._Meta(ctx, sel, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ec *executionContext) unmarshalNMobileLoginInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐMobileLoginInput(ctx context.Context, v interface{}) (model.MobileLoginInput, error) {
|
||||||
|
res, err := ec.unmarshalInputMobileLoginInput(ctx, v)
|
||||||
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalNOAuthRevokeInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐOAuthRevokeInput(ctx context.Context, v interface{}) (model.OAuthRevokeInput, error) {
|
func (ec *executionContext) unmarshalNOAuthRevokeInput2githubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐOAuthRevokeInput(ctx context.Context, v interface{}) (model.OAuthRevokeInput, error) {
|
||||||
res, err := ec.unmarshalInputOAuthRevokeInput(ctx, v)
|
res, err := ec.unmarshalInputOAuthRevokeInput(ctx, v)
|
||||||
return res, graphql.ErrorOnPath(ctx, err)
|
return res, graphql.ErrorOnPath(ctx, err)
|
||||||
|
@ -19382,11 +19569,11 @@ func (ec *executionContext) marshalOMap2map(ctx context.Context, sel ast.Selecti
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ec *executionContext) unmarshalOMobileBasicAuthSignUpUpInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐMobileBasicAuthSignUpUpInput(ctx context.Context, v interface{}) (*model.MobileBasicAuthSignUpUpInput, error) {
|
func (ec *executionContext) unmarshalOMobileSignUpInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐMobileSignUpInput(ctx context.Context, v interface{}) (*model.MobileSignUpInput, error) {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
res, err := ec.unmarshalInputMobileBasicAuthSignUpUpInput(ctx, v)
|
res, err := ec.unmarshalInputMobileSignUpInput(ctx, v)
|
||||||
return &res, graphql.ErrorOnPath(ctx, err)
|
return &res, graphql.ErrorOnPath(ctx, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -179,7 +179,15 @@ type Meta struct {
|
||||||
IsMultiFactorAuthEnabled bool `json:"is_multi_factor_auth_enabled"`
|
IsMultiFactorAuthEnabled bool `json:"is_multi_factor_auth_enabled"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MobileBasicAuthSignUpUpInput struct {
|
type MobileLoginInput struct {
|
||||||
|
PhoneNumber string `json:"phone_number"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Roles []string `json:"roles"`
|
||||||
|
Scope []string `json:"scope"`
|
||||||
|
State *string `json:"state"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MobileSignUpInput struct {
|
||||||
Email *string `json:"email"`
|
Email *string `json:"email"`
|
||||||
GivenName *string `json:"given_name"`
|
GivenName *string `json:"given_name"`
|
||||||
FamilyName *string `json:"family_name"`
|
FamilyName *string `json:"family_name"`
|
||||||
|
|
|
@ -269,7 +269,7 @@ input AdminSignupInput {
|
||||||
admin_secret: String!
|
admin_secret: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
input MobileBasicAuthSignUpUpInput {
|
input MobileSignUpInput {
|
||||||
email: String
|
email: String
|
||||||
given_name: String
|
given_name: String
|
||||||
family_name: String
|
family_name: String
|
||||||
|
@ -324,6 +324,17 @@ input LoginInput {
|
||||||
state: String
|
state: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input MobileLoginInput {
|
||||||
|
phone_number: String!
|
||||||
|
password: String!
|
||||||
|
roles: [String!]
|
||||||
|
scope: [String!]
|
||||||
|
# state is used for authorization code grant flow
|
||||||
|
# it is used to get code for an on-going auth process during login
|
||||||
|
# and use that code for setting `c_hash` in id_token
|
||||||
|
state: String
|
||||||
|
}
|
||||||
|
|
||||||
input VerifyEmailInput {
|
input VerifyEmailInput {
|
||||||
token: String!
|
token: String!
|
||||||
# state is used for authorization code grant flow
|
# state is used for authorization code grant flow
|
||||||
|
@ -506,8 +517,9 @@ input ResendOTPRequest {
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
signup(params: SignUpInput!): AuthResponse!
|
signup(params: SignUpInput!): AuthResponse!
|
||||||
mobile_basic_auth_signup(params: MobileBasicAuthSignUpUpInput): AuthResponse!
|
mobile_signup(params: MobileSignUpInput): AuthResponse!
|
||||||
login(params: LoginInput!): AuthResponse!
|
login(params: LoginInput!): AuthResponse!
|
||||||
|
mobile_login(params: MobileLoginInput!): AuthResponse!
|
||||||
magic_link_login(params: MagicLinkLoginInput!): Response!
|
magic_link_login(params: MagicLinkLoginInput!): Response!
|
||||||
logout: Response!
|
logout: Response!
|
||||||
update_profile(params: UpdateProfileInput!): Response!
|
update_profile(params: UpdateProfileInput!): Response!
|
||||||
|
|
|
@ -16,9 +16,9 @@ func (r *mutationResolver) Signup(ctx context.Context, params model.SignUpInput)
|
||||||
return resolvers.SignupResolver(ctx, params)
|
return resolvers.SignupResolver(ctx, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MobileBasicAuthSignup is the resolver for the mobile_basic_auth_signup field.
|
// MobileSignup is the resolver for the mobile_signup field.
|
||||||
func (r *mutationResolver) MobileBasicAuthSignup(ctx context.Context, params *model.MobileBasicAuthSignUpUpInput) (*model.AuthResponse, error) {
|
func (r *mutationResolver) MobileSignup(ctx context.Context, params *model.MobileSignUpInput) (*model.AuthResponse, error) {
|
||||||
return resolvers.MobileBasicAuthSignupResolver(ctx, params)
|
return resolvers.MobileSignupResolver(ctx, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login is the resolver for the login field.
|
// Login is the resolver for the login field.
|
||||||
|
@ -26,6 +26,11 @@ func (r *mutationResolver) Login(ctx context.Context, params model.LoginInput) (
|
||||||
return resolvers.LoginResolver(ctx, params)
|
return resolvers.LoginResolver(ctx, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MobileLogin is the resolver for the mobile_login field.
|
||||||
|
func (r *mutationResolver) MobileLogin(ctx context.Context, params model.MobileLoginInput) (*model.AuthResponse, error) {
|
||||||
|
return resolvers.MobileLoginResolver(ctx, params)
|
||||||
|
}
|
||||||
|
|
||||||
// MagicLinkLogin is the resolver for the magic_link_login field.
|
// MagicLinkLogin is the resolver for the magic_link_login field.
|
||||||
func (r *mutationResolver) MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error) {
|
func (r *mutationResolver) MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error) {
|
||||||
return resolvers.MagicLinkLoginResolver(ctx, params)
|
return resolvers.MagicLinkLoginResolver(ctx, params)
|
||||||
|
|
216
server/resolvers/mobile_login.go
Normal file
216
server/resolvers/mobile_login.go
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
package resolvers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/cookie"
|
||||||
|
"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/memorystore"
|
||||||
|
"github.com/authorizerdev/authorizer/server/refs"
|
||||||
|
"github.com/authorizerdev/authorizer/server/token"
|
||||||
|
"github.com/authorizerdev/authorizer/server/utils"
|
||||||
|
"github.com/authorizerdev/authorizer/server/validators"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MobileLoginResolver is a resolver for mobile login mutation
|
||||||
|
func MobileLoginResolver(ctx context.Context, params model.MobileLoginInput) (*model.AuthResponse, error) {
|
||||||
|
var res *model.AuthResponse
|
||||||
|
|
||||||
|
gc, err := utils.GinContextFromContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to get GinContext: ", err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
isBasiAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error getting mobile basic auth disabled: ", err)
|
||||||
|
isBasiAuthDisabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isBasiAuthDisabled {
|
||||||
|
log.Debug("Basic authentication is disabled.")
|
||||||
|
return res, fmt.Errorf(`phone number based basic authentication is disabled for this instance`)
|
||||||
|
}
|
||||||
|
|
||||||
|
log := log.WithFields(log.Fields{
|
||||||
|
"phone_number": params.PhoneNumber,
|
||||||
|
})
|
||||||
|
|
||||||
|
user, err := db.Provider.GetUserByPhoneNumber(ctx, params.PhoneNumber)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to get user by phone number: ", err)
|
||||||
|
return res, fmt.Errorf(`bad user credentials`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.RevokedTimestamp != nil {
|
||||||
|
log.Debug("User access is revoked")
|
||||||
|
return res, fmt.Errorf(`user access has been revoked`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(user.SignupMethods, constants.AuthRecipeMethodMobileBasicAuth) {
|
||||||
|
log.Debug("User signup method is not mobile basic auth")
|
||||||
|
return res, fmt.Errorf(`user has not signed up with phone number & password`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.PhoneNumberVerifiedAt == nil {
|
||||||
|
log.Debug("User phone number is not verified")
|
||||||
|
return res, fmt.Errorf(`phone number is not verified`)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bcrypt.CompareHashAndPassword([]byte(*user.Password), []byte(params.Password))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to compare password: ", err)
|
||||||
|
return res, fmt.Errorf(`bad user credentials`)
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultRolesString, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles)
|
||||||
|
roles := []string{}
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Error getting default roles: ", err)
|
||||||
|
defaultRolesString = ""
|
||||||
|
} else {
|
||||||
|
roles = strings.Split(defaultRolesString, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRoles := strings.Split(user.Roles, ",")
|
||||||
|
if len(params.Roles) > 0 {
|
||||||
|
if !validators.IsValidRoles(params.Roles, currentRoles) {
|
||||||
|
log.Debug("Invalid roles: ", params.Roles)
|
||||||
|
return res, fmt.Errorf(`invalid roles`)
|
||||||
|
}
|
||||||
|
|
||||||
|
roles = params.Roles
|
||||||
|
}
|
||||||
|
|
||||||
|
scope := []string{"openid", "email", "profile"}
|
||||||
|
if params.Scope != nil && len(scope) > 0 {
|
||||||
|
scope = params.Scope
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// TODO use sms authentication for MFA
|
||||||
|
isEmailServiceEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyIsEmailServiceEnabled)
|
||||||
|
if err != nil || !isEmailServiceEnabled {
|
||||||
|
log.Debug("Email service not enabled: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
isMFADisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMultiFactorAuthentication)
|
||||||
|
if err != nil || !isEmailServiceEnabled {
|
||||||
|
log.Debug("MFA service not enabled: ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If email service is not enabled continue the process in any way
|
||||||
|
if refs.BoolValue(user.IsMultiFactorAuthEnabled) && isEmailServiceEnabled && !isMFADisabled {
|
||||||
|
otp := utils.GenerateOTP()
|
||||||
|
otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{
|
||||||
|
Email: user.Email,
|
||||||
|
Otp: otp,
|
||||||
|
ExpiresAt: time.Now().Add(1 * time.Minute).Unix(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to add otp: ", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
// exec it as go routine so that we can reduce the api latency
|
||||||
|
go email.SendEmail([]string{params.PhoneNumber}, constants.VerificationTypeOTP, map[string]interface{}{
|
||||||
|
"user": user.ToMap(),
|
||||||
|
"organization": utils.GetOrganization(),
|
||||||
|
"otp": otpData.Otp,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to send otp email: ", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return &model.AuthResponse{
|
||||||
|
Message: "Please check the OTP in your inbox",
|
||||||
|
ShouldShowOtpScreen: refs.NewBoolRef(true),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
code := ""
|
||||||
|
codeChallenge := ""
|
||||||
|
nonce := ""
|
||||||
|
if params.State != nil {
|
||||||
|
// Get state from store
|
||||||
|
authorizeState, _ := memorystore.Provider.GetState(refs.StringValue(params.State))
|
||||||
|
if authorizeState != "" {
|
||||||
|
authorizeStateSplit := strings.Split(authorizeState, "@@")
|
||||||
|
if len(authorizeStateSplit) > 1 {
|
||||||
|
code = authorizeStateSplit[0]
|
||||||
|
codeChallenge = authorizeStateSplit[1]
|
||||||
|
} else {
|
||||||
|
nonce = authorizeState
|
||||||
|
}
|
||||||
|
go memorystore.Provider.RemoveState(refs.StringValue(params.State))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if nonce == "" {
|
||||||
|
nonce = uuid.New().String()
|
||||||
|
}
|
||||||
|
|
||||||
|
authToken, err := token.CreateAuthToken(gc, *user, roles, scope, constants.AuthRecipeMethodMobileBasicAuth, nonce, code)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("Failed to create auth token", err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO add to other login options as well
|
||||||
|
// Code challenge could be optional if PKCE flow is not used
|
||||||
|
if code != "" {
|
||||||
|
if err := memorystore.Provider.SetState(code, codeChallenge+"@@"+authToken.FingerPrintHash); err != nil {
|
||||||
|
log.Debug("SetState failed: ", err)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expiresIn := authToken.AccessToken.ExpiresAt - time.Now().Unix()
|
||||||
|
if expiresIn <= 0 {
|
||||||
|
expiresIn = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
res = &model.AuthResponse{
|
||||||
|
Message: `Logged in successfully`,
|
||||||
|
AccessToken: &authToken.AccessToken.Token,
|
||||||
|
IDToken: &authToken.IDToken.Token,
|
||||||
|
ExpiresIn: &expiresIn,
|
||||||
|
User: user.AsAPIUser(),
|
||||||
|
}
|
||||||
|
|
||||||
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
|
sessionStoreKey := constants.AuthRecipeMethodMobileBasicAuth + ":" + user.ID
|
||||||
|
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||||
|
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||||
|
|
||||||
|
if authToken.RefreshToken != nil {
|
||||||
|
res.RefreshToken = &authToken.RefreshToken.Token
|
||||||
|
memorystore.Provider.SetUserSession(sessionStoreKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
utils.RegisterEvent(ctx, constants.UserLoginWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, *user)
|
||||||
|
db.Provider.AddSession(ctx, models.Session{
|
||||||
|
UserID: user.ID,
|
||||||
|
UserAgent: utils.GetUserAgent(gc.Request),
|
||||||
|
IP: utils.GetIP(gc.Request),
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
|
@ -22,8 +22,8 @@ import (
|
||||||
"github.com/authorizerdev/authorizer/server/validators"
|
"github.com/authorizerdev/authorizer/server/validators"
|
||||||
)
|
)
|
||||||
|
|
||||||
// MobileBasicAuthSignupResolver is a resolver for mobile_basic_auth_signup mutation
|
// MobileSignupResolver is a resolver for mobile_basic_auth_signup mutation
|
||||||
func MobileBasicAuthSignupResolver(ctx context.Context, params *model.MobileBasicAuthSignUpUpInput) (*model.AuthResponse, error) {
|
func MobileSignupResolver(ctx context.Context, params *model.MobileSignUpInput) (*model.AuthResponse, error) {
|
||||||
var res *model.AuthResponse
|
var res *model.AuthResponse
|
||||||
|
|
||||||
gc, err := utils.GinContextFromContext(ctx)
|
gc, err := utils.GinContextFromContext(ctx)
|
||||||
|
@ -221,7 +221,7 @@ func MobileBasicAuthSignupResolver(ctx context.Context, params *model.MobileBasi
|
||||||
nonce = uuid.New().String()
|
nonce = uuid.New().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodBasicAuth, nonce, code)
|
authToken, err := token.CreateAuthToken(gc, user, roles, scope, constants.AuthRecipeMethodMobileBasicAuth, nonce, code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("Failed to create auth token: ", err)
|
log.Debug("Failed to create auth token: ", err)
|
||||||
return res, err
|
return res, err
|
||||||
|
@ -247,7 +247,7 @@ func MobileBasicAuthSignupResolver(ctx context.Context, params *model.MobileBasi
|
||||||
User: userToReturn,
|
User: userToReturn,
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionKey := constants.AuthRecipeMethodBasicAuth + ":" + user.ID
|
sessionKey := constants.AuthRecipeMethodMobileBasicAuth + ":" + user.ID
|
||||||
cookie.SetSession(gc, authToken.FingerPrintHash)
|
cookie.SetSession(gc, authToken.FingerPrintHash)
|
||||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash)
|
||||||
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token)
|
||||||
|
@ -258,7 +258,7 @@ func MobileBasicAuthSignupResolver(ctx context.Context, params *model.MobileBasi
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, constants.AuthRecipeMethodBasicAuth, user)
|
utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, constants.AuthRecipeMethodMobileBasicAuth, user)
|
||||||
db.Provider.AddSession(ctx, models.Session{
|
db.Provider.AddSession(ctx, models.Session{
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
UserAgent: utils.GetUserAgent(gc.Request),
|
UserAgent: utils.GetUserAgent(gc.Request),
|
|
@ -88,6 +88,11 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.PhoneNumber != nil && refs.StringValue(user.PhoneNumber) != refs.StringValue(params.PhoneNumber) {
|
if params.PhoneNumber != nil && refs.StringValue(user.PhoneNumber) != refs.StringValue(params.PhoneNumber) {
|
||||||
|
// verify if phone number is unique
|
||||||
|
if _, err := db.Provider.GetUserByPhoneNumber(ctx, strings.TrimSpace(refs.StringValue(params.PhoneNumber))); err == nil {
|
||||||
|
log.Debug("user with given phone number already exists")
|
||||||
|
return nil, errors.New("user with given phone number already exists")
|
||||||
|
}
|
||||||
user.PhoneNumber = params.PhoneNumber
|
user.PhoneNumber = params.PhoneNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,11 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.PhoneNumber != nil && refs.StringValue(user.PhoneNumber) != refs.StringValue(params.PhoneNumber) {
|
if params.PhoneNumber != nil && refs.StringValue(user.PhoneNumber) != refs.StringValue(params.PhoneNumber) {
|
||||||
|
// verify if phone number is unique
|
||||||
|
if _, err := db.Provider.GetUserByPhoneNumber(ctx, strings.TrimSpace(refs.StringValue(params.PhoneNumber))); err == nil {
|
||||||
|
log.Debug("user with given phone number already exists")
|
||||||
|
return nil, errors.New("user with given phone number already exists")
|
||||||
|
}
|
||||||
user.PhoneNumber = params.PhoneNumber
|
user.PhoneNumber = params.PhoneNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
58
server/test/mobile_login_test.go
Normal file
58
server/test/mobile_login_test.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/authorizerdev/authorizer/server/constants"
|
||||||
|
"github.com/authorizerdev/authorizer/server/graph/model"
|
||||||
|
"github.com/authorizerdev/authorizer/server/refs"
|
||||||
|
"github.com/authorizerdev/authorizer/server/resolvers"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mobileLoginTests(t *testing.T, s TestSetup) {
|
||||||
|
t.Helper()
|
||||||
|
t.Run(`should login via mobile`, func(t *testing.T) {
|
||||||
|
_, ctx := createContext(s)
|
||||||
|
email := "mobile_login." + s.TestInfo.Email
|
||||||
|
phoneNumber := "2234567890"
|
||||||
|
signUpRes, err := resolvers.MobileSignupResolver(ctx, &model.MobileSignUpInput{
|
||||||
|
Email: refs.NewStringRef(email),
|
||||||
|
PhoneNumber: phoneNumber,
|
||||||
|
Password: s.TestInfo.Password,
|
||||||
|
ConfirmPassword: s.TestInfo.Password,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, signUpRes)
|
||||||
|
assert.Equal(t, email, signUpRes.User.Email)
|
||||||
|
assert.Equal(t, phoneNumber, refs.StringValue(signUpRes.User.PhoneNumber))
|
||||||
|
assert.True(t, strings.Contains(signUpRes.User.SignupMethods, constants.AuthRecipeMethodMobileBasicAuth))
|
||||||
|
assert.Len(t, strings.Split(signUpRes.User.SignupMethods, ","), 1)
|
||||||
|
|
||||||
|
res, err := resolvers.MobileLoginResolver(ctx, model.MobileLoginInput{
|
||||||
|
PhoneNumber: phoneNumber,
|
||||||
|
Password: "random_test",
|
||||||
|
})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, res)
|
||||||
|
|
||||||
|
// Should fail for email login
|
||||||
|
res, err = resolvers.LoginResolver(ctx, model.LoginInput{
|
||||||
|
Email: email,
|
||||||
|
Password: s.TestInfo.Password,
|
||||||
|
})
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Nil(t, res)
|
||||||
|
|
||||||
|
res, err = resolvers.MobileLoginResolver(ctx, model.MobileLoginInput{
|
||||||
|
PhoneNumber: phoneNumber,
|
||||||
|
Password: s.TestInfo.Password,
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotEmpty(t, res.AccessToken)
|
||||||
|
assert.NotEmpty(t, res.IDToken)
|
||||||
|
|
||||||
|
cleanData(email)
|
||||||
|
})
|
||||||
|
}
|
|
@ -11,12 +11,12 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func mobileBasicAuthSingupTest(t *testing.T, s TestSetup) {
|
func mobileSingupTest(t *testing.T, s TestSetup) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
t.Run(`should complete the signup with mobile and check duplicates`, func(t *testing.T) {
|
t.Run(`should complete the signup with mobile and check duplicates`, func(t *testing.T) {
|
||||||
_, ctx := createContext(s)
|
_, ctx := createContext(s)
|
||||||
email := "mobile_basic_auth_signup." + s.TestInfo.Email
|
email := "mobile_basic_auth_signup." + s.TestInfo.Email
|
||||||
res, err := resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{
|
res, err := resolvers.MobileSignupResolver(ctx, &model.MobileSignUpInput{
|
||||||
Email: refs.NewStringRef(email),
|
Email: refs.NewStringRef(email),
|
||||||
Password: s.TestInfo.Password,
|
Password: s.TestInfo.Password,
|
||||||
ConfirmPassword: s.TestInfo.Password + "s",
|
ConfirmPassword: s.TestInfo.Password + "s",
|
||||||
|
@ -24,7 +24,7 @@ func mobileBasicAuthSingupTest(t *testing.T, s TestSetup) {
|
||||||
assert.NotNil(t, err, "invalid password")
|
assert.NotNil(t, err, "invalid password")
|
||||||
assert.Nil(t, res)
|
assert.Nil(t, res)
|
||||||
|
|
||||||
res, err = resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{
|
res, err = resolvers.MobileSignupResolver(ctx, &model.MobileSignUpInput{
|
||||||
Email: refs.NewStringRef(email),
|
Email: refs.NewStringRef(email),
|
||||||
Password: "test",
|
Password: "test",
|
||||||
ConfirmPassword: "test",
|
ConfirmPassword: "test",
|
||||||
|
@ -32,7 +32,7 @@ func mobileBasicAuthSingupTest(t *testing.T, s TestSetup) {
|
||||||
assert.NotNil(t, err, "invalid password")
|
assert.NotNil(t, err, "invalid password")
|
||||||
|
|
||||||
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableSignUp, true)
|
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableSignUp, true)
|
||||||
res, err = resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{
|
res, err = resolvers.MobileSignupResolver(ctx, &model.MobileSignUpInput{
|
||||||
Email: refs.NewStringRef(email),
|
Email: refs.NewStringRef(email),
|
||||||
Password: s.TestInfo.Password,
|
Password: s.TestInfo.Password,
|
||||||
ConfirmPassword: s.TestInfo.Password,
|
ConfirmPassword: s.TestInfo.Password,
|
||||||
|
@ -41,7 +41,7 @@ func mobileBasicAuthSingupTest(t *testing.T, s TestSetup) {
|
||||||
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableSignUp, false)
|
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableSignUp, false)
|
||||||
|
|
||||||
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication, true)
|
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication, true)
|
||||||
res, err = resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{
|
res, err = resolvers.MobileSignupResolver(ctx, &model.MobileSignUpInput{
|
||||||
Email: refs.NewStringRef(email),
|
Email: refs.NewStringRef(email),
|
||||||
Password: s.TestInfo.Password,
|
Password: s.TestInfo.Password,
|
||||||
ConfirmPassword: s.TestInfo.Password,
|
ConfirmPassword: s.TestInfo.Password,
|
||||||
|
@ -49,21 +49,21 @@ func mobileBasicAuthSingupTest(t *testing.T, s TestSetup) {
|
||||||
assert.NotNil(t, err, "singup disabled")
|
assert.NotNil(t, err, "singup disabled")
|
||||||
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication, false)
|
memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication, false)
|
||||||
|
|
||||||
res, err = resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{
|
res, err = resolvers.MobileSignupResolver(ctx, &model.MobileSignUpInput{
|
||||||
PhoneNumber: " ",
|
PhoneNumber: " ",
|
||||||
Password: s.TestInfo.Password,
|
Password: s.TestInfo.Password,
|
||||||
ConfirmPassword: s.TestInfo.Password,
|
ConfirmPassword: s.TestInfo.Password,
|
||||||
})
|
})
|
||||||
assert.NotNil(t, err, "invalid mobile")
|
assert.NotNil(t, err, "invalid mobile")
|
||||||
|
|
||||||
res, err = resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{
|
res, err = resolvers.MobileSignupResolver(ctx, &model.MobileSignUpInput{
|
||||||
PhoneNumber: "test",
|
PhoneNumber: "test",
|
||||||
Password: s.TestInfo.Password,
|
Password: s.TestInfo.Password,
|
||||||
ConfirmPassword: s.TestInfo.Password,
|
ConfirmPassword: s.TestInfo.Password,
|
||||||
})
|
})
|
||||||
assert.NotNil(t, err, "invalid mobile")
|
assert.NotNil(t, err, "invalid mobile")
|
||||||
|
|
||||||
res, err = resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{
|
res, err = resolvers.MobileSignupResolver(ctx, &model.MobileSignUpInput{
|
||||||
PhoneNumber: "1234567890",
|
PhoneNumber: "1234567890",
|
||||||
Password: s.TestInfo.Password,
|
Password: s.TestInfo.Password,
|
||||||
ConfirmPassword: s.TestInfo.Password,
|
ConfirmPassword: s.TestInfo.Password,
|
||||||
|
@ -72,11 +72,14 @@ func mobileBasicAuthSingupTest(t *testing.T, s TestSetup) {
|
||||||
assert.NotEmpty(t, res.AccessToken)
|
assert.NotEmpty(t, res.AccessToken)
|
||||||
assert.Equal(t, "1234567890@authorizer.dev", res.User.Email)
|
assert.Equal(t, "1234567890@authorizer.dev", res.User.Email)
|
||||||
|
|
||||||
res, err = resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{
|
res, err = resolvers.MobileSignupResolver(ctx, &model.MobileSignUpInput{
|
||||||
PhoneNumber: "1234567890",
|
PhoneNumber: "1234567890",
|
||||||
Password: s.TestInfo.Password,
|
Password: s.TestInfo.Password,
|
||||||
ConfirmPassword: s.TestInfo.Password,
|
ConfirmPassword: s.TestInfo.Password,
|
||||||
})
|
})
|
||||||
assert.Error(t, err, "user exists")
|
assert.Error(t, err, "user exists")
|
||||||
|
|
||||||
|
cleanData(email)
|
||||||
|
cleanData("1234567890@authorizer.dev")
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -111,7 +111,8 @@ func TestResolvers(t *testing.T) {
|
||||||
// user resolvers tests
|
// user resolvers tests
|
||||||
loginTests(t, s)
|
loginTests(t, s)
|
||||||
signupTests(t, s)
|
signupTests(t, s)
|
||||||
mobileBasicAuthSingupTest(t, s)
|
mobileSingupTest(t, s)
|
||||||
|
mobileLoginTests(t, s)
|
||||||
forgotPasswordTest(t, s)
|
forgotPasswordTest(t, s)
|
||||||
resendVerifyEmailTests(t, s)
|
resendVerifyEmailTests(t, s)
|
||||||
resetPasswordTest(t, s)
|
resetPasswordTest(t, s)
|
||||||
|
|
|
@ -31,7 +31,7 @@ func testEndpointTest(t *testing.T, s TestSetup) {
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotNil(t, res)
|
assert.NotNil(t, res)
|
||||||
assert.GreaterOrEqual(t, int64(201), *res.HTTPStatus)
|
assert.GreaterOrEqual(t, *res.HTTPStatus, int64(200))
|
||||||
assert.NotEmpty(t, res.Response)
|
assert.NotEmpty(t, res.Response)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user