diff --git a/app/package-lock.json b/app/package-lock.json index f8d8f63..fec1e8d 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@authorizerdev/authorizer-react": "^1.1.15", + "@authorizerdev/authorizer-react": "^1.1.19", "@types/react": "^17.0.15", "@types/react-dom": "^17.0.9", "esbuild": "^0.12.17", @@ -27,9 +27,9 @@ } }, "node_modules/@authorizerdev/authorizer-js": { - "version": "1.2.17", - "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.2.17.tgz", - "integrity": "sha512-aF/lu9wZR7TBRaRMAes/hy1q8cZzz5Zo60QLU9Iu09sqnhliHJCp5wSkjsVH+V4ER9i7bmJ2HNABTmOdluxj3A==", + "version": "1.2.18", + "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.2.18.tgz", + "integrity": "sha512-9j5U/4lqaaEcG78Zli+TtLJ0migSKhFwnXXunulAGTZOzQSTCJ/CSSPip5wWNa/Mkr6gdEMwk1HYfhIdk2A9Mg==", "dependencies": { "cross-fetch": "^3.1.5" }, @@ -41,11 +41,12 @@ } }, "node_modules/@authorizerdev/authorizer-react": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.15.tgz", - "integrity": "sha512-Y71qC4GUAHL0QCNj5mVv0Jwv1cIg4Y0yXRiOeYV21C1NMleyLRXgw4qzJ/Vk8rmXsxqSHmr8SGrwOLcSKA2oMA==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.19.tgz", + "integrity": "sha512-hbId4mtzeWke1uUFAZrPwT45UmxgTp0QHAAsQvl/I0+mgoCJlJdAnUBCiJD6d5lVHJk41nx/ePYG4rw2Aj6HTw==", "dependencies": { - "@authorizerdev/authorizer-js": "^1.2.17" + "@authorizerdev/authorizer-js": "^1.2.18", + "validator": "^13.11.0" }, "engines": { "node": ">=10" @@ -847,6 +848,14 @@ "node": ">=4.2.0" } }, + "node_modules/validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/value-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", diff --git a/app/package.json b/app/package.json index 5221603..95fb694 100644 --- a/app/package.json +++ b/app/package.json @@ -12,7 +12,7 @@ "author": "Lakhan Samani", "license": "ISC", "dependencies": { - "@authorizerdev/authorizer-react": "^1.1.15", + "@authorizerdev/authorizer-react": "^1.1.19", "@types/react": "^17.0.15", "@types/react-dom": "^17.0.9", "esbuild": "^0.12.17", diff --git a/app/src/pages/login.tsx b/app/src/pages/login.tsx index 3e8e4a1..d53d36c 100644 --- a/app/src/pages/login.tsx +++ b/app/src/pages/login.tsx @@ -32,6 +32,7 @@ const FooterContent = styled.div` export default function Login({ urlProps }: { urlProps: Record }) { const { config } = useAuthorizer(); const [view, setView] = useState(VIEW_TYPES.LOGIN); + const isBasicAuth = config.is_basic_authentication_enabled; return ( {view === VIEW_TYPES.LOGIN && ( @@ -39,22 +40,26 @@ export default function Login({ urlProps }: { urlProps: Record }) {

Login


- {config.is_basic_authentication_enabled && + {(config.is_basic_authentication_enabled || + config.is_mobile_basic_authentication_enabled) && !config.is_magic_link_login_enabled && ( )} {config.is_magic_link_login_enabled && ( )} -
- setView(VIEW_TYPES.FORGOT_PASSWORD)} - style={{ marginBottom: 10 }} - > - Forgot Password? - -
+ {(config.is_basic_authentication_enabled || + config.is_mobile_basic_authentication_enabled) && ( +
+ setView(VIEW_TYPES.FORGOT_PASSWORD)} + style={{ marginBottom: 10 }} + > + Forgot Password? + +
+ )}
)} {view === VIEW_TYPES.FORGOT_PASSWORD && ( @@ -81,7 +86,7 @@ export default function Login({ urlProps }: { urlProps: Record }) { !config.is_magic_link_login_enabled && config.is_sign_up_enabled && ( - Don't have an account? Sign Up + Don't have an account?   Sign Up )} diff --git a/app/yarn.lock b/app/yarn.lock index 5e2c385..fcb882e 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -2,19 +2,20 @@ # yarn lockfile v1 -"@authorizerdev/authorizer-js@^1.2.17": - version "1.2.17" - resolved "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.2.17.tgz" - integrity sha512-aF/lu9wZR7TBRaRMAes/hy1q8cZzz5Zo60QLU9Iu09sqnhliHJCp5wSkjsVH+V4ER9i7bmJ2HNABTmOdluxj3A== +"@authorizerdev/authorizer-js@^1.2.18": + version "1.2.18" + resolved "https://registry.npmjs.org/@authorizerdev/authorizer-js/-/authorizer-js-1.2.18.tgz" + integrity sha512-9j5U/4lqaaEcG78Zli+TtLJ0migSKhFwnXXunulAGTZOzQSTCJ/CSSPip5wWNa/Mkr6gdEMwk1HYfhIdk2A9Mg== dependencies: cross-fetch "^3.1.5" -"@authorizerdev/authorizer-react@^1.1.15": - version "1.1.15" - resolved "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.15.tgz" - integrity sha512-Y71qC4GUAHL0QCNj5mVv0Jwv1cIg4Y0yXRiOeYV21C1NMleyLRXgw4qzJ/Vk8rmXsxqSHmr8SGrwOLcSKA2oMA== +"@authorizerdev/authorizer-react@^1.1.19": + version "1.1.19" + resolved "https://registry.npmjs.org/@authorizerdev/authorizer-react/-/authorizer-react-1.1.19.tgz" + integrity sha512-hbId4mtzeWke1uUFAZrPwT45UmxgTp0QHAAsQvl/I0+mgoCJlJdAnUBCiJD6d5lVHJk41nx/ePYG4rw2Aj6HTw== dependencies: - "@authorizerdev/authorizer-js" "^1.2.17" + "@authorizerdev/authorizer-js" "^1.2.18" + validator "^13.11.0" "@babel/code-frame@^7.22.13": version "7.22.13" @@ -594,6 +595,11 @@ typescript@^4.3.5: resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== +validator@^13.11.0: + version "13.11.0" + resolved "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz" + integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ== + value-equal@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz" diff --git a/dashboard/src/components/EnvComponents/Features.tsx b/dashboard/src/components/EnvComponents/Features.tsx index 77553bf..4c72693 100644 --- a/dashboard/src/components/EnvComponents/Features.tsx +++ b/dashboard/src/components/EnvComponents/Features.tsx @@ -108,11 +108,10 @@ const Features = ({ variables, setVariables }: any) => { /> - {/** TODO enable after final release */} - {/* {!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && ( + {!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && ( - TOTP: + Time Based OTP (TOTP): Note: to enable totp mfa @@ -125,7 +124,7 @@ const Features = ({ variables, setVariables }: any) => { /> - )} */} + )} {!variables.DISABLE_MULTI_FACTOR_AUTHENTICATION && ( diff --git a/server/graph/generated/generated.go b/server/graph/generated/generated.go index df7829a..b0792bc 100644 --- a/server/graph/generated/generated.go +++ b/server/graph/generated/generated.go @@ -162,22 +162,24 @@ type ComplexityRoot struct { } Meta struct { - ClientID func(childComplexity int) int - IsAppleLoginEnabled func(childComplexity int) int - IsBasicAuthenticationEnabled func(childComplexity int) int - IsEmailVerificationEnabled func(childComplexity int) int - IsFacebookLoginEnabled func(childComplexity int) int - IsGithubLoginEnabled func(childComplexity int) int - IsGoogleLoginEnabled func(childComplexity int) int - IsLinkedinLoginEnabled func(childComplexity int) int - IsMagicLinkLoginEnabled func(childComplexity int) int - IsMicrosoftLoginEnabled func(childComplexity int) int - IsMultiFactorAuthEnabled func(childComplexity int) int - IsSignUpEnabled func(childComplexity int) int - IsStrongPasswordEnabled func(childComplexity int) int - IsTwitchLoginEnabled func(childComplexity int) int - IsTwitterLoginEnabled func(childComplexity int) int - Version func(childComplexity int) int + ClientID func(childComplexity int) int + IsAppleLoginEnabled func(childComplexity int) int + IsBasicAuthenticationEnabled func(childComplexity int) int + IsEmailVerificationEnabled func(childComplexity int) int + IsFacebookLoginEnabled func(childComplexity int) int + IsGithubLoginEnabled func(childComplexity int) int + IsGoogleLoginEnabled func(childComplexity int) int + IsLinkedinLoginEnabled func(childComplexity int) int + IsMagicLinkLoginEnabled func(childComplexity int) int + IsMicrosoftLoginEnabled func(childComplexity int) int + IsMobileBasicAuthenticationEnabled func(childComplexity int) int + IsMultiFactorAuthEnabled func(childComplexity int) int + IsPhoneVerificationEnabled func(childComplexity int) int + IsSignUpEnabled func(childComplexity int) int + IsStrongPasswordEnabled func(childComplexity int) int + IsTwitchLoginEnabled func(childComplexity int) int + IsTwitterLoginEnabled func(childComplexity int) int + Version func(childComplexity int) int } Mutation struct { @@ -1142,6 +1144,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Meta.IsMicrosoftLoginEnabled(childComplexity), true + case "Meta.is_mobile_basic_authentication_enabled": + if e.complexity.Meta.IsMobileBasicAuthenticationEnabled == nil { + break + } + + return e.complexity.Meta.IsMobileBasicAuthenticationEnabled(childComplexity), true + case "Meta.is_multi_factor_auth_enabled": if e.complexity.Meta.IsMultiFactorAuthEnabled == nil { break @@ -1149,6 +1158,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Meta.IsMultiFactorAuthEnabled(childComplexity), true + case "Meta.is_phone_verification_enabled": + if e.complexity.Meta.IsPhoneVerificationEnabled == nil { + break + } + + return e.complexity.Meta.IsPhoneVerificationEnabled(childComplexity), true + case "Meta.is_sign_up_enabled": if e.complexity.Meta.IsSignUpEnabled == nil { break @@ -2355,6 +2371,8 @@ type Meta { is_sign_up_enabled: Boolean! is_strong_password_enabled: Boolean! is_multi_factor_auth_enabled: Boolean! + is_mobile_basic_authentication_enabled: Boolean! + is_phone_verification_enabled: Boolean! } type User { @@ -8373,6 +8391,94 @@ func (ec *executionContext) fieldContext_Meta_is_multi_factor_auth_enabled(ctx c return fc, nil } +func (ec *executionContext) _Meta_is_mobile_basic_authentication_enabled(ctx context.Context, field graphql.CollectedField, obj *model.Meta) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Meta_is_mobile_basic_authentication_enabled(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.IsMobileBasicAuthenticationEnabled, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Meta_is_mobile_basic_authentication_enabled(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Meta", + 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) _Meta_is_phone_verification_enabled(ctx context.Context, field graphql.CollectedField, obj *model.Meta) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Meta_is_phone_verification_enabled(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.IsPhoneVerificationEnabled, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(bool) + fc.Result = res + return ec.marshalNBoolean2bool(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_Meta_is_phone_verification_enabled(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "Meta", + 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) _Mutation_signup(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Mutation_signup(ctx, field) if err != nil { @@ -10653,6 +10759,10 @@ func (ec *executionContext) fieldContext_Query_meta(ctx context.Context, field g return ec.fieldContext_Meta_is_strong_password_enabled(ctx, field) case "is_multi_factor_auth_enabled": return ec.fieldContext_Meta_is_multi_factor_auth_enabled(ctx, field) + case "is_mobile_basic_authentication_enabled": + return ec.fieldContext_Meta_is_mobile_basic_authentication_enabled(ctx, field) + case "is_phone_verification_enabled": + return ec.fieldContext_Meta_is_phone_verification_enabled(ctx, field) } return nil, fmt.Errorf("no field named %q was found under type Meta", field.Name) }, @@ -19594,6 +19704,16 @@ func (ec *executionContext) _Meta(ctx context.Context, sel ast.SelectionSet, obj if out.Values[i] == graphql.Null { out.Invalids++ } + case "is_mobile_basic_authentication_enabled": + out.Values[i] = ec._Meta_is_mobile_basic_authentication_enabled(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "is_phone_verification_enabled": + out.Values[i] = ec._Meta_is_phone_verification_enabled(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } default: panic("unknown field " + strconv.Quote(field.Name)) } diff --git a/server/graph/model/models_gen.go b/server/graph/model/models_gen.go index 06a93ee..45f5889 100644 --- a/server/graph/model/models_gen.go +++ b/server/graph/model/models_gen.go @@ -191,22 +191,24 @@ type MagicLinkLoginInput struct { } type Meta struct { - Version string `json:"version"` - ClientID string `json:"client_id"` - IsGoogleLoginEnabled bool `json:"is_google_login_enabled"` - IsFacebookLoginEnabled bool `json:"is_facebook_login_enabled"` - IsGithubLoginEnabled bool `json:"is_github_login_enabled"` - IsLinkedinLoginEnabled bool `json:"is_linkedin_login_enabled"` - IsAppleLoginEnabled bool `json:"is_apple_login_enabled"` - IsTwitterLoginEnabled bool `json:"is_twitter_login_enabled"` - IsMicrosoftLoginEnabled bool `json:"is_microsoft_login_enabled"` - IsTwitchLoginEnabled bool `json:"is_twitch_login_enabled"` - IsEmailVerificationEnabled bool `json:"is_email_verification_enabled"` - IsBasicAuthenticationEnabled bool `json:"is_basic_authentication_enabled"` - IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"` - IsSignUpEnabled bool `json:"is_sign_up_enabled"` - IsStrongPasswordEnabled bool `json:"is_strong_password_enabled"` - IsMultiFactorAuthEnabled bool `json:"is_multi_factor_auth_enabled"` + Version string `json:"version"` + ClientID string `json:"client_id"` + IsGoogleLoginEnabled bool `json:"is_google_login_enabled"` + IsFacebookLoginEnabled bool `json:"is_facebook_login_enabled"` + IsGithubLoginEnabled bool `json:"is_github_login_enabled"` + IsLinkedinLoginEnabled bool `json:"is_linkedin_login_enabled"` + IsAppleLoginEnabled bool `json:"is_apple_login_enabled"` + IsTwitterLoginEnabled bool `json:"is_twitter_login_enabled"` + IsMicrosoftLoginEnabled bool `json:"is_microsoft_login_enabled"` + IsTwitchLoginEnabled bool `json:"is_twitch_login_enabled"` + IsEmailVerificationEnabled bool `json:"is_email_verification_enabled"` + IsBasicAuthenticationEnabled bool `json:"is_basic_authentication_enabled"` + IsMagicLinkLoginEnabled bool `json:"is_magic_link_login_enabled"` + IsSignUpEnabled bool `json:"is_sign_up_enabled"` + IsStrongPasswordEnabled bool `json:"is_strong_password_enabled"` + IsMultiFactorAuthEnabled bool `json:"is_multi_factor_auth_enabled"` + IsMobileBasicAuthenticationEnabled bool `json:"is_mobile_basic_authentication_enabled"` + IsPhoneVerificationEnabled bool `json:"is_phone_verification_enabled"` } type MobileLoginInput struct { diff --git a/server/graph/schema.graphqls b/server/graph/schema.graphqls index 35e6459..07d3678 100644 --- a/server/graph/schema.graphqls +++ b/server/graph/schema.graphqls @@ -29,6 +29,8 @@ type Meta { is_sign_up_enabled: Boolean! is_strong_password_enabled: Boolean! is_multi_factor_auth_enabled: Boolean! + is_mobile_basic_authentication_enabled: Boolean! + is_phone_verification_enabled: Boolean! } type User { diff --git a/server/handlers/verify_email.go b/server/handlers/verify_email.go index 47f61d3..34f9d9f 100644 --- a/server/handlers/verify_email.go +++ b/server/handlers/verify_email.go @@ -74,7 +74,13 @@ func VerifyEmailHandler() gin.HandlerFunc { now := time.Now().Unix() user.EmailVerifiedAt = &now isSignUp = true - db.Provider.UpdateUser(c, user) + user, err = db.Provider.UpdateUser(c, user) + if err != nil { + log.Debug("Error updating user: ", err) + errorRes["error"] = err.Error() + utils.HandleRedirectORJsonResponse(c, http.StatusBadRequest, errorRes, generateRedirectURL(redirectURL, errorRes)) + return + } } // delete from verification table db.Provider.DeleteVerificationRequest(c, verificationRequest) diff --git a/server/resolvers/login.go b/server/resolvers/login.go index 708e3b4..34edbca 100644 --- a/server/resolvers/login.go +++ b/server/resolvers/login.go @@ -78,7 +78,7 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes } if err != nil { log.Debug("Failed to get user: ", err) - return res, fmt.Errorf(`bad user credentials`) + return res, fmt.Errorf(`user not found`) } if user.RevokedTimestamp != nil { log.Debug("User access is revoked") diff --git a/server/resolvers/meta.go b/server/resolvers/meta.go index 9290a41..cbab1e0 100644 --- a/server/resolvers/meta.go +++ b/server/resolvers/meta.go @@ -106,6 +106,16 @@ func MetaResolver(ctx context.Context) (*model.Meta, error) { log.Debug("Failed to get Disable Basic Authentication from environment variable", err) isBasicAuthDisabled = true } + isMobileBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication) + if err != nil { + log.Debug("Failed to get Disable Basic Authentication from environment variable", err) + isMobileBasicAuthDisabled = true + } + isMobileVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisablePhoneVerification) + if err != nil { + log.Debug("Failed to get Disable Basic Authentication from environment variable", err) + isMobileVerificationDisabled = true + } isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) if err != nil { @@ -138,21 +148,23 @@ func MetaResolver(ctx context.Context) (*model.Meta, error) { } metaInfo := model.Meta{ - Version: constants.VERSION, - ClientID: clientID, - IsGoogleLoginEnabled: googleClientID != "" && googleClientSecret != "", - IsGithubLoginEnabled: githubClientID != "" && githubClientSecret != "", - IsFacebookLoginEnabled: facebookClientID != "" && facebookClientSecret != "", - IsLinkedinLoginEnabled: linkedClientID != "" && linkedInClientSecret != "", - IsAppleLoginEnabled: appleClientID != "" && appleClientSecret != "", - IsTwitterLoginEnabled: twitterClientID != "" && twitterClientSecret != "", - IsMicrosoftLoginEnabled: microsoftClientID != "" && microsoftClientSecret != "", - IsBasicAuthenticationEnabled: !isBasicAuthDisabled, - IsEmailVerificationEnabled: !isEmailVerificationDisabled, - IsMagicLinkLoginEnabled: !isMagicLinkLoginDisabled, - IsSignUpEnabled: !isSignUpDisabled, - IsStrongPasswordEnabled: !isStrongPasswordDisabled, - IsMultiFactorAuthEnabled: !isMultiFactorAuthenticationEnabled, + Version: constants.VERSION, + ClientID: clientID, + IsGoogleLoginEnabled: googleClientID != "" && googleClientSecret != "", + IsGithubLoginEnabled: githubClientID != "" && githubClientSecret != "", + IsFacebookLoginEnabled: facebookClientID != "" && facebookClientSecret != "", + IsLinkedinLoginEnabled: linkedClientID != "" && linkedInClientSecret != "", + IsAppleLoginEnabled: appleClientID != "" && appleClientSecret != "", + IsTwitterLoginEnabled: twitterClientID != "" && twitterClientSecret != "", + IsMicrosoftLoginEnabled: microsoftClientID != "" && microsoftClientSecret != "", + IsBasicAuthenticationEnabled: !isBasicAuthDisabled, + IsEmailVerificationEnabled: !isEmailVerificationDisabled, + IsMagicLinkLoginEnabled: !isMagicLinkLoginDisabled, + IsSignUpEnabled: !isSignUpDisabled, + IsStrongPasswordEnabled: !isStrongPasswordDisabled, + IsMultiFactorAuthEnabled: !isMultiFactorAuthenticationEnabled, + IsMobileBasicAuthenticationEnabled: !isMobileBasicAuthDisabled, + IsPhoneVerificationEnabled: !isMobileVerificationDisabled, } return &metaInfo, nil } diff --git a/server/resolvers/signup.go b/server/resolvers/signup.go index 8eb3368..f947f2c 100644 --- a/server/resolvers/signup.go +++ b/server/resolvers/signup.go @@ -73,7 +73,7 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR } isEmailSignup := email != "" isMobileSignup := phoneNumber != "" - if isBasicAuthDisabled { + if isBasicAuthDisabled && isEmailSignup { log.Debug("Basic authentication is disabled") return res, fmt.Errorf(`basic authentication is disabled for this instance`) } @@ -222,12 +222,12 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR log.Debug("Error getting email verification disabled: ", err) isEmailVerificationDisabled = true } - if isEmailVerificationDisabled { + if isEmailVerificationDisabled && isEmailSignup { now := time.Now().Unix() user.EmailVerifiedAt = &now } disablePhoneVerification, _ := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisablePhoneVerification) - if disablePhoneVerification { + if disablePhoneVerification && isMobileSignup { now := time.Now().Unix() user.PhoneNumberVerifiedAt = &now } diff --git a/server/resolvers/verify_otp.go b/server/resolvers/verify_otp.go index e056dee..16a10c7 100644 --- a/server/resolvers/verify_otp.go +++ b/server/resolvers/verify_otp.go @@ -36,24 +36,29 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod return res, fmt.Errorf(`invalid session: %s`, err.Error()) } - if refs.StringValue(params.Email) == "" && refs.StringValue(params.PhoneNumber) == "" { + email := strings.TrimSpace(refs.StringValue(params.Email)) + phoneNumber := strings.TrimSpace(refs.StringValue(params.PhoneNumber)) + if email == "" && phoneNumber == "" { log.Debug("Email or phone number is required") - return res, fmt.Errorf(`email or phone_number is required`) - } - currentField := models.FieldNameEmail - if refs.StringValue(params.Email) == "" { - currentField = models.FieldNamePhoneNumber + return res, fmt.Errorf(`email or phone number is required`) } + isEmailVerification := email != "" + isMobileVerification := phoneNumber != "" // Get user by email or phone number var user *models.User - if currentField == models.FieldNameEmail { + if isEmailVerification { user, err = db.Provider.GetUserByEmail(ctx, refs.StringValue(params.Email)) + if err != nil { + log.Debug("Failed to get user by email: ", err) + } } else { user, err = db.Provider.GetUserByPhoneNumber(ctx, refs.StringValue(params.PhoneNumber)) + if err != nil { + log.Debug("Failed to get user by phone number: ", err) + } } if user == nil || err != nil { - log.Debug("Failed to get user by email or phone number: ", err) - return res, err + return res, fmt.Errorf(`user not found`) } // Verify OTP based on TOPT or OTP if refs.BoolValue(params.IsTotp) { @@ -78,14 +83,19 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod } } else { var otp *models.OTP - if currentField == models.FieldNameEmail { + if isEmailVerification { otp, err = db.Provider.GetOTPByEmail(ctx, refs.StringValue(params.Email)) + if err != nil { + log.Debug(`Failed to get otp request for email: `, err.Error()) + } } else { otp, err = db.Provider.GetOTPByPhoneNumber(ctx, refs.StringValue(params.PhoneNumber)) + if err != nil { + log.Debug(`Failed to get otp request for phone number: `, err.Error()) + } } if otp == nil && err != nil { - log.Debugf("Failed to get otp request for %s: %s", currentField, err.Error()) - return res, fmt.Errorf(`invalid %s: %s`, currentField, err.Error()) + return res, fmt.Errorf(`OTP not found`) } if params.Otp != otp.Otp { log.Debug("Failed to verify otp request: Incorrect value") @@ -104,10 +114,26 @@ func VerifyOtpResolver(ctx context.Context, params model.VerifyOTPRequest) (*mod return res, fmt.Errorf(`invalid session: %s`, err.Error()) } - isSignUp := user.EmailVerifiedAt == nil && user.PhoneNumberVerifiedAt == nil - // TODO - Add Login method in DB when we introduce OTP for social media login + isSignUp := false + if user.EmailVerifiedAt == nil && isEmailVerification { + isSignUp = true + now := time.Now().Unix() + user.EmailVerifiedAt = &now + } + if user.PhoneNumberVerifiedAt == nil && isMobileVerification { + isSignUp = true + now := time.Now().Unix() + user.PhoneNumberVerifiedAt = &now + } + if isSignUp { + user, err = db.Provider.UpdateUser(ctx, user) + if err != nil { + log.Debug("Failed to update user: ", err) + return res, err + } + } loginMethod := constants.AuthRecipeMethodBasicAuth - if currentField == models.FieldNamePhoneNumber { + if isMobileVerification { loginMethod = constants.AuthRecipeMethodMobileOTP } roles := strings.Split(user.Roles, ",") diff --git a/server/test/mobile_signup_test.go b/server/test/mobile_signup_test.go index 949895b..7bfba1c 100644 --- a/server/test/mobile_signup_test.go +++ b/server/test/mobile_signup_test.go @@ -98,12 +98,17 @@ func mobileSingupTest(t *testing.T, s TestSetup) { }) assert.Nil(t, err) assert.NotEmpty(t, otpRes.Message) + // Check if phone number is verified + user, err = db.Provider.GetUserByPhoneNumber(ctx, phoneNumber) + assert.NoError(t, err) + assert.NotNil(t, user) + assert.NotNil(t, user.PhoneNumberVerifiedAt) res, err = resolvers.SignupResolver(ctx, model.SignUpInput{ PhoneNumber: refs.NewStringRef(phoneNumber), Password: s.TestInfo.Password, ConfirmPassword: s.TestInfo.Password, }) - assert.Error(t, err) + assert.Error(t, err, "should throw duplicate error") assert.Nil(t, res) cleanData("1234567890@authorizer.dev") }) diff --git a/server/test/verify_email_test.go b/server/test/verify_email_test.go index 491f349..19416c1 100644 --- a/server/test/verify_email_test.go +++ b/server/test/verify_email_test.go @@ -35,7 +35,11 @@ func verifyEmailTest(t *testing.T, s TestSetup) { }) assert.Nil(t, err) assert.NotEqual(t, verifyRes.AccessToken, "", "access token should not be empty") - + // Check if phone number is verified + user1, err := db.Provider.GetUserByEmail(ctx, email) + assert.NoError(t, err) + assert.NotNil(t, user1) + assert.NotNil(t, user1.EmailVerifiedAt) cleanData(email) }) }