feat: add is_multi_factor_auth_enabled

This commit is contained in:
Lakhan Samani 2022-07-23 15:26:44 +05:30
parent 0f081ac3c8
commit 9ef5f33f7a
8 changed files with 216 additions and 117 deletions

View File

@ -14,23 +14,24 @@ type User struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id"`
Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at"`
Password *string `gorm:"type:text" json:"password" bson:"password" cql:"password"`
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods"`
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name"`
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name"`
MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name"`
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname"`
Gender *string `json:"gender" bson:"gender" cql:"gender"`
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate"`
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number" cql:"phone_number"`
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at"`
Picture *string `gorm:"type:text" json:"picture" bson:"picture" cql:"picture"`
Roles string `json:"roles" bson:"roles" cql:"roles"`
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
Email string `gorm:"unique" json:"email" bson:"email" cql:"email"`
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at"`
Password *string `gorm:"type:text" json:"password" bson:"password" cql:"password"`
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods"`
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name"`
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name"`
MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name"`
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname"`
Gender *string `json:"gender" bson:"gender" cql:"gender"`
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate"`
PhoneNumber *string `gorm:"unique" json:"phone_number" bson:"phone_number" cql:"phone_number"`
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at"`
Picture *string `gorm:"type:text" json:"picture" bson:"picture" cql:"picture"`
Roles string `json:"roles" bson:"roles" cql:"roles"`
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp"`
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled" bson:"is_multi_factor_auth_enabled" cql:"is_multi_factor_auth_enabled"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at"`
}
func (user *User) AsAPIUser() *model.User {
@ -42,23 +43,24 @@ func (user *User) AsAPIUser() *model.User {
id = strings.TrimPrefix(id, Collections.WebhookLog+"/")
}
return &model.User{
ID: id,
Email: user.Email,
EmailVerified: isEmailVerified,
SignupMethods: user.SignupMethods,
GivenName: user.GivenName,
FamilyName: user.FamilyName,
MiddleName: user.MiddleName,
Nickname: user.Nickname,
PreferredUsername: refs.NewStringRef(user.Email),
Gender: user.Gender,
Birthdate: user.Birthdate,
PhoneNumber: user.PhoneNumber,
PhoneNumberVerified: &isPhoneVerified,
Picture: user.Picture,
Roles: strings.Split(user.Roles, ","),
RevokedTimestamp: user.RevokedTimestamp,
CreatedAt: refs.NewInt64Ref(user.CreatedAt),
UpdatedAt: refs.NewInt64Ref(user.UpdatedAt),
ID: id,
Email: user.Email,
EmailVerified: isEmailVerified,
SignupMethods: user.SignupMethods,
GivenName: user.GivenName,
FamilyName: user.FamilyName,
MiddleName: user.MiddleName,
Nickname: user.Nickname,
PreferredUsername: refs.NewStringRef(user.Email),
Gender: user.Gender,
Birthdate: user.Birthdate,
PhoneNumber: user.PhoneNumber,
PhoneNumberVerified: &isPhoneVerified,
Picture: user.Picture,
Roles: strings.Split(user.Roles, ","),
RevokedTimestamp: user.RevokedTimestamp,
IsMultiFactorAuthEnabled: user.IsMultiFactorAuthEnabled,
CreatedAt: refs.NewInt64Ref(user.CreatedAt),
UpdatedAt: refs.NewInt64Ref(user.UpdatedAt),
}
}

View File

@ -159,6 +159,12 @@ func NewProvider() (*provider, error) {
if err != nil {
return nil, err
}
// add is_multi_factor_auth_enabled on users table
userTableAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD is_multi_factor_auth_enabled boolean;`, KeySpace, models.Collections.User)
err = session.Query(userTableAlterQuery).Exec()
if err != nil {
return nil, err
}
// token is reserved keyword in cassandra, hence we need to use jwt_token
verificationRequestCollectionQuery := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s.%s (id text, jwt_token text, identifier text, expires_at bigint, email text, nonce text, redirect_uri text, created_at bigint, updated_at bigint, PRIMARY KEY (id))", KeySpace, models.Collections.VerificationRequest)

View File

