From 465a92de224ee715dafdb682ea72ac7e4b4526af Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Wed, 3 Aug 2022 23:20:23 +0530 Subject: [PATCH] feat: add managing mfa --- server/constants/env.go | 4 + server/env/env.go | 18 ++++ server/env/persist_env.go | 2 +- server/go.mod | 1 - server/go.sum | 4 - server/graph/generated/generated.go | 107 ++++++++++++++++++++ server/graph/model/models_gen.go | 3 + server/graph/schema.graphqls | 3 + server/memorystore/memory_store.go | 1 + server/memorystore/providers/redis/store.go | 2 +- server/resolvers/env.go | 1 + server/resolvers/invite_members.go | 10 ++ server/resolvers/login.go | 7 +- server/resolvers/meta.go | 7 ++ server/resolvers/resend_otp.go | 6 ++ server/resolvers/reset_password.go | 11 ++ server/resolvers/signup.go | 11 ++ server/resolvers/update_env.go | 7 ++ server/resolvers/update_profile.go | 11 ++ 19 files changed, 208 insertions(+), 8 deletions(-) diff --git a/server/constants/env.go b/server/constants/env.go index 2b26688..bd3afaf 100644 --- a/server/constants/env.go +++ b/server/constants/env.go @@ -120,7 +120,11 @@ const ( // EnvKeyDisableStrongPassword key for env variable DISABLE_STRONG_PASSWORD EnvKeyDisableStrongPassword = "DISABLE_STRONG_PASSWORD" // EnvKeyEnforceMultiFactorAuthentication is key for env variable ENFORCE_MULTI_FACTOR_AUTHENTICATION + // If enforced and changed later on, existing user will have MFA but new user will not have MFA EnvKeyEnforceMultiFactorAuthentication = "ENFORCE_MULTI_FACTOR_AUTHENTICATION" + // EnvKeyDisableMultiFactorAuthentication is key for env variable DISABLE_MULTI_FACTOR_AUTHENTICATION + // this variable is used to completely disable multi factor authentication. It will have no effect on profile preference + EnvKeyDisableMultiFactorAuthentication = "DISABLE_MULTI_FACTOR_AUTHENTICATION" // Slice variables // EnvKeyRoles key for env variable ROLES diff --git a/server/env/env.go b/server/env/env.go index e8fe863..f8df401 100644 --- a/server/env/env.go +++ b/server/env/env.go @@ -85,6 +85,7 @@ func InitAllEnv() error { osDisableRedisForEnv := os.Getenv(constants.EnvKeyDisableRedisForEnv) osDisableStrongPassword := os.Getenv(constants.EnvKeyDisableStrongPassword) osEnforceMultiFactorAuthentication := os.Getenv(constants.EnvKeyEnforceMultiFactorAuthentication) + osDisableMultiFactorAuthentication := os.Getenv(constants.EnvKeyDisableMultiFactorAuthentication) // os slice vars osAllowedOrigins := os.Getenv(constants.EnvKeyAllowedOrigins) @@ -504,6 +505,19 @@ func InitAllEnv() error { } } + if _, ok := envData[constants.EnvKeyDisableMultiFactorAuthentication]; !ok { + envData[constants.EnvKeyDisableMultiFactorAuthentication] = osDisableMultiFactorAuthentication == "true" + } + if osDisableMultiFactorAuthentication != "" { + boolValue, err := strconv.ParseBool(osDisableMultiFactorAuthentication) + if err != nil { + return err + } + if boolValue != envData[constants.EnvKeyDisableMultiFactorAuthentication].(bool) { + envData[constants.EnvKeyDisableMultiFactorAuthentication] = boolValue + } + } + // no need to add nil check as its already done above if envData[constants.EnvKeySmtpHost] == "" || envData[constants.EnvKeySmtpUsername] == "" || envData[constants.EnvKeySmtpPassword] == "" || envData[constants.EnvKeySenderEmail] == "" && envData[constants.EnvKeySmtpPort] == "" { envData[constants.EnvKeyDisableEmailVerification] = true @@ -519,6 +533,10 @@ func InitAllEnv() error { return errors.New("to enable multi factor authentication, please enable email service") } + if !envData[constants.EnvKeyIsEmailServiceEnabled].(bool) { + envData[constants.EnvKeyDisableMultiFactorAuthentication] = true + } + if envData[constants.EnvKeyDisableEmailVerification].(bool) { envData[constants.EnvKeyDisableMagicLinkLogin] = true } diff --git a/server/env/persist_env.go b/server/env/persist_env.go index e4ec275..10eb778 100644 --- a/server/env/persist_env.go +++ b/server/env/persist_env.go @@ -201,7 +201,7 @@ func PersistEnv() error { envValue := strings.TrimSpace(os.Getenv(key)) if envValue != "" { switch key { - case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication: + case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication, constants.EnvKeyDisableMultiFactorAuthentication: if envValueBool, err := strconv.ParseBool(envValue); err == nil { if value.(bool) != envValueBool { storeData[key] = envValueBool diff --git a/server/go.mod b/server/go.mod index 98dcc94..8b5cea4 100644 --- a/server/go.mod +++ b/server/go.mod @@ -5,7 +5,6 @@ go 1.16 require ( github.com/99designs/gqlgen v0.14.0 github.com/arangodb/go-driver v1.2.1 - github.com/coreos/etcd v3.3.27+incompatible github.com/coreos/go-oidc/v3 v3.1.0 github.com/gin-gonic/gin v1.7.2 github.com/go-playground/validator/v10 v10.8.0 // indirect diff --git a/server/go.sum b/server/go.sum index 7a1bbcb..c51da71 100644 --- a/server/go.sum +++ b/server/go.sum @@ -62,8 +62,6 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/coreos/etcd v3.3.27+incompatible h1:QIudLb9KeBsE5zyYxd1mjzRSkzLg9Wf9QlRwFgd6oTA= -github.com/coreos/etcd v3.3.27+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-iptables v0.4.3/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw= github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo= @@ -112,8 +110,6 @@ github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfC github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gocql/gocql v1.0.0 h1:UnbTERpP72VZ/viKE1Q1gPtmLvyTZTvuAstvSRydw/c= -github.com/gocql/gocql v1.0.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= github.com/gocql/gocql v1.2.0 h1:TZhsCd7fRuye4VyHr3WCvWwIQaZUmjsqnSIXK9FcVCE= github.com/gocql/gocql v1.2.0/go.mod h1:3gM2c4D3AnkISwBxGnMMsS8Oy4y2lhbPRsH4xnJrHG8= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= diff --git a/server/graph/generated/generated.go b/server/graph/generated/generated.go index 0d247f3..ab728ca 100644 --- a/server/graph/generated/generated.go +++ b/server/graph/generated/generated.go @@ -88,6 +88,7 @@ type ComplexityRoot struct { DisableEmailVerification func(childComplexity int) int DisableLoginPage func(childComplexity int) int DisableMagicLinkLogin func(childComplexity int) int + DisableMultiFactorAuthentication func(childComplexity int) int DisableRedisForEnv func(childComplexity int) int DisableSignUp func(childComplexity int) int DisableStrongPassword func(childComplexity int) int @@ -139,6 +140,7 @@ type ComplexityRoot struct { IsGoogleLoginEnabled func(childComplexity int) int IsLinkedinLoginEnabled func(childComplexity int) int IsMagicLinkLoginEnabled func(childComplexity int) int + IsMultiFactorAuthEnabled func(childComplexity int) int IsSignUpEnabled func(childComplexity int) int IsStrongPasswordEnabled func(childComplexity int) int Version func(childComplexity int) int @@ -592,6 +594,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Env.DisableMagicLinkLogin(childComplexity), true + case "Env.DISABLE_MULTI_FACTOR_AUTHENTICATION": + if e.complexity.Env.DisableMultiFactorAuthentication == nil { + break + } + + return e.complexity.Env.DisableMultiFactorAuthentication(childComplexity), true + case "Env.DISABLE_REDIS_FOR_ENV": if e.complexity.Env.DisableRedisForEnv == nil { break @@ -886,6 +895,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Meta.IsMagicLinkLoginEnabled(childComplexity), true + case "Meta.is_multi_factor_auth_enabled": + if e.complexity.Meta.IsMultiFactorAuthEnabled == nil { + break + } + + return e.complexity.Meta.IsMultiFactorAuthEnabled(childComplexity), true + case "Meta.is_sign_up_enabled": if e.complexity.Meta.IsSignUpEnabled == nil { break @@ -1866,6 +1882,7 @@ type Meta { is_magic_link_login_enabled: Boolean! is_sign_up_enabled: Boolean! is_strong_password_enabled: Boolean! + is_multi_factor_auth_enabled: Boolean! } type User { @@ -1965,6 +1982,7 @@ type Env { DISABLE_SIGN_UP: Boolean! DISABLE_REDIS_FOR_ENV: Boolean! DISABLE_STRONG_PASSWORD: Boolean! + DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean! ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean! ROLES: [String!] PROTECTED_ROLES: [String!] @@ -2066,6 +2084,7 @@ input UpdateEnvInput { DISABLE_SIGN_UP: Boolean DISABLE_REDIS_FOR_ENV: Boolean DISABLE_STRONG_PASSWORD: Boolean + DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean ROLES: [String!] PROTECTED_ROLES: [String!] @@ -4425,6 +4444,41 @@ func (ec *executionContext) _Env_DISABLE_STRONG_PASSWORD(ctx context.Context, fi return ec.marshalNBoolean2bool(ctx, field.Selections, res) } +func (ec *executionContext) _Env_DISABLE_MULTI_FACTOR_AUTHENTICATION(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Env", + Field: field, + Args: nil, + IsMethod: false, + IsResolver: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.DisableMultiFactorAuthentication, 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) _Env_ENFORCE_MULTI_FACTOR_AUTHENTICATION(ctx context.Context, field graphql.CollectedField, obj *model.Env) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -5558,6 +5612,41 @@ func (ec *executionContext) _Meta_is_strong_password_enabled(ctx context.Context return ec.marshalNBoolean2bool(ctx, field.Selections, res) } +func (ec *executionContext) _Meta_is_multi_factor_auth_enabled(ctx context.Context, field graphql.CollectedField, obj *model.Meta) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "Meta", + Field: field, + Args: nil, + IsMethod: false, + IsResolver: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.IsMultiFactorAuthEnabled, 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) _Mutation_signup(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -11379,6 +11468,14 @@ func (ec *executionContext) unmarshalInputUpdateEnvInput(ctx context.Context, ob if err != nil { return it, err } + case "DISABLE_MULTI_FACTOR_AUTHENTICATION": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("DISABLE_MULTI_FACTOR_AUTHENTICATION")) + it.DisableMultiFactorAuthentication, err = ec.unmarshalOBoolean2ᚖbool(ctx, v) + if err != nil { + return it, err + } case "ENFORCE_MULTI_FACTOR_AUTHENTICATION": var err error @@ -12152,6 +12249,11 @@ func (ec *executionContext) _Env(ctx context.Context, sel ast.SelectionSet, obj if out.Values[i] == graphql.Null { invalids++ } + case "DISABLE_MULTI_FACTOR_AUTHENTICATION": + out.Values[i] = ec._Env_DISABLE_MULTI_FACTOR_AUTHENTICATION(ctx, field, obj) + if out.Values[i] == graphql.Null { + invalids++ + } case "ENFORCE_MULTI_FACTOR_AUTHENTICATION": out.Values[i] = ec._Env_ENFORCE_MULTI_FACTOR_AUTHENTICATION(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -12331,6 +12433,11 @@ func (ec *executionContext) _Meta(ctx context.Context, sel ast.SelectionSet, obj if out.Values[i] == graphql.Null { invalids++ } + case "is_multi_factor_auth_enabled": + out.Values[i] = ec._Meta_is_multi_factor_auth_enabled(ctx, field, obj) + if out.Values[i] == graphql.Null { + 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 3c5f883..553d6c5 100644 --- a/server/graph/model/models_gen.go +++ b/server/graph/model/models_gen.go @@ -86,6 +86,7 @@ type Env struct { DisableSignUp bool `json:"DISABLE_SIGN_UP"` DisableRedisForEnv bool `json:"DISABLE_REDIS_FOR_ENV"` DisableStrongPassword bool `json:"DISABLE_STRONG_PASSWORD"` + DisableMultiFactorAuthentication bool `json:"DISABLE_MULTI_FACTOR_AUTHENTICATION"` EnforceMultiFactorAuthentication bool `json:"ENFORCE_MULTI_FACTOR_AUTHENTICATION"` Roles []string `json:"ROLES"` ProtectedRoles []string `json:"PROTECTED_ROLES"` @@ -164,6 +165,7 @@ type Meta struct { 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"` } type OAuthRevokeInput struct { @@ -273,6 +275,7 @@ type UpdateEnvInput struct { DisableSignUp *bool `json:"DISABLE_SIGN_UP"` DisableRedisForEnv *bool `json:"DISABLE_REDIS_FOR_ENV"` DisableStrongPassword *bool `json:"DISABLE_STRONG_PASSWORD"` + DisableMultiFactorAuthentication *bool `json:"DISABLE_MULTI_FACTOR_AUTHENTICATION"` EnforceMultiFactorAuthentication *bool `json:"ENFORCE_MULTI_FACTOR_AUTHENTICATION"` Roles []string `json:"ROLES"` ProtectedRoles []string `json:"PROTECTED_ROLES"` diff --git a/server/graph/schema.graphqls b/server/graph/schema.graphqls index ddab91c..17fc1ec 100644 --- a/server/graph/schema.graphqls +++ b/server/graph/schema.graphqls @@ -25,6 +25,7 @@ type Meta { is_magic_link_login_enabled: Boolean! is_sign_up_enabled: Boolean! is_strong_password_enabled: Boolean! + is_multi_factor_auth_enabled: Boolean! } type User { @@ -124,6 +125,7 @@ type Env { DISABLE_SIGN_UP: Boolean! DISABLE_REDIS_FOR_ENV: Boolean! DISABLE_STRONG_PASSWORD: Boolean! + DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean! ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean! ROLES: [String!] PROTECTED_ROLES: [String!] @@ -225,6 +227,7 @@ input UpdateEnvInput { DISABLE_SIGN_UP: Boolean DISABLE_REDIS_FOR_ENV: Boolean DISABLE_STRONG_PASSWORD: Boolean + DISABLE_MULTI_FACTOR_AUTHENTICATION: Boolean ENFORCE_MULTI_FACTOR_AUTHENTICATION: Boolean ROLES: [String!] PROTECTED_ROLES: [String!] diff --git a/server/memorystore/memory_store.go b/server/memorystore/memory_store.go index dc7a195..a44856e 100644 --- a/server/memorystore/memory_store.go +++ b/server/memorystore/memory_store.go @@ -33,6 +33,7 @@ func InitMemStore() error { constants.EnvKeyDisableStrongPassword: false, constants.EnvKeyIsEmailServiceEnabled: false, constants.EnvKeyEnforceMultiFactorAuthentication: false, + constants.EnvKeyDisableMultiFactorAuthentication: false, } requiredEnvs := RequiredEnvStoreObj.GetRequiredEnv() diff --git a/server/memorystore/providers/redis/store.go b/server/memorystore/providers/redis/store.go index 4fb1206..f57e1ca 100644 --- a/server/memorystore/providers/redis/store.go +++ b/server/memorystore/providers/redis/store.go @@ -160,7 +160,7 @@ func (c *provider) GetEnvStore() (map[string]interface{}, error) { return nil, err } for key, value := range data { - if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword || key == constants.EnvKeyIsEmailServiceEnabled || key == constants.EnvKeyEnforceMultiFactorAuthentication { + if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableEmailVerification || key == constants.EnvKeyDisableLoginPage || key == constants.EnvKeyDisableMagicLinkLogin || key == constants.EnvKeyDisableRedisForEnv || key == constants.EnvKeyDisableSignUp || key == constants.EnvKeyDisableStrongPassword || key == constants.EnvKeyIsEmailServiceEnabled || key == constants.EnvKeyEnforceMultiFactorAuthentication || key == constants.EnvKeyDisableMultiFactorAuthentication { boolValue, err := strconv.ParseBool(value) if err != nil { return res, err diff --git a/server/resolvers/env.go b/server/resolvers/env.go index e97fac4..cd78f98 100644 --- a/server/resolvers/env.go +++ b/server/resolvers/env.go @@ -171,6 +171,7 @@ func EnvResolver(ctx context.Context) (*model.Env, error) { res.DisableSignUp = store[constants.EnvKeyDisableSignUp].(bool) res.DisableStrongPassword = store[constants.EnvKeyDisableStrongPassword].(bool) res.EnforceMultiFactorAuthentication = store[constants.EnvKeyEnforceMultiFactorAuthentication].(bool) + res.DisableMultiFactorAuthentication = store[constants.EnvKeyDisableMultiFactorAuthentication].(bool) return res, nil } diff --git a/server/resolvers/invite_members.go b/server/resolvers/invite_members.go index 914e7bb..e05406e 100644 --- a/server/resolvers/invite_members.go +++ b/server/resolvers/invite_members.go @@ -16,6 +16,7 @@ import ( "github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/memorystore" "github.com/authorizerdev/authorizer/server/parsers" + "github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/token" "github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/validators" @@ -136,6 +137,15 @@ func InviteMembersResolver(ctx context.Context, params model.InviteMemberInput) user.SignupMethods = constants.AuthRecipeMethodBasicAuth verificationRequest.Identifier = constants.VerificationTypeForgotPassword + 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) + } verifyEmailURL = appURL + "/setup-password" } diff --git a/server/resolvers/login.go b/server/resolvers/login.go index 6325a6a..8587330 100644 --- a/server/resolvers/login.go +++ b/server/resolvers/login.go @@ -104,8 +104,13 @@ func LoginResolver(ctx context.Context, params model.LoginInput) (*model.AuthRes 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 { + if refs.BoolValue(user.IsMultiFactorAuthEnabled) && isEmailServiceEnabled && !isMFADisabled { otp := utils.GenerateOTP() otpData, err := db.Provider.UpsertOTP(ctx, &models.OTP{ Email: user.Email, diff --git a/server/resolvers/meta.go b/server/resolvers/meta.go index 2acbcb4..f53437c 100644 --- a/server/resolvers/meta.go +++ b/server/resolvers/meta.go @@ -107,6 +107,12 @@ func MetaResolver(ctx context.Context) (*model.Meta, error) { isSignUpDisabled = true } + isMultiFactorAuthenticationEnabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMultiFactorAuthentication) + if err != nil { + log.Debug("Failed to get Disable Multi Factor Authentication from environment variable", err) + isSignUpDisabled = true + } + metaInfo := model.Meta{ Version: constants.VERSION, ClientID: clientID, @@ -120,6 +126,7 @@ func MetaResolver(ctx context.Context) (*model.Meta, error) { IsMagicLinkLoginEnabled: !isMagicLinkLoginDisabled, IsSignUpEnabled: !isSignUpDisabled, IsStrongPasswordEnabled: !isStrongPasswordDisabled, + IsMultiFactorAuthEnabled: !isMultiFactorAuthenticationEnabled, } return &metaInfo, nil } diff --git a/server/resolvers/resend_otp.go b/server/resolvers/resend_otp.go index edd7445..4ae53c0 100644 --- a/server/resolvers/resend_otp.go +++ b/server/resolvers/resend_otp.go @@ -52,6 +52,12 @@ func ResendOTPResolver(ctx context.Context, params model.ResendOTPRequest) (*mod return nil, errors.New("email service not enabled") } + isMFADisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMultiFactorAuthentication) + if err != nil || isMFADisabled { + log.Debug("MFA service not enabled: ", err) + return nil, errors.New("multi factor authentication is disabled for this instance") + } + // get otp by email otpData, err := db.Provider.GetOTPByEmail(ctx, params.Email) if err != nil { diff --git a/server/resolvers/reset_password.go b/server/resolvers/reset_password.go index abdfc79..84976bb 100644 --- a/server/resolvers/reset_password.go +++ b/server/resolvers/reset_password.go @@ -14,6 +14,7 @@ import ( "github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/memorystore" "github.com/authorizerdev/authorizer/server/parsers" + "github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/token" "github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/validators" @@ -84,6 +85,16 @@ func ResetPasswordResolver(ctx context.Context, params model.ResetPasswordInput) signupMethod := user.SignupMethods if !strings.Contains(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 diff --git a/server/resolvers/signup.go b/server/resolvers/signup.go index 71ffb93..b7a548c 100644 --- a/server/resolvers/signup.go +++ b/server/resolvers/signup.go @@ -17,6 +17,7 @@ import ( "github.com/authorizerdev/authorizer/server/graph/model" "github.com/authorizerdev/authorizer/server/memorystore" "github.com/authorizerdev/authorizer/server/parsers" + "github.com/authorizerdev/authorizer/server/refs" "github.com/authorizerdev/authorizer/server/token" "github.com/authorizerdev/authorizer/server/utils" "github.com/authorizerdev/authorizer/server/validators" @@ -161,6 +162,16 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR user.IsMultiFactorAuthEnabled = params.IsMultiFactorAuthEnabled } + 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 = constants.AuthRecipeMethodBasicAuth isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification) if err != nil { diff --git a/server/resolvers/update_env.go b/server/resolvers/update_env.go index deecc20..caeea6e 100644 --- a/server/resolvers/update_env.go +++ b/server/resolvers/update_env.go @@ -235,6 +235,7 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model // in case SMTP is off but env is set to true if updatedData[constants.EnvKeySmtpHost] == "" || updatedData[constants.EnvKeySmtpUsername] == "" || updatedData[constants.EnvKeySmtpPassword] == "" || updatedData[constants.EnvKeySenderEmail] == "" && updatedData[constants.EnvKeySmtpPort] == "" { updatedData[constants.EnvKeyIsEmailServiceEnabled] = false + updatedData[constants.EnvKeyDisableMultiFactorAuthentication] = true if !updatedData[constants.EnvKeyDisableEmailVerification].(bool) { updatedData[constants.EnvKeyDisableEmailVerification] = true } @@ -248,6 +249,12 @@ func UpdateEnvResolver(ctx context.Context, params model.UpdateEnvInput) (*model updatedData[constants.EnvKeyIsEmailServiceEnabled] = true } + if !currentData[constants.EnvKeyEnforceMultiFactorAuthentication].(bool) && updatedData[constants.EnvKeyEnforceMultiFactorAuthentication].(bool) && !updatedData[constants.EnvKeyDisableMultiFactorAuthentication].(bool) { + go db.Provider.UpdateUsers(ctx, map[string]interface{}{ + "is_multi_factor_auth_enabled": true, + }, nil) + } + // check the roles change if len(params.Roles) > 0 { if len(params.DefaultRoles) > 0 { diff --git a/server/resolvers/update_profile.go b/server/resolvers/update_profile.go index ebc4718..87762d9 100644 --- a/server/resolvers/update_profile.go +++ b/server/resolvers/update_profile.go @@ -104,6 +104,17 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput) } } + isMFAEnforced, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyEnforceMultiFactorAuthentication) + if err != nil { + log.Debug("MFA service not enabled: ", err) + isMFAEnforced = false + } + + if isMFAEnforced && !refs.BoolValue(params.IsMultiFactorAuthEnabled) { + log.Debug("Cannot disable mfa service as it is enforced:") + return nil, errors.New("cannot disable multi factor authentication as it is enforced by organization") + } + user.IsMultiFactorAuthEnabled = params.IsMultiFactorAuthEnabled }