@ -204,24 +204,25 @@ type ComplexityRoot struct {
}
User struct {
Birthdate func(childComplexity int) int
CreatedAt func(childComplexity int) int
Email func(childComplexity int) int
EmailVerified func(childComplexity int) int
FamilyName func(childComplexity int) int
Gender func(childComplexity int) int
GivenName func(childComplexity int) int
ID func(childComplexity int) int
MiddleName func(childComplexity int) int
Nickname func(childComplexity int) int
PhoneNumber func(childComplexity int) int
PhoneNumberVerified func(childComplexity int) int
Picture func(childComplexity int) int
PreferredUsername func(childComplexity int) int
RevokedTimestamp func(childComplexity int) int
Roles func(childComplexity int) int
SignupMethods func(childComplexity int) int
UpdatedAt func(childComplexity int) int
Birthdate func(childComplexity int) int
CreatedAt func(childComplexity int) int
Email func(childComplexity int) int
EmailVerified func(childComplexity int) int
FamilyName func(childComplexity int) int
Gender func(childComplexity int) int
GivenName func(childComplexity int) int
ID func(childComplexity int) int
IsMultiFactorAuthEnabled func(childComplexity int) int
MiddleName func(childComplexity int) int
Nickname func(childComplexity int) int
PhoneNumber func(childComplexity int) int
PhoneNumberVerified func(childComplexity int) int
Picture func(childComplexity int) int
PreferredUsername func(childComplexity int) int
RevokedTimestamp func(childComplexity int) int
Roles func(childComplexity int) int
SignupMethods func(childComplexity int) int
UpdatedAt func(childComplexity int) int
}
Users struct {
@ -1429,6 +1430,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
return e.complexity.User.ID(childComplexity), true
case "User.is_multi_factor_auth_enabled":
if e.complexity.User.IsMultiFactorAuthEnabled == nil {
break
}
return e.complexity.User.IsMultiFactorAuthEnabled(childComplexity), true
case "User.middle_name":
if e.complexity.User.MiddleName == nil {
break
@ -1836,6 +1844,7 @@ type User {
created_at: Int64
updated_at: Int64
revoked_timestamp: Int64
is_multi_factor_auth_enabled: Boolean
}
type Users {
@ -2052,6 +2061,7 @@ input SignUpInput {
roles: [String!]
scope: [String!]
redirect_uri: String
is_multi_factor_auth_enabled: Boolean
}
input LoginInput {
@ -2083,6 +2093,7 @@ input UpdateProfileInput {
birthdate: String
phone_number: String
picture: String
is_multi_factor_auth_enabled: Boolean
}
input UpdateUserInput {
@ -2098,6 +2109,7 @@ input UpdateUserInput {
phone_number: String
picture: String
roles: [String]
is_multi_factor_auth_enabled: Boolean
}
input ForgotPasswordInput {
@ -7888,6 +7900,38 @@ func (ec *executionContext) _User_revoked_timestamp(ctx context.Context, field g
return ec.marshalOInt642ᚖint64(ctx, field.Selections, res)
}
func (ec *executionContext) _User_is_multi_factor_auth_enabled(ctx context.Context, field graphql.CollectedField, obj *model.User) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
ec.Error(ctx, ec.Recover(ctx, r))
ret = graphql.Null
}
}()
fc := &graphql.FieldContext{
Object: "User",
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 {
return graphql.Null
}
res := resTmp.(*bool)
fc.Result = res
return ec.marshalOBoolean2ᚖbool(ctx, field.Selections, res)
}
func (ec *executionContext) _Users_pagination(ctx context.Context, field graphql.CollectedField, obj *model.Users) (ret graphql.Marshaler) {
defer func() {
if r := recover(); r != nil {
@ -10765,6 +10809,14 @@ func (ec *executionContext) unmarshalInputSignUpInput(ctx context.Context, obj i
if err != nil {
return it, err
}
case "is_multi_factor_auth_enabled":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("is_multi_factor_auth_enabled"))
it.IsMultiFactorAuthEnabled, err = ec.unmarshalOBoolean2ᚖbool(ctx, v)
if err != nil {
return it, err
}
}
}
@ -11304,6 +11356,14 @@ func (ec *executionContext) unmarshalInputUpdateProfileInput(ctx context.Context
if err != nil {
return it, err
}
case "is_multi_factor_auth_enabled":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("is_multi_factor_auth_enabled"))
it.IsMultiFactorAuthEnabled, err = ec.unmarshalOBoolean2ᚖbool(ctx, v)
if err != nil {
return it, err
}
}
}
@ -11415,6 +11475,14 @@ func (ec *executionContext) unmarshalInputUpdateUserInput(ctx context.Context, o
if err != nil {
return it, err
}
case "is_multi_factor_auth_enabled":
var err error
ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("is_multi_factor_auth_enabled"))
it.IsMultiFactorAuthEnabled, err = ec.unmarshalOBoolean2ᚖbool(ctx, v)
if err != nil {
return it, err
}
}
}
@ -12482,6 +12550,8 @@ func (ec *executionContext) _User(ctx context.Context, sel ast.SelectionSet, obj
out.Values[i] = ec._User_updated_at(ctx, field, obj)
case "revoked_timestamp":
out.Values[i] = ec._User_revoked_timestamp(ctx, field, obj)
case "is_multi_factor_auth_enabled":
out.Values[i] = ec._User_is_multi_factor_auth_enabled(ctx, field, obj)
default:
panic("unknown field " + strconv.Quote(field.Name))
}

View File

@ -205,20 +205,21 @@ type SessionQueryInput struct {
}
type SignUpInput struct {
Email string `json:"email"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"`
Picture *string `json:"picture"`
Password string `json:"password"`
ConfirmPassword string `json:"confirm_password"`
Roles []string `json:"roles"`
Scope []string `json:"scope"`
RedirectURI *string `json:"redirect_uri"`
Email string `json:"email"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"`
Picture *string `json:"picture"`
Password string `json:"password"`
ConfirmPassword string `json:"confirm_password"`
Roles []string `json:"roles"`
Scope []string `json:"scope"`
RedirectURI *string `json:"redirect_uri"`
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
}
type TestEndpointRequest struct {
@ -285,33 +286,35 @@ type UpdateEnvInput struct {
}
type UpdateProfileInput struct {
OldPassword *string `json:"old_password"`
NewPassword *string `json:"new_password"`
ConfirmNewPassword *string `json:"confirm_new_password"`
Email *string `json:"email"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"`
Picture *string `json:"picture"`
OldPassword *string `json:"old_password"`
NewPassword *string `json:"new_password"`
ConfirmNewPassword *string `json:"confirm_new_password"`
Email *string `json:"email"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"`
Picture *string `json:"picture"`
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
}
type UpdateUserInput struct {
ID string `json:"id"`
Email *string `json:"email"`
EmailVerified *bool `json:"email_verified"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"`
Picture *string `json:"picture"`
Roles []*string `json:"roles"`
ID string `json:"id"`
Email *string `json:"email"`
EmailVerified *bool `json:"email_verified"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"`
Picture *string `json:"picture"`
Roles []*string `json:"roles"`
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
}
type UpdateWebhookRequest struct {
@ -323,24 +326,25 @@ type UpdateWebhookRequest struct {
}
type User struct {
ID string `json:"id"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
SignupMethods string `json:"signup_methods"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
PreferredUsername *string `json:"preferred_username"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"`
PhoneNumberVerified *bool `json:"phone_number_verified"`
Picture *string `json:"picture"`
Roles []string `json:"roles"`
CreatedAt *int64 `json:"created_at"`
UpdatedAt *int64 `json:"updated_at"`
RevokedTimestamp *int64 `json:"revoked_timestamp"`
ID string `json:"id"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
SignupMethods string `json:"signup_methods"`
GivenName *string `json:"given_name"`
FamilyName *string `json:"family_name"`
MiddleName *string `json:"middle_name"`
Nickname *string `json:"nickname"`
PreferredUsername *string `json:"preferred_username"`
Gender *string `json:"gender"`
Birthdate *string `json:"birthdate"`
PhoneNumber *string `json:"phone_number"`
PhoneNumberVerified *bool `json:"phone_number_verified"`
Picture *string `json:"picture"`
Roles []string `json:"roles"`
CreatedAt *int64 `json:"created_at"`
UpdatedAt *int64 `json:"updated_at"`
RevokedTimestamp *int64 `json:"revoked_timestamp"`
IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"`
}
type Users struct {

View File

@ -47,6 +47,7 @@ type User {
created_at: Int64
updated_at: Int64
revoked_timestamp: Int64
is_multi_factor_auth_enabled: Boolean
}
type Users {
@ -263,6 +264,7 @@ input SignUpInput {
roles: [String!]
scope: [String!]
redirect_uri: String
is_multi_factor_auth_enabled: Boolean
}
input LoginInput {
@ -294,6 +296,7 @@ input UpdateProfileInput {
birthdate: String
phone_number: String
picture: String
is_multi_factor_auth_enabled: Boolean
}
input UpdateUserInput {
@ -309,6 +312,7 @@ input UpdateUserInput {
phone_number: String
picture: String
roles: [String]
is_multi_factor_auth_enabled: Boolean
}
input ForgotPasswordInput {

View File

@ -157,6 +157,10 @@ func SignupResolver(ctx context.Context, params model.SignUpInput) (*model.AuthR
user.Picture = params.Picture
}
if params.IsMultiFactorAuthEnabled != nil {
user.IsMultiFactorAuthEnabled = params.IsMultiFactorAuthEnabled
}
user.SignupMethods = constants.AuthRecipeMethodBasicAuth
isEmailVerificationDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableEmailVerification)
if err != nil {

View File

@ -94,6 +94,10 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput)
user.Picture = params.Picture
}
if params.IsMultiFactorAuthEnabled != nil && refs.BoolValue(user.IsMultiFactorAuthEnabled) != refs.BoolValue(params.IsMultiFactorAuthEnabled) {
user.IsMultiFactorAuthEnabled = params.IsMultiFactorAuthEnabled
}
isPasswordChanging := false
if params.NewPassword != nil && params.ConfirmNewPassword == nil {
isPasswordChanging = true

View File

@ -15,6 +15,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"
@ -56,38 +57,42 @@ func UpdateUserResolver(ctx context.Context, params model.UpdateUserInput) (*mod
return res, fmt.Errorf(`User not found`)
}
if params.GivenName != nil && user.GivenName != params.GivenName {
if params.GivenName != nil && refs.StringValue(user.GivenName) != refs.StringValue(params.GivenName) {
user.GivenName = params.GivenName
}
if params.FamilyName != nil && user.FamilyName != params.FamilyName {
if params.FamilyName != nil && refs.StringValue(user.FamilyName) != refs.StringValue(params.FamilyName) {
user.FamilyName = params.FamilyName
}
if params.MiddleName != nil && user.MiddleName != params.MiddleName {
if params.MiddleName != nil && refs.StringValue(user.MiddleName) != refs.StringValue(params.MiddleName) {
user.MiddleName = params.MiddleName
}
if params.Nickname != nil && user.Nickname != params.Nickname {
if params.Nickname != nil && refs.StringValue(user.Nickname) != refs.StringValue(params.Nickname) {
user.Nickname = params.Nickname
}
if params.Birthdate != nil && user.Birthdate != params.Birthdate {
if params.Birthdate != nil && refs.StringValue(user.Birthdate) != refs.StringValue(params.Birthdate) {
user.Birthdate = params.Birthdate
}
if params.Gender != nil && user.Gender != params.Gender {
if params.Gender != nil && refs.StringValue(user.Gender) != refs.StringValue(params.Gender) {
user.Gender = params.Gender
}
if params.PhoneNumber != nil && user.PhoneNumber != params.PhoneNumber {
if params.PhoneNumber != nil && refs.StringValue(user.PhoneNumber) != refs.StringValue(params.PhoneNumber) {
user.PhoneNumber = params.PhoneNumber
}
if params.Picture != nil && user.Picture != params.Picture {
if params.Picture != nil && refs.StringValue(user.Picture) != refs.StringValue(params.Picture) {
user.Picture = params.Picture
}
if params.IsMultiFactorAuthEnabled != nil && refs.BoolValue(user.IsMultiFactorAuthEnabled) != refs.BoolValue(params.IsMultiFactorAuthEnabled) {
user.IsMultiFactorAuthEnabled = params.IsMultiFactorAuthEnabled
}
if params.EmailVerified != nil {
if *params.EmailVerified {
now := time.Now().Unix()