From 1eb8965f98dcea421ab99b5626b46afe930b9c79 Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Wed, 21 Dec 2022 23:14:24 +0530 Subject: [PATCH] feat: add mobile based basic auth --- server/constants/auth_methods.go | 2 + server/constants/env.go | 2 + server/db/models/user.go | 2 +- server/db/providers/arangodb/user.go | 39 + server/db/providers/cassandradb/provider.go | 6 + server/db/providers/cassandradb/user.go | 24 + server/db/providers/dynamodb/user.go | 35 + server/db/providers/mongodb/user.go | 13 + server/db/providers/provider_template/user.go | 7 + server/db/providers/providers.go | 2 + server/db/providers/sql/user.go | 9 +- server/env/env.go | 14 + server/env/persist_env.go | 2 +- server/go.sum | 8 - server/graph/generated/generated.go | 1083 +++++++++++------ server/graph/model/models_gen.go | 19 + server/graph/schema.graphqls | 763 ++++++------ server/graph/schema.resolvers.go | 5 + server/memorystore/memory_store.go | 1 + server/memorystore/providers/redis/store.go | 2 +- server/resolvers/mobile_basic_auth_signup.go | 270 ++++ server/resolvers/update_env.go | 6 + server/resolvers/update_profile.go | 8 +- server/test/mobile_basic_auth_signup_test.go | 82 ++ server/test/resolvers_test.go | 1 + 25 files changed, 1624 insertions(+), 781 deletions(-) create mode 100644 server/resolvers/mobile_basic_auth_signup.go create mode 100644 server/test/mobile_basic_auth_signup_test.go diff --git a/server/constants/auth_methods.go b/server/constants/auth_methods.go index 59f0b1e..ee1e679 100644 --- a/server/constants/auth_methods.go +++ b/server/constants/auth_methods.go @@ -3,6 +3,8 @@ package constants const ( // AuthRecipeMethodBasicAuth is the basic_auth auth method AuthRecipeMethodBasicAuth = "basic_auth" + // AuthRecipeMethodMobileBasicAuth is the mobile basic_auth method, where user can signup using mobile number and password + AuthRecipeMethodMobileBasicAuth = "mobile_basic_auth" // AuthRecipeMethodMagicLinkLogin is the magic_link_login auth method AuthRecipeMethodMagicLinkLogin = "magic_link_login" // AuthRecipeMethodGoogle is the google auth method diff --git a/server/constants/env.go b/server/constants/env.go index a7dbd65..a91ad21 100644 --- a/server/constants/env.go +++ b/server/constants/env.go @@ -125,6 +125,8 @@ const ( EnvKeyDisableEmailVerification = "DISABLE_EMAIL_VERIFICATION" // EnvKeyDisableBasicAuthentication key for env variable DISABLE_BASIC_AUTH EnvKeyDisableBasicAuthentication = "DISABLE_BASIC_AUTHENTICATION" + // EnvKeyDisableBasicAuthentication key for env variable DISABLE_MOBILE_BASIC_AUTH + EnvKeyDisableMobileBasicAuthentication = "DISABLE_MOBILE_BASIC_AUTHENTICATION" // EnvKeyDisableMagicLinkLogin key for env variable DISABLE_MAGIC_LINK_LOGIN EnvKeyDisableMagicLinkLogin = "DISABLE_MAGIC_LINK_LOGIN" // EnvKeyDisableLoginPage key for env variable DISABLE_LOGIN_PAGE diff --git a/server/db/models/user.go b/server/db/models/user.go index 4628359..6a50741 100644 --- a/server/db/models/user.go +++ b/server/db/models/user.go @@ -25,7 +25,7 @@ type User struct { Nickname *string `json:"nickname" bson:"nickname" cql:"nickname" dynamo:"nickname"` Gender *string `json:"gender" bson:"gender" cql:"gender" dynamo:"gender"` 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"` + PhoneNumber *string `gorm:"index" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number" index:"phone_number,hash"` 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"` Roles string `json:"roles" bson:"roles" cql:"roles" dynamo:"roles"` diff --git a/server/db/providers/arangodb/user.go b/server/db/providers/arangodb/user.go index 945de33..1201297 100644 --- a/server/db/providers/arangodb/user.go +++ b/server/db/providers/arangodb/user.go @@ -15,6 +15,7 @@ import ( "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" ) // AddUser to save user information in database @@ -32,6 +33,12 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User, user.Roles = defaultRoles } + 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") + } + } + user.CreatedAt = time.Now().Unix() user.UpdatedAt = time.Now().Unix() userCollection, _ := p.db.Collection(ctx, models.Collections.User) @@ -48,6 +55,7 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User, // UpdateUser to update user information in database func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.User, error) { user.UpdatedAt = time.Now().Unix() + collection, _ := p.db.Collection(ctx, models.Collections.User) meta, err := collection.UpdateDocument(ctx, user.Key, user) if err != nil { @@ -211,3 +219,34 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, return nil } + +// GetUserByPhoneNumber to get user information from database using phone number +func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) { + var user models.User + + query := fmt.Sprintf("FOR d in %s FILTER d.phone_number == @phone_number RETURN d", models.Collections.User) + bindVars := map[string]interface{}{ + "phone_number": phoneNumber, + } + + cursor, err := p.db.Query(ctx, query, bindVars) + if err != nil { + return nil, err + } + defer cursor.Close() + + for { + if !cursor.HasMore() { + if user.Key == "" { + return nil, fmt.Errorf("user not found") + } + break + } + _, err := cursor.ReadDocument(ctx, &user) + if err != nil { + return nil, err + } + } + + return &user, nil +} diff --git a/server/db/providers/cassandradb/provider.go b/server/db/providers/cassandradb/provider.go index 5acb656..6849089 100644 --- a/server/db/providers/cassandradb/provider.go +++ b/server/db/providers/cassandradb/provider.go @@ -161,6 +161,12 @@ func NewProvider() (*provider, error) { if err != nil { return nil, err } + + userPhoneNumberIndexQuery := fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_user_phone_number ON %s.%s (phone_number)", KeySpace, models.Collections.User) + err = session.Query(userPhoneNumberIndexQuery).Exec() + 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() diff --git a/server/db/providers/cassandradb/user.go b/server/db/providers/cassandradb/user.go index 4da7ec9..5dd7c31 100644 --- a/server/db/providers/cassandradb/user.go +++ b/server/db/providers/cassandradb/user.go @@ -12,6 +12,7 @@ import ( "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/gocql/gocql" "github.com/google/uuid" ) @@ -30,6 +31,12 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User, user.Roles = defaultRoles } + 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") + } + } + user.CreatedAt = time.Now().Unix() user.UpdatedAt = time.Now().Unix() @@ -83,6 +90,12 @@ 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) { 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) if err != nil { return user, err @@ -299,3 +312,14 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, return nil } + +// GetUserByPhoneNumber to get user information from database using phone number +func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) { + var user models.User + query := fmt.Sprintf("SELECT id, email, email_verified_at, password, signup_methods, given_name, family_name, middle_name, nickname, birthdate, phone_number, phone_number_verified_at, picture, roles, revoked_timestamp, is_multi_factor_auth_enabled, created_at, updated_at FROM %s WHERE phone_number = '%s' LIMIT 1 ALLOW FILTERING", KeySpace+"."+models.Collections.User, phoneNumber) + err := p.db.Query(query).Consistency(gocql.One).Scan(&user.ID, &user.Email, &user.EmailVerifiedAt, &user.Password, &user.SignupMethods, &user.GivenName, &user.FamilyName, &user.MiddleName, &user.Nickname, &user.Birthdate, &user.PhoneNumber, &user.PhoneNumberVerifiedAt, &user.Picture, &user.Roles, &user.RevokedTimestamp, &user.IsMultiFactorAuthEnabled, &user.CreatedAt, &user.UpdatedAt) + if err != nil { + return nil, err + } + return &user, nil +} diff --git a/server/db/providers/dynamodb/user.go b/server/db/providers/dynamodb/user.go index 3bd45db..2ca1346 100644 --- a/server/db/providers/dynamodb/user.go +++ b/server/db/providers/dynamodb/user.go @@ -3,12 +3,15 @@ package dynamodb import ( "context" "errors" + "fmt" + "strings" "time" "github.com/authorizerdev/authorizer/server/constants" "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/google/uuid" "github.com/guregu/dynamo" log "github.com/sirupsen/logrus" @@ -30,6 +33,12 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User, user.Roles = defaultRoles } + if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" { + if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil { + return user, fmt.Errorf("user with given phone number already exists") + } + } + user.CreatedAt = time.Now().Unix() user.UpdatedAt = time.Now().Unix() @@ -49,6 +58,12 @@ func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.Use 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) if err != nil { return user, err @@ -193,3 +208,23 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, } return nil } + +// GetUserByPhoneNumber to get user information from database using phone number +func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) { + var users []models.User + var user models.User + + collection := p.db.Table(models.Collections.User) + err := collection.Scan().Index("phone_number").Filter("'phone_number' = ?", phoneNumber).AllWithContext(ctx, &users) + + if err != nil { + return nil, err + } + + if len(users) > 0 { + user = users[0] + return &user, nil + } else { + return nil, errors.New("no record found") + } +} diff --git a/server/db/providers/mongodb/user.go b/server/db/providers/mongodb/user.go index 6e90a40..32b6a17 100644 --- a/server/db/providers/mongodb/user.go +++ b/server/db/providers/mongodb/user.go @@ -155,3 +155,16 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, } return nil } + +// GetUserByPhoneNumber to get user information from database using phone number +func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) { + var user models.User + + userCollection := p.db.Collection(models.Collections.User, options.Collection()) + err := userCollection.FindOne(ctx, bson.M{"phone_number": phoneNumber}).Decode(&user) + if err != nil { + return nil, err + } + + return &user, nil +} diff --git a/server/db/providers/provider_template/user.go b/server/db/providers/provider_template/user.go index 2b167db..286e74d 100644 --- a/server/db/providers/provider_template/user.go +++ b/server/db/providers/provider_template/user.go @@ -69,3 +69,10 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, return nil } + +// GetUserByPhoneNumber to get user information from database using phone number +func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) { + var user *models.User + + return user, nil +} diff --git a/server/db/providers/providers.go b/server/db/providers/providers.go index a578396..7325204 100644 --- a/server/db/providers/providers.go +++ b/server/db/providers/providers.go @@ -18,6 +18,8 @@ type Provider interface { ListUsers(ctx context.Context, pagination model.Pagination) (*model.Users, error) // GetUserByEmail to get user information from database using email address GetUserByEmail(ctx context.Context, email string) (models.User, error) + // GetUserByPhoneNumber to get user information from database using phone number + GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) // GetUserByID to get user information from database using user ID GetUserByID(ctx context.Context, id string) (models.User, error) // UpdateUsers to update multiple users, with parameters of user IDs slice diff --git a/server/db/providers/sql/user.go b/server/db/providers/sql/user.go index d8f74a4..b8fd94f 100644 --- a/server/db/providers/sql/user.go +++ b/server/db/providers/sql/user.go @@ -31,7 +31,7 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User, } if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" { - if u, _ := p.GetUserByPhone(ctx, refs.StringValue(user.PhoneNumber)); u != nil { + if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil { return user, fmt.Errorf("user with given phone number already exists") } } @@ -57,7 +57,7 @@ func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.Use user.UpdatedAt = time.Now().Unix() if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" { - if u, _ := p.GetUserByPhone(ctx, refs.StringValue(user.PhoneNumber)); u != nil && u.ID != user.ID { + 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") } } @@ -156,12 +156,13 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{}, return nil } -func (p *provider) GetUserByPhone(ctx context.Context, phoneNumber string) (*models.User, error) { +// GetUserByPhoneNumber to get user information from database using phone number +func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) { var user *models.User result := p.db.Where("phone_number = ?", phoneNumber).First(&user) if result.Error != nil { - return user, result.Error + return nil, result.Error } return user, nil diff --git a/server/env/env.go b/server/env/env.go index 5905b99..cbc252d 100644 --- a/server/env/env.go +++ b/server/env/env.go @@ -86,6 +86,7 @@ func InitAllEnv() error { osAppCookieSecure := os.Getenv(constants.EnvKeyAppCookieSecure) osAdminCookieSecure := os.Getenv(constants.EnvKeyAdminCookieSecure) osDisableBasicAuthentication := os.Getenv(constants.EnvKeyDisableBasicAuthentication) + osDisableMobileBasicAuthentication := os.Getenv(constants.AuthRecipeMethodMobileBasicAuth) osDisableEmailVerification := os.Getenv(constants.EnvKeyDisableEmailVerification) osDisableMagicLinkLogin := os.Getenv(constants.EnvKeyDisableMagicLinkLogin) osDisableLoginPage := os.Getenv(constants.EnvKeyDisableLoginPage) @@ -498,6 +499,19 @@ func InitAllEnv() error { } } + if _, ok := envData[constants.EnvKeyDisableMobileBasicAuthentication]; !ok { + envData[constants.EnvKeyDisableMobileBasicAuthentication] = osDisableBasicAuthentication == "true" + } + if osDisableMobileBasicAuthentication != "" { + boolValue, err := strconv.ParseBool(osDisableMobileBasicAuthentication) + if err != nil { + return err + } + if boolValue != envData[constants.EnvKeyDisableMobileBasicAuthentication].(bool) { + envData[constants.EnvKeyDisableMobileBasicAuthentication] = boolValue + } + } + if _, ok := envData[constants.EnvKeyDisableEmailVerification]; !ok { envData[constants.EnvKeyDisableEmailVerification] = osDisableEmailVerification == "true" } diff --git a/server/env/persist_env.go b/server/env/persist_env.go index 2c6e917..a460dbb 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, constants.EnvKeyDisableMultiFactorAuthentication, constants.EnvKeyAdminCookieSecure, constants.EnvKeyAppCookieSecure: + case constants.EnvKeyIsProd, constants.EnvKeyDisableBasicAuthentication, constants.EnvKeyDisableMobileBasicAuthentication, constants.EnvKeyDisableEmailVerification, constants.EnvKeyDisableLoginPage, constants.EnvKeyDisableMagicLinkLogin, constants.EnvKeyDisableSignUp, constants.EnvKeyDisableRedisForEnv, constants.EnvKeyDisableStrongPassword, constants.EnvKeyIsEmailServiceEnabled, constants.EnvKeyEnforceMultiFactorAuthentication, constants.EnvKeyDisableMultiFactorAuthentication, constants.EnvKeyAdminCookieSecure, constants.EnvKeyAppCookieSecure: if envValueBool, err := strconv.ParseBool(envValue); err == nil { if value.(bool) != envValueBool { storeData[key] = envValueBool diff --git a/server/go.sum b/server/go.sum index ffe2596..316bcce 100644 --- a/server/go.sum +++ b/server/go.sum @@ -447,7 +447,6 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0= golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= @@ -524,8 +523,6 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b h1:uKO3Js8lXGjpjdc4J3rqs0/Ex5yDKUGfk43tTYWVLas= -golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -598,16 +595,12 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI= -golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -617,7 +610,6 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= diff --git a/server/graph/generated/generated.go b/server/graph/generated/generated.go index 7ada9f2..8ec60f1 100644 --- a/server/graph/generated/generated.go +++ b/server/graph/generated/generated.go @@ -156,35 +156,36 @@ type ComplexityRoot struct { } Mutation struct { - AddEmailTemplate func(childComplexity int, params model.AddEmailTemplateRequest) int - AddWebhook func(childComplexity int, params model.AddWebhookRequest) int - AdminLogin func(childComplexity int, params model.AdminLoginInput) int - AdminLogout func(childComplexity int) int - AdminSignup func(childComplexity int, params model.AdminSignupInput) int - DeleteEmailTemplate func(childComplexity int, params model.DeleteEmailTemplateRequest) int - DeleteUser func(childComplexity int, params model.DeleteUserInput) int - DeleteWebhook func(childComplexity int, params model.WebhookRequest) int - EnableAccess func(childComplexity int, param model.UpdateAccessInput) int - ForgotPassword func(childComplexity int, params model.ForgotPasswordInput) int - GenerateJwtKeys func(childComplexity int, params model.GenerateJWTKeysInput) int - InviteMembers func(childComplexity int, params model.InviteMemberInput) int - Login func(childComplexity int, params model.LoginInput) int - Logout func(childComplexity int) int - MagicLinkLogin func(childComplexity int, params model.MagicLinkLoginInput) int - ResendOtp func(childComplexity int, params model.ResendOTPRequest) int - ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int - ResetPassword func(childComplexity int, params model.ResetPasswordInput) int - Revoke func(childComplexity int, params model.OAuthRevokeInput) int - RevokeAccess func(childComplexity int, param model.UpdateAccessInput) int - Signup func(childComplexity int, params model.SignUpInput) int - TestEndpoint func(childComplexity int, params model.TestEndpointRequest) int - UpdateEmailTemplate func(childComplexity int, params model.UpdateEmailTemplateRequest) int - UpdateEnv func(childComplexity int, params model.UpdateEnvInput) int - UpdateProfile func(childComplexity int, params model.UpdateProfileInput) int - UpdateUser func(childComplexity int, params model.UpdateUserInput) int - UpdateWebhook func(childComplexity int, params model.UpdateWebhookRequest) int - VerifyEmail func(childComplexity int, params model.VerifyEmailInput) int - VerifyOtp func(childComplexity int, params model.VerifyOTPRequest) int + AddEmailTemplate func(childComplexity int, params model.AddEmailTemplateRequest) int + AddWebhook func(childComplexity int, params model.AddWebhookRequest) int + AdminLogin func(childComplexity int, params model.AdminLoginInput) int + AdminLogout func(childComplexity int) int + AdminSignup func(childComplexity int, params model.AdminSignupInput) int + DeleteEmailTemplate func(childComplexity int, params model.DeleteEmailTemplateRequest) int + DeleteUser func(childComplexity int, params model.DeleteUserInput) int + DeleteWebhook func(childComplexity int, params model.WebhookRequest) int + EnableAccess func(childComplexity int, param model.UpdateAccessInput) int + ForgotPassword func(childComplexity int, params model.ForgotPasswordInput) int + GenerateJwtKeys func(childComplexity int, params model.GenerateJWTKeysInput) int + InviteMembers func(childComplexity int, params model.InviteMemberInput) int + Login func(childComplexity int, params model.LoginInput) int + Logout func(childComplexity int) int + MagicLinkLogin func(childComplexity int, params model.MagicLinkLoginInput) int + MobileBasicAuthSignup func(childComplexity int, params *model.MobileBasicAuthSignUpUpInput) int + ResendOtp func(childComplexity int, params model.ResendOTPRequest) int + ResendVerifyEmail func(childComplexity int, params model.ResendVerifyEmailInput) int + ResetPassword func(childComplexity int, params model.ResetPasswordInput) int + Revoke func(childComplexity int, params model.OAuthRevokeInput) int + RevokeAccess func(childComplexity int, param model.UpdateAccessInput) int + Signup func(childComplexity int, params model.SignUpInput) int + TestEndpoint func(childComplexity int, params model.TestEndpointRequest) int + UpdateEmailTemplate func(childComplexity int, params model.UpdateEmailTemplateRequest) int + UpdateEnv func(childComplexity int, params model.UpdateEnvInput) int + UpdateProfile func(childComplexity int, params model.UpdateProfileInput) int + UpdateUser func(childComplexity int, params model.UpdateUserInput) int + UpdateWebhook func(childComplexity int, params model.UpdateWebhookRequest) int + VerifyEmail func(childComplexity int, params model.VerifyEmailInput) int + VerifyOtp func(childComplexity int, params model.VerifyOTPRequest) int } Pagination struct { @@ -300,6 +301,7 @@ type ComplexityRoot struct { type MutationResolver interface { Signup(ctx context.Context, params model.SignUpInput) (*model.AuthResponse, error) + MobileBasicAuthSignup(ctx context.Context, params *model.MobileBasicAuthSignUpUpInput) (*model.AuthResponse, error) Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, error) MagicLinkLogin(ctx context.Context, params model.MagicLinkLoginInput) (*model.Response, error) Logout(ctx context.Context) (*model.Response, error) @@ -1159,6 +1161,18 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.Mutation.MagicLinkLogin(childComplexity, args["params"].(model.MagicLinkLoginInput)), true + case "Mutation.mobile_basic_auth_signup": + if e.complexity.Mutation.MobileBasicAuthSignup == nil { + break + } + + args, err := ec.field_Mutation_mobile_basic_auth_signup_args(context.TODO(), rawArgs) + if err != nil { + return 0, false + } + + return e.complexity.Mutation.MobileBasicAuthSignup(childComplexity, args["params"].(*model.MobileBasicAuthSignUpUpInput)), true + case "Mutation.resend_otp": if e.complexity.Mutation.ResendOtp == nil { break @@ -1884,6 +1898,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ec.unmarshalInputListWebhookLogRequest, ec.unmarshalInputLoginInput, ec.unmarshalInputMagicLinkLoginInput, + ec.unmarshalInputMobileBasicAuthSignUpUpInput, ec.unmarshalInputOAuthRevokeInput, ec.unmarshalInputPaginatedInput, ec.unmarshalInputPaginationInput, @@ -1971,525 +1986,552 @@ scalar Map scalar Any type Pagination { - limit: Int64! - page: Int64! - offset: Int64! - total: Int64! + limit: Int64! + page: Int64! + offset: Int64! + total: Int64! } type Meta { - version: String! - client_id: String! - is_google_login_enabled: Boolean! - is_facebook_login_enabled: Boolean! - is_github_login_enabled: Boolean! - is_linkedin_login_enabled: Boolean! - is_apple_login_enabled: Boolean! - is_twitter_login_enabled: Boolean! - is_email_verification_enabled: Boolean! - is_basic_authentication_enabled: Boolean! - is_magic_link_login_enabled: Boolean! - is_sign_up_enabled: Boolean! - is_strong_password_enabled: Boolean! - is_multi_factor_auth_enabled: Boolean! + version: String! + client_id: String! + is_google_login_enabled: Boolean! + is_facebook_login_enabled: Boolean! + is_github_login_enabled: Boolean! + is_linkedin_login_enabled: Boolean! + is_apple_login_enabled: Boolean! + is_twitter_login_enabled: Boolean! + is_email_verification_enabled: Boolean! + is_basic_authentication_enabled: Boolean! + is_magic_link_login_enabled: Boolean! + is_sign_up_enabled: Boolean! + is_strong_password_enabled: Boolean! + is_multi_factor_auth_enabled: Boolean! } type User { - id: ID! - email: String! - email_verified: Boolean! - signup_methods: String! - given_name: String - family_name: String - middle_name: String - nickname: String - # defaults to email - preferred_username: String - gender: String - birthdate: String - phone_number: String - phone_number_verified: Boolean - picture: String - roles: [String!]! - created_at: Int64 - updated_at: Int64 - revoked_timestamp: Int64 - is_multi_factor_auth_enabled: Boolean + id: ID! + email: String! + email_verified: Boolean! + signup_methods: String! + given_name: String + family_name: String + middle_name: String + nickname: String + # defaults to email + preferred_username: String + gender: String + birthdate: String + phone_number: String + phone_number_verified: Boolean + picture: String + roles: [String!]! + created_at: Int64 + updated_at: Int64 + revoked_timestamp: Int64 + is_multi_factor_auth_enabled: Boolean } type Users { - pagination: Pagination! - users: [User!]! + pagination: Pagination! + users: [User!]! } type VerificationRequest { - id: ID! - identifier: String - token: String - email: String - expires: Int64 - created_at: Int64 - updated_at: Int64 - nonce: String - redirect_uri: String + id: ID! + identifier: String + token: String + email: String + expires: Int64 + created_at: Int64 + updated_at: Int64 + nonce: String + redirect_uri: String } type VerificationRequests { - pagination: Pagination! - verification_requests: [VerificationRequest!]! + pagination: Pagination! + verification_requests: [VerificationRequest!]! } type Error { - message: String! - reason: String! + message: String! + reason: String! } type AuthResponse { - message: String! - should_show_otp_screen: Boolean - access_token: String - id_token: String - refresh_token: String - expires_in: Int64 - user: User + message: String! + should_show_otp_screen: Boolean + access_token: String + id_token: String + refresh_token: String + expires_in: Int64 + user: User } type Response { - message: String! + message: String! } type Env { - ACCESS_TOKEN_EXPIRY_TIME: String - ADMIN_SECRET: String - DATABASE_NAME: String - DATABASE_URL: String - DATABASE_TYPE: String - DATABASE_USERNAME: String - DATABASE_PASSWORD: String - DATABASE_HOST: String - DATABASE_PORT: String - CLIENT_ID: String! - CLIENT_SECRET: String! - CUSTOM_ACCESS_TOKEN_SCRIPT: String - SMTP_HOST: String - SMTP_PORT: String - SMTP_USERNAME: String - SMTP_PASSWORD: String - SMTP_LOCAL_NAME: String - SENDER_EMAIL: String - JWT_TYPE: String - JWT_SECRET: String - JWT_PRIVATE_KEY: String - JWT_PUBLIC_KEY: String - ALLOWED_ORIGINS: [String!] - APP_URL: String - REDIS_URL: String - RESET_PASSWORD_URL: String - DISABLE_EMAIL_VERIFICATION: Boolean! - DISABLE_BASIC_AUTHENTICATION: Boolean! - DISABLE_MAGIC_LINK_LOGIN: Boolean! - DISABLE_LOGIN_PAGE: Boolean! - 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!] - DEFAULT_ROLES: [String!] - JWT_ROLE_CLAIM: String - GOOGLE_CLIENT_ID: String - GOOGLE_CLIENT_SECRET: String - GITHUB_CLIENT_ID: String - GITHUB_CLIENT_SECRET: String - FACEBOOK_CLIENT_ID: String - FACEBOOK_CLIENT_SECRET: String - LINKEDIN_CLIENT_ID: String - LINKEDIN_CLIENT_SECRET: String - APPLE_CLIENT_ID: String - APPLE_CLIENT_SECRET: String - TWITTER_CLIENT_ID: String - TWITTER_CLIENT_SECRET: String - ORGANIZATION_NAME: String - ORGANIZATION_LOGO: String - APP_COOKIE_SECURE: Boolean! - ADMIN_COOKIE_SECURE: Boolean! + ACCESS_TOKEN_EXPIRY_TIME: String + ADMIN_SECRET: String + DATABASE_NAME: String + DATABASE_URL: String + DATABASE_TYPE: String + DATABASE_USERNAME: String + DATABASE_PASSWORD: String + DATABASE_HOST: String + DATABASE_PORT: String + CLIENT_ID: String! + CLIENT_SECRET: String! + CUSTOM_ACCESS_TOKEN_SCRIPT: String + SMTP_HOST: String + SMTP_PORT: String + SMTP_USERNAME: String + SMTP_PASSWORD: String + SMTP_LOCAL_NAME: String + SENDER_EMAIL: String + JWT_TYPE: String + JWT_SECRET: String + JWT_PRIVATE_KEY: String + JWT_PUBLIC_KEY: String + ALLOWED_ORIGINS: [String!] + APP_URL: String + REDIS_URL: String + RESET_PASSWORD_URL: String + DISABLE_EMAIL_VERIFICATION: Boolean! + DISABLE_BASIC_AUTHENTICATION: Boolean! + DISABLE_MAGIC_LINK_LOGIN: Boolean! + DISABLE_LOGIN_PAGE: Boolean! + 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!] + DEFAULT_ROLES: [String!] + JWT_ROLE_CLAIM: String + GOOGLE_CLIENT_ID: String + GOOGLE_CLIENT_SECRET: String + GITHUB_CLIENT_ID: String + GITHUB_CLIENT_SECRET: String + FACEBOOK_CLIENT_ID: String + FACEBOOK_CLIENT_SECRET: String + LINKEDIN_CLIENT_ID: String + LINKEDIN_CLIENT_SECRET: String + APPLE_CLIENT_ID: String + APPLE_CLIENT_SECRET: String + TWITTER_CLIENT_ID: String + TWITTER_CLIENT_SECRET: String + ORGANIZATION_NAME: String + ORGANIZATION_LOGO: String + APP_COOKIE_SECURE: Boolean! + ADMIN_COOKIE_SECURE: Boolean! } type ValidateJWTTokenResponse { - is_valid: Boolean! - claims: Map + is_valid: Boolean! + claims: Map } type GenerateJWTKeysResponse { - secret: String - public_key: String - private_key: String + secret: String + public_key: String + private_key: String } type Webhook { - id: ID! - event_name: String - endpoint: String - enabled: Boolean - headers: Map - created_at: Int64 - updated_at: Int64 + id: ID! + event_name: String + endpoint: String + enabled: Boolean + headers: Map + created_at: Int64 + updated_at: Int64 } type Webhooks { - pagination: Pagination! - webhooks: [Webhook!]! + pagination: Pagination! + webhooks: [Webhook!]! } type WebhookLog { - id: ID! - http_status: Int64 - response: String - request: String - webhook_id: ID - created_at: Int64 - updated_at: Int64 + id: ID! + http_status: Int64 + response: String + request: String + webhook_id: ID + created_at: Int64 + updated_at: Int64 } type TestEndpointResponse { - http_status: Int64 - response: String + http_status: Int64 + response: String } type WebhookLogs { - pagination: Pagination! - webhook_logs: [WebhookLog!]! + pagination: Pagination! + webhook_logs: [WebhookLog!]! } type EmailTemplate { - id: ID! - event_name: String! - template: String! - design: String! - subject: String! - created_at: Int64 - updated_at: Int64 + id: ID! + event_name: String! + template: String! + design: String! + subject: String! + created_at: Int64 + updated_at: Int64 } type EmailTemplates { - pagination: Pagination! - email_templates: [EmailTemplate!]! + pagination: Pagination! + email_templates: [EmailTemplate!]! } input UpdateEnvInput { - ACCESS_TOKEN_EXPIRY_TIME: String - ADMIN_SECRET: String - CUSTOM_ACCESS_TOKEN_SCRIPT: String - OLD_ADMIN_SECRET: String - SMTP_HOST: String - SMTP_PORT: String - SMTP_USERNAME: String - SMTP_PASSWORD: String - SMTP_LOCAL_NAME: String - SENDER_EMAIL: String - JWT_TYPE: String - JWT_SECRET: String - JWT_PRIVATE_KEY: String - JWT_PUBLIC_KEY: String - ALLOWED_ORIGINS: [String!] - APP_URL: String - RESET_PASSWORD_URL: String - APP_COOKIE_SECURE: Boolean - ADMIN_COOKIE_SECURE: Boolean - DISABLE_EMAIL_VERIFICATION: Boolean - DISABLE_BASIC_AUTHENTICATION: Boolean - DISABLE_MAGIC_LINK_LOGIN: Boolean - DISABLE_LOGIN_PAGE: Boolean - 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!] - DEFAULT_ROLES: [String!] - JWT_ROLE_CLAIM: String - GOOGLE_CLIENT_ID: String - GOOGLE_CLIENT_SECRET: String - GITHUB_CLIENT_ID: String - GITHUB_CLIENT_SECRET: String - FACEBOOK_CLIENT_ID: String - FACEBOOK_CLIENT_SECRET: String - LINKEDIN_CLIENT_ID: String - LINKEDIN_CLIENT_SECRET: String - APPLE_CLIENT_ID: String - APPLE_CLIENT_SECRET: String - TWITTER_CLIENT_ID: String - TWITTER_CLIENT_SECRET: String - ORGANIZATION_NAME: String - ORGANIZATION_LOGO: String + ACCESS_TOKEN_EXPIRY_TIME: String + ADMIN_SECRET: String + CUSTOM_ACCESS_TOKEN_SCRIPT: String + OLD_ADMIN_SECRET: String + SMTP_HOST: String + SMTP_PORT: String + SMTP_USERNAME: String + SMTP_PASSWORD: String + SMTP_LOCAL_NAME: String + SENDER_EMAIL: String + JWT_TYPE: String + JWT_SECRET: String + JWT_PRIVATE_KEY: String + JWT_PUBLIC_KEY: String + ALLOWED_ORIGINS: [String!] + APP_URL: String + RESET_PASSWORD_URL: String + APP_COOKIE_SECURE: Boolean + ADMIN_COOKIE_SECURE: Boolean + DISABLE_EMAIL_VERIFICATION: Boolean + DISABLE_BASIC_AUTHENTICATION: Boolean + DISABLE_MAGIC_LINK_LOGIN: Boolean + DISABLE_LOGIN_PAGE: Boolean + 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!] + DEFAULT_ROLES: [String!] + JWT_ROLE_CLAIM: String + GOOGLE_CLIENT_ID: String + GOOGLE_CLIENT_SECRET: String + GITHUB_CLIENT_ID: String + GITHUB_CLIENT_SECRET: String + FACEBOOK_CLIENT_ID: String + FACEBOOK_CLIENT_SECRET: String + LINKEDIN_CLIENT_ID: String + LINKEDIN_CLIENT_SECRET: String + APPLE_CLIENT_ID: String + APPLE_CLIENT_SECRET: String + TWITTER_CLIENT_ID: String + TWITTER_CLIENT_SECRET: String + ORGANIZATION_NAME: String + ORGANIZATION_LOGO: String } input AdminLoginInput { - admin_secret: String! + admin_secret: String! } input AdminSignupInput { - admin_secret: String! + admin_secret: String! +} + +input MobileBasicAuthSignUpUpInput { + email: String + given_name: String + family_name: String + middle_name: String + nickname: String + gender: String + birthdate: String + phone_number: String! + picture: String + password: String! + confirm_password: String! + roles: [String!] + scope: [String!] + redirect_uri: String + is_multi_factor_auth_enabled: Boolean + # 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 SignUpInput { - email: String! - given_name: String - family_name: String - middle_name: String - nickname: String - gender: String - birthdate: String - phone_number: String - picture: String - password: String! - confirm_password: String! - roles: [String!] - scope: [String!] - redirect_uri: String - is_multi_factor_auth_enabled: Boolean - # 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 + email: String! + given_name: String + family_name: String + middle_name: String + nickname: String + gender: String + birthdate: String + phone_number: String + picture: String + password: String! + confirm_password: String! + roles: [String!] + scope: [String!] + redirect_uri: String + is_multi_factor_auth_enabled: Boolean + # 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 LoginInput { - email: 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 + email: 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 { - token: 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 + token: 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 ResendVerifyEmailInput { - email: String! - identifier: 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 + email: String! + identifier: 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 UpdateProfileInput { - old_password: String - new_password: String - confirm_new_password: String - email: String - given_name: String - family_name: String - middle_name: String - nickname: String - gender: String - birthdate: String - phone_number: String - picture: String - is_multi_factor_auth_enabled: Boolean + old_password: String + new_password: String + confirm_new_password: String + email: String + given_name: String + family_name: String + middle_name: String + nickname: String + gender: String + birthdate: String + phone_number: String + picture: String + is_multi_factor_auth_enabled: Boolean } input UpdateUserInput { - id: ID! - email: String - email_verified: Boolean - given_name: String - family_name: String - middle_name: String - nickname: String - gender: String - birthdate: String - phone_number: String - picture: String - roles: [String] - is_multi_factor_auth_enabled: Boolean + id: ID! + email: String + email_verified: Boolean + given_name: String + family_name: String + middle_name: String + nickname: String + gender: String + birthdate: String + phone_number: String + picture: String + roles: [String] + is_multi_factor_auth_enabled: Boolean } input ForgotPasswordInput { - email: String! - state: String - redirect_uri: String + email: String! + state: String + redirect_uri: String } input ResetPasswordInput { - token: String! - password: String! - confirm_password: String! + token: String! + password: String! + confirm_password: String! } input DeleteUserInput { - email: String! + email: String! } input MagicLinkLoginInput { - email: String! - roles: [String!] - scope: [String!] - state: String - redirect_uri: String + email: String! + roles: [String!] + scope: [String!] + state: String + redirect_uri: String } input SessionQueryInput { - roles: [String!] - scope: [String!] + roles: [String!] + scope: [String!] } input PaginationInput { - limit: Int64 - page: Int64 + limit: Int64 + page: Int64 } input PaginatedInput { - pagination: PaginationInput + pagination: PaginationInput } input OAuthRevokeInput { - refresh_token: String! + refresh_token: String! } input InviteMemberInput { - emails: [String!]! - redirect_uri: String + emails: [String!]! + redirect_uri: String } input UpdateAccessInput { - user_id: String! + user_id: String! } input ValidateJWTTokenInput { - token_type: String! - token: String! - roles: [String!] + token_type: String! + token: String! + roles: [String!] } input GenerateJWTKeysInput { - type: String! + type: String! } input ListWebhookLogRequest { - pagination: PaginationInput - webhook_id: String + pagination: PaginationInput + webhook_id: String } input AddWebhookRequest { - event_name: String! - endpoint: String! - enabled: Boolean! - headers: Map + event_name: String! + endpoint: String! + enabled: Boolean! + headers: Map } input UpdateWebhookRequest { - id: ID! - event_name: String - endpoint: String - enabled: Boolean - headers: Map + id: ID! + event_name: String + endpoint: String + enabled: Boolean + headers: Map } input WebhookRequest { - id: ID! + id: ID! } input TestEndpointRequest { - endpoint: String! - event_name: String! - headers: Map + endpoint: String! + event_name: String! + headers: Map } input AddEmailTemplateRequest { - event_name: String! - subject: String! - template: String! - design: String + event_name: String! + subject: String! + template: String! + # Design value is set when editor is used + # If raw HTML is used design value is set to null + design: String } input UpdateEmailTemplateRequest { - id: ID! - event_name: String - template: String - subject: String - design: String + id: ID! + event_name: String + template: String + subject: String + # Design value is set when editor is used + # If raw HTML is used design value is set to null + design: String } input DeleteEmailTemplateRequest { - id: ID! + id: ID! } input VerifyOTPRequest { - email: String! - otp: 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 + email: String! + otp: 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 ResendOTPRequest { - email: 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 + email: 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 } type Mutation { - signup(params: SignUpInput!): AuthResponse! - login(params: LoginInput!): AuthResponse! - magic_link_login(params: MagicLinkLoginInput!): Response! - logout: Response! - update_profile(params: UpdateProfileInput!): Response! - verify_email(params: VerifyEmailInput!): AuthResponse! - resend_verify_email(params: ResendVerifyEmailInput!): Response! - forgot_password(params: ForgotPasswordInput!): Response! - reset_password(params: ResetPasswordInput!): Response! - revoke(params: OAuthRevokeInput!): Response! - verify_otp(params: VerifyOTPRequest!): AuthResponse! - resend_otp(params: ResendOTPRequest!): Response! - # admin only apis - _delete_user(params: DeleteUserInput!): Response! - _update_user(params: UpdateUserInput!): User! - _admin_signup(params: AdminSignupInput!): Response! - _admin_login(params: AdminLoginInput!): Response! - _admin_logout: Response! - _update_env(params: UpdateEnvInput!): Response! - _invite_members(params: InviteMemberInput!): Response! - _revoke_access(param: UpdateAccessInput!): Response! - _enable_access(param: UpdateAccessInput!): Response! - _generate_jwt_keys(params: GenerateJWTKeysInput!): GenerateJWTKeysResponse! - _add_webhook(params: AddWebhookRequest!): Response! - _update_webhook(params: UpdateWebhookRequest!): Response! - _delete_webhook(params: WebhookRequest!): Response! - _test_endpoint(params: TestEndpointRequest!): TestEndpointResponse! - _add_email_template(params: AddEmailTemplateRequest!): Response! - _update_email_template(params: UpdateEmailTemplateRequest!): Response! - _delete_email_template(params: DeleteEmailTemplateRequest!): Response! + signup(params: SignUpInput!): AuthResponse! + mobile_basic_auth_signup(params: MobileBasicAuthSignUpUpInput): AuthResponse! + login(params: LoginInput!): AuthResponse! + magic_link_login(params: MagicLinkLoginInput!): Response! + logout: Response! + update_profile(params: UpdateProfileInput!): Response! + verify_email(params: VerifyEmailInput!): AuthResponse! + resend_verify_email(params: ResendVerifyEmailInput!): Response! + forgot_password(params: ForgotPasswordInput!): Response! + reset_password(params: ResetPasswordInput!): Response! + revoke(params: OAuthRevokeInput!): Response! + verify_otp(params: VerifyOTPRequest!): AuthResponse! + resend_otp(params: ResendOTPRequest!): Response! + # admin only apis + _delete_user(params: DeleteUserInput!): Response! + _update_user(params: UpdateUserInput!): User! + _admin_signup(params: AdminSignupInput!): Response! + _admin_login(params: AdminLoginInput!): Response! + _admin_logout: Response! + _update_env(params: UpdateEnvInput!): Response! + _invite_members(params: InviteMemberInput!): Response! + _revoke_access(param: UpdateAccessInput!): Response! + _enable_access(param: UpdateAccessInput!): Response! + _generate_jwt_keys(params: GenerateJWTKeysInput!): GenerateJWTKeysResponse! + _add_webhook(params: AddWebhookRequest!): Response! + _update_webhook(params: UpdateWebhookRequest!): Response! + _delete_webhook(params: WebhookRequest!): Response! + _test_endpoint(params: TestEndpointRequest!): TestEndpointResponse! + _add_email_template(params: AddEmailTemplateRequest!): Response! + _update_email_template(params: UpdateEmailTemplateRequest!): Response! + _delete_email_template(params: DeleteEmailTemplateRequest!): Response! } type Query { - meta: Meta! - session(params: SessionQueryInput): AuthResponse! - profile: User! - validate_jwt_token(params: ValidateJWTTokenInput!): ValidateJWTTokenResponse! - # admin only apis - _users(params: PaginatedInput): Users! - _verification_requests(params: PaginatedInput): VerificationRequests! - _admin_session: Response! - _env: Env! - _webhook(params: WebhookRequest!): Webhook! - _webhooks(params: PaginatedInput): Webhooks! - _webhook_logs(params: ListWebhookLogRequest): WebhookLogs! - _email_templates(params: PaginatedInput): EmailTemplates! + meta: Meta! + session(params: SessionQueryInput): AuthResponse! + profile: User! + validate_jwt_token(params: ValidateJWTTokenInput!): ValidateJWTTokenResponse! + # admin only apis + _users(params: PaginatedInput): Users! + _verification_requests(params: PaginatedInput): VerificationRequests! + _admin_session: Response! + _env: Env! + _webhook(params: WebhookRequest!): Webhook! + _webhooks(params: PaginatedInput): Webhooks! + _webhook_logs(params: ListWebhookLogRequest): WebhookLogs! + _email_templates(params: PaginatedInput): EmailTemplates! } `, BuiltIn: false}, } @@ -2784,6 +2826,21 @@ func (ec *executionContext) field_Mutation_magic_link_login_args(ctx context.Con return args, nil } +func (ec *executionContext) field_Mutation_mobile_basic_auth_signup_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { + var err error + args := map[string]interface{}{} + var arg0 *model.MobileBasicAuthSignUpUpInput + if tmp, ok := rawArgs["params"]; ok { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("params")) + arg0, err = ec.unmarshalOMobileBasicAuthSignUpUpInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐMobileBasicAuthSignUpUpInput(ctx, tmp) + if err != nil { + return nil, err + } + } + args["params"] = arg0 + return args, nil +} + func (ec *executionContext) field_Mutation_resend_otp_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} @@ -7015,6 +7072,77 @@ func (ec *executionContext) fieldContext_Mutation_signup(ctx context.Context, fi return fc, nil } +func (ec *executionContext) _Mutation_mobile_basic_auth_signup(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_Mutation_mobile_basic_auth_signup(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().MobileBasicAuthSignup(rctx, fc.Args["params"].(*model.MobileBasicAuthSignUpUpInput)) + }) + 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_basic_auth_signup(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_basic_auth_signup_args(ctx, field.ArgumentMap(ec.Variables)); err != nil { + ec.Error(ctx, err) + return + } + return fc, nil +} + func (ec *executionContext) _Mutation_login(ctx context.Context, field graphql.CollectedField) (ret graphql.Marshaler) { fc, err := ec.fieldContext_Mutation_login(ctx, field) if err != nil { @@ -14592,6 +14720,154 @@ func (ec *executionContext) unmarshalInputMagicLinkLoginInput(ctx context.Contex return it, nil } +func (ec *executionContext) unmarshalInputMobileBasicAuthSignUpUpInput(ctx context.Context, obj interface{}) (model.MobileBasicAuthSignUpUpInput, error) { + var it model.MobileBasicAuthSignUpUpInput + asMap := map[string]interface{}{} + for k, v := range obj.(map[string]interface{}) { + asMap[k] = v + } + + fieldsInOrder := [...]string{"email", "given_name", "family_name", "middle_name", "nickname", "gender", "birthdate", "phone_number", "picture", "password", "confirm_password", "roles", "scope", "redirect_uri", "is_multi_factor_auth_enabled", "state"} + for _, k := range fieldsInOrder { + v, ok := asMap[k] + if !ok { + continue + } + switch k { + case "email": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("email")) + it.Email, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "given_name": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("given_name")) + it.GivenName, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "family_name": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("family_name")) + it.FamilyName, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "middle_name": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("middle_name")) + it.MiddleName, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "nickname": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nickname")) + it.Nickname, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "gender": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("gender")) + it.Gender, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + case "birthdate": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("birthdate")) + it.Birthdate, err = ec.unmarshalOString2ᚖstring(ctx, v) + if err != nil { + return it, err + } + 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 "picture": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("picture")) + it.Picture, err = ec.unmarshalOString2ᚖstring(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 "confirm_password": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("confirm_password")) + it.ConfirmPassword, 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 "redirect_uri": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("redirect_uri")) + it.RedirectURI, err = ec.unmarshalOString2ᚖstring(ctx, v) + 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 + } + 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) unmarshalInputOAuthRevokeInput(ctx context.Context, obj interface{}) (model.OAuthRevokeInput, error) { var it model.OAuthRevokeInput asMap := map[string]interface{}{} @@ -16623,6 +16899,15 @@ func (ec *executionContext) _Mutation(ctx context.Context, sel ast.SelectionSet) return ec._Mutation_signup(ctx, field) }) + if out.Values[i] == graphql.Null { + invalids++ + } + case "mobile_basic_auth_signup": + + out.Values[i] = ec.OperationContext.RootResolverMiddleware(innerCtx, func(ctx context.Context) (res graphql.Marshaler) { + return ec._Mutation_mobile_basic_auth_signup(ctx, field) + }) + if out.Values[i] == graphql.Null { invalids++ } @@ -19097,6 +19382,14 @@ func (ec *executionContext) marshalOMap2map(ctx context.Context, sel ast.Selecti return res } +func (ec *executionContext) unmarshalOMobileBasicAuthSignUpUpInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐMobileBasicAuthSignUpUpInput(ctx context.Context, v interface{}) (*model.MobileBasicAuthSignUpUpInput, error) { + if v == nil { + return nil, nil + } + res, err := ec.unmarshalInputMobileBasicAuthSignUpUpInput(ctx, v) + return &res, graphql.ErrorOnPath(ctx, err) +} + func (ec *executionContext) unmarshalOPaginatedInput2ᚖgithubᚗcomᚋauthorizerdevᚋauthorizerᚋserverᚋgraphᚋmodelᚐPaginatedInput(ctx context.Context, v interface{}) (*model.PaginatedInput, error) { if v == nil { return nil, nil diff --git a/server/graph/model/models_gen.go b/server/graph/model/models_gen.go index ce44f87..6cffad0 100644 --- a/server/graph/model/models_gen.go +++ b/server/graph/model/models_gen.go @@ -179,6 +179,25 @@ type Meta struct { IsMultiFactorAuthEnabled bool `json:"is_multi_factor_auth_enabled"` } +type MobileBasicAuthSignUpUpInput 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"` + IsMultiFactorAuthEnabled *bool `json:"is_multi_factor_auth_enabled"` + State *string `json:"state"` +} + type OAuthRevokeInput struct { RefreshToken string `json:"refresh_token"` } diff --git a/server/graph/schema.graphqls b/server/graph/schema.graphqls index 457b6b0..2fb8d95 100644 --- a/server/graph/schema.graphqls +++ b/server/graph/schema.graphqls @@ -6,527 +6,550 @@ scalar Map scalar Any type Pagination { - limit: Int64! - page: Int64! - offset: Int64! - total: Int64! + limit: Int64! + page: Int64! + offset: Int64! + total: Int64! } type Meta { - version: String! - client_id: String! - is_google_login_enabled: Boolean! - is_facebook_login_enabled: Boolean! - is_github_login_enabled: Boolean! - is_linkedin_login_enabled: Boolean! - is_apple_login_enabled: Boolean! - is_twitter_login_enabled: Boolean! - is_email_verification_enabled: Boolean! - is_basic_authentication_enabled: Boolean! - is_magic_link_login_enabled: Boolean! - is_sign_up_enabled: Boolean! - is_strong_password_enabled: Boolean! - is_multi_factor_auth_enabled: Boolean! + version: String! + client_id: String! + is_google_login_enabled: Boolean! + is_facebook_login_enabled: Boolean! + is_github_login_enabled: Boolean! + is_linkedin_login_enabled: Boolean! + is_apple_login_enabled: Boolean! + is_twitter_login_enabled: Boolean! + is_email_verification_enabled: Boolean! + is_basic_authentication_enabled: Boolean! + is_magic_link_login_enabled: Boolean! + is_sign_up_enabled: Boolean! + is_strong_password_enabled: Boolean! + is_multi_factor_auth_enabled: Boolean! } type User { - id: ID! - email: String! - email_verified: Boolean! - signup_methods: String! - given_name: String - family_name: String - middle_name: String - nickname: String - # defaults to email - preferred_username: String - gender: String - birthdate: String - phone_number: String - phone_number_verified: Boolean - picture: String - roles: [String!]! - created_at: Int64 - updated_at: Int64 - revoked_timestamp: Int64 - is_multi_factor_auth_enabled: Boolean + id: ID! + email: String! + email_verified: Boolean! + signup_methods: String! + given_name: String + family_name: String + middle_name: String + nickname: String + # defaults to email + preferred_username: String + gender: String + birthdate: String + phone_number: String + phone_number_verified: Boolean + picture: String + roles: [String!]! + created_at: Int64 + updated_at: Int64 + revoked_timestamp: Int64 + is_multi_factor_auth_enabled: Boolean } type Users { - pagination: Pagination! - users: [User!]! + pagination: Pagination! + users: [User!]! } type VerificationRequest { - id: ID! - identifier: String - token: String - email: String - expires: Int64 - created_at: Int64 - updated_at: Int64 - nonce: String - redirect_uri: String + id: ID! + identifier: String + token: String + email: String + expires: Int64 + created_at: Int64 + updated_at: Int64 + nonce: String + redirect_uri: String } type VerificationRequests { - pagination: Pagination! - verification_requests: [VerificationRequest!]! + pagination: Pagination! + verification_requests: [VerificationRequest!]! } type Error { - message: String! - reason: String! + message: String! + reason: String! } type AuthResponse { - message: String! - should_show_otp_screen: Boolean - access_token: String - id_token: String - refresh_token: String - expires_in: Int64 - user: User + message: String! + should_show_otp_screen: Boolean + access_token: String + id_token: String + refresh_token: String + expires_in: Int64 + user: User } type Response { - message: String! + message: String! } type Env { - ACCESS_TOKEN_EXPIRY_TIME: String - ADMIN_SECRET: String - DATABASE_NAME: String - DATABASE_URL: String - DATABASE_TYPE: String - DATABASE_USERNAME: String - DATABASE_PASSWORD: String - DATABASE_HOST: String - DATABASE_PORT: String - CLIENT_ID: String! - CLIENT_SECRET: String! - CUSTOM_ACCESS_TOKEN_SCRIPT: String - SMTP_HOST: String - SMTP_PORT: String - SMTP_USERNAME: String - SMTP_PASSWORD: String - SMTP_LOCAL_NAME: String - SENDER_EMAIL: String - JWT_TYPE: String - JWT_SECRET: String - JWT_PRIVATE_KEY: String - JWT_PUBLIC_KEY: String - ALLOWED_ORIGINS: [String!] - APP_URL: String - REDIS_URL: String - RESET_PASSWORD_URL: String - DISABLE_EMAIL_VERIFICATION: Boolean! - DISABLE_BASIC_AUTHENTICATION: Boolean! - DISABLE_MAGIC_LINK_LOGIN: Boolean! - DISABLE_LOGIN_PAGE: Boolean! - 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!] - DEFAULT_ROLES: [String!] - JWT_ROLE_CLAIM: String - GOOGLE_CLIENT_ID: String - GOOGLE_CLIENT_SECRET: String - GITHUB_CLIENT_ID: String - GITHUB_CLIENT_SECRET: String - FACEBOOK_CLIENT_ID: String - FACEBOOK_CLIENT_SECRET: String - LINKEDIN_CLIENT_ID: String - LINKEDIN_CLIENT_SECRET: String - APPLE_CLIENT_ID: String - APPLE_CLIENT_SECRET: String - TWITTER_CLIENT_ID: String - TWITTER_CLIENT_SECRET: String - ORGANIZATION_NAME: String - ORGANIZATION_LOGO: String - APP_COOKIE_SECURE: Boolean! - ADMIN_COOKIE_SECURE: Boolean! + ACCESS_TOKEN_EXPIRY_TIME: String + ADMIN_SECRET: String + DATABASE_NAME: String + DATABASE_URL: String + DATABASE_TYPE: String + DATABASE_USERNAME: String + DATABASE_PASSWORD: String + DATABASE_HOST: String + DATABASE_PORT: String + CLIENT_ID: String! + CLIENT_SECRET: String! + CUSTOM_ACCESS_TOKEN_SCRIPT: String + SMTP_HOST: String + SMTP_PORT: String + SMTP_USERNAME: String + SMTP_PASSWORD: String + SMTP_LOCAL_NAME: String + SENDER_EMAIL: String + JWT_TYPE: String + JWT_SECRET: String + JWT_PRIVATE_KEY: String + JWT_PUBLIC_KEY: String + ALLOWED_ORIGINS: [String!] + APP_URL: String + REDIS_URL: String + RESET_PASSWORD_URL: String + DISABLE_EMAIL_VERIFICATION: Boolean! + DISABLE_BASIC_AUTHENTICATION: Boolean! + DISABLE_MAGIC_LINK_LOGIN: Boolean! + DISABLE_LOGIN_PAGE: Boolean! + 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!] + DEFAULT_ROLES: [String!] + JWT_ROLE_CLAIM: String + GOOGLE_CLIENT_ID: String + GOOGLE_CLIENT_SECRET: String + GITHUB_CLIENT_ID: String + GITHUB_CLIENT_SECRET: String + FACEBOOK_CLIENT_ID: String + FACEBOOK_CLIENT_SECRET: String + LINKEDIN_CLIENT_ID: String + LINKEDIN_CLIENT_SECRET: String + APPLE_CLIENT_ID: String + APPLE_CLIENT_SECRET: String + TWITTER_CLIENT_ID: String + TWITTER_CLIENT_SECRET: String + ORGANIZATION_NAME: String + ORGANIZATION_LOGO: String + APP_COOKIE_SECURE: Boolean! + ADMIN_COOKIE_SECURE: Boolean! } type ValidateJWTTokenResponse { - is_valid: Boolean! - claims: Map + is_valid: Boolean! + claims: Map } type GenerateJWTKeysResponse { - secret: String - public_key: String - private_key: String + secret: String + public_key: String + private_key: String } type Webhook { - id: ID! - event_name: String - endpoint: String - enabled: Boolean - headers: Map - created_at: Int64 - updated_at: Int64 + id: ID! + event_name: String + endpoint: String + enabled: Boolean + headers: Map + created_at: Int64 + updated_at: Int64 } type Webhooks { - pagination: Pagination! - webhooks: [Webhook!]! + pagination: Pagination! + webhooks: [Webhook!]! } type WebhookLog { - id: ID! - http_status: Int64 - response: String - request: String - webhook_id: ID - created_at: Int64 - updated_at: Int64 + id: ID! + http_status: Int64 + response: String + request: String + webhook_id: ID + created_at: Int64 + updated_at: Int64 } type TestEndpointResponse { - http_status: Int64 - response: String + http_status: Int64 + response: String } type WebhookLogs { - pagination: Pagination! - webhook_logs: [WebhookLog!]! + pagination: Pagination! + webhook_logs: [WebhookLog!]! } type EmailTemplate { - id: ID! - event_name: String! - template: String! - design: String! - subject: String! - created_at: Int64 - updated_at: Int64 + id: ID! + event_name: String! + template: String! + design: String! + subject: String! + created_at: Int64 + updated_at: Int64 } type EmailTemplates { - pagination: Pagination! - email_templates: [EmailTemplate!]! + pagination: Pagination! + email_templates: [EmailTemplate!]! } input UpdateEnvInput { - ACCESS_TOKEN_EXPIRY_TIME: String - ADMIN_SECRET: String - CUSTOM_ACCESS_TOKEN_SCRIPT: String - OLD_ADMIN_SECRET: String - SMTP_HOST: String - SMTP_PORT: String - SMTP_USERNAME: String - SMTP_PASSWORD: String - SMTP_LOCAL_NAME: String - SENDER_EMAIL: String - JWT_TYPE: String - JWT_SECRET: String - JWT_PRIVATE_KEY: String - JWT_PUBLIC_KEY: String - ALLOWED_ORIGINS: [String!] - APP_URL: String - RESET_PASSWORD_URL: String - APP_COOKIE_SECURE: Boolean - ADMIN_COOKIE_SECURE: Boolean - DISABLE_EMAIL_VERIFICATION: Boolean - DISABLE_BASIC_AUTHENTICATION: Boolean - DISABLE_MAGIC_LINK_LOGIN: Boolean - DISABLE_LOGIN_PAGE: Boolean - 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!] - DEFAULT_ROLES: [String!] - JWT_ROLE_CLAIM: String - GOOGLE_CLIENT_ID: String - GOOGLE_CLIENT_SECRET: String - GITHUB_CLIENT_ID: String - GITHUB_CLIENT_SECRET: String - FACEBOOK_CLIENT_ID: String - FACEBOOK_CLIENT_SECRET: String - LINKEDIN_CLIENT_ID: String - LINKEDIN_CLIENT_SECRET: String - APPLE_CLIENT_ID: String - APPLE_CLIENT_SECRET: String - TWITTER_CLIENT_ID: String - TWITTER_CLIENT_SECRET: String - ORGANIZATION_NAME: String - ORGANIZATION_LOGO: String + ACCESS_TOKEN_EXPIRY_TIME: String + ADMIN_SECRET: String + CUSTOM_ACCESS_TOKEN_SCRIPT: String + OLD_ADMIN_SECRET: String + SMTP_HOST: String + SMTP_PORT: String + SMTP_USERNAME: String + SMTP_PASSWORD: String + SMTP_LOCAL_NAME: String + SENDER_EMAIL: String + JWT_TYPE: String + JWT_SECRET: String + JWT_PRIVATE_KEY: String + JWT_PUBLIC_KEY: String + ALLOWED_ORIGINS: [String!] + APP_URL: String + RESET_PASSWORD_URL: String + APP_COOKIE_SECURE: Boolean + ADMIN_COOKIE_SECURE: Boolean + DISABLE_EMAIL_VERIFICATION: Boolean + DISABLE_BASIC_AUTHENTICATION: Boolean + DISABLE_MAGIC_LINK_LOGIN: Boolean + DISABLE_LOGIN_PAGE: Boolean + 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!] + DEFAULT_ROLES: [String!] + JWT_ROLE_CLAIM: String + GOOGLE_CLIENT_ID: String + GOOGLE_CLIENT_SECRET: String + GITHUB_CLIENT_ID: String + GITHUB_CLIENT_SECRET: String + FACEBOOK_CLIENT_ID: String + FACEBOOK_CLIENT_SECRET: String + LINKEDIN_CLIENT_ID: String + LINKEDIN_CLIENT_SECRET: String + APPLE_CLIENT_ID: String + APPLE_CLIENT_SECRET: String + TWITTER_CLIENT_ID: String + TWITTER_CLIENT_SECRET: String + ORGANIZATION_NAME: String + ORGANIZATION_LOGO: String } input AdminLoginInput { - admin_secret: String! + admin_secret: String! } input AdminSignupInput { - admin_secret: String! + admin_secret: String! +} + +input MobileBasicAuthSignUpUpInput { + email: String + given_name: String + family_name: String + middle_name: String + nickname: String + gender: String + birthdate: String + phone_number: String! + picture: String + password: String! + confirm_password: String! + roles: [String!] + scope: [String!] + redirect_uri: String + is_multi_factor_auth_enabled: Boolean + # 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 SignUpInput { - email: String! - given_name: String - family_name: String - middle_name: String - nickname: String - gender: String - birthdate: String - phone_number: String - picture: String - password: String! - confirm_password: String! - roles: [String!] - scope: [String!] - redirect_uri: String - is_multi_factor_auth_enabled: Boolean - # 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 + email: String! + given_name: String + family_name: String + middle_name: String + nickname: String + gender: String + birthdate: String + phone_number: String + picture: String + password: String! + confirm_password: String! + roles: [String!] + scope: [String!] + redirect_uri: String + is_multi_factor_auth_enabled: Boolean + # 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 LoginInput { - email: 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 + email: 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 { - token: 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 + token: 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 ResendVerifyEmailInput { - email: String! - identifier: 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 + email: String! + identifier: 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 UpdateProfileInput { - old_password: String - new_password: String - confirm_new_password: String - email: String - given_name: String - family_name: String - middle_name: String - nickname: String - gender: String - birthdate: String - phone_number: String - picture: String - is_multi_factor_auth_enabled: Boolean + old_password: String + new_password: String + confirm_new_password: String + email: String + given_name: String + family_name: String + middle_name: String + nickname: String + gender: String + birthdate: String + phone_number: String + picture: String + is_multi_factor_auth_enabled: Boolean } input UpdateUserInput { - id: ID! - email: String - email_verified: Boolean - given_name: String - family_name: String - middle_name: String - nickname: String - gender: String - birthdate: String - phone_number: String - picture: String - roles: [String] - is_multi_factor_auth_enabled: Boolean + id: ID! + email: String + email_verified: Boolean + given_name: String + family_name: String + middle_name: String + nickname: String + gender: String + birthdate: String + phone_number: String + picture: String + roles: [String] + is_multi_factor_auth_enabled: Boolean } input ForgotPasswordInput { - email: String! - state: String - redirect_uri: String + email: String! + state: String + redirect_uri: String } input ResetPasswordInput { - token: String! - password: String! - confirm_password: String! + token: String! + password: String! + confirm_password: String! } input DeleteUserInput { - email: String! + email: String! } input MagicLinkLoginInput { - email: String! - roles: [String!] - scope: [String!] - state: String - redirect_uri: String + email: String! + roles: [String!] + scope: [String!] + state: String + redirect_uri: String } input SessionQueryInput { - roles: [String!] - scope: [String!] + roles: [String!] + scope: [String!] } input PaginationInput { - limit: Int64 - page: Int64 + limit: Int64 + page: Int64 } input PaginatedInput { - pagination: PaginationInput + pagination: PaginationInput } input OAuthRevokeInput { - refresh_token: String! + refresh_token: String! } input InviteMemberInput { - emails: [String!]! - redirect_uri: String + emails: [String!]! + redirect_uri: String } input UpdateAccessInput { - user_id: String! + user_id: String! } input ValidateJWTTokenInput { - token_type: String! - token: String! - roles: [String!] + token_type: String! + token: String! + roles: [String!] } input GenerateJWTKeysInput { - type: String! + type: String! } input ListWebhookLogRequest { - pagination: PaginationInput - webhook_id: String + pagination: PaginationInput + webhook_id: String } input AddWebhookRequest { - event_name: String! - endpoint: String! - enabled: Boolean! - headers: Map + event_name: String! + endpoint: String! + enabled: Boolean! + headers: Map } input UpdateWebhookRequest { - id: ID! - event_name: String - endpoint: String - enabled: Boolean - headers: Map + id: ID! + event_name: String + endpoint: String + enabled: Boolean + headers: Map } input WebhookRequest { - id: ID! + id: ID! } input TestEndpointRequest { - endpoint: String! - event_name: String! - headers: Map + endpoint: String! + event_name: String! + headers: Map } input AddEmailTemplateRequest { - event_name: String! - subject: String! - template: String! - # Design value is set when editor is used - # If raw HTML is used design value is set to null - design: String + event_name: String! + subject: String! + template: String! + # Design value is set when editor is used + # If raw HTML is used design value is set to null + design: String } input UpdateEmailTemplateRequest { - id: ID! - event_name: String - template: String - subject: String - # Design value is set when editor is used - # If raw HTML is used design value is set to null - design: String + id: ID! + event_name: String + template: String + subject: String + # Design value is set when editor is used + # If raw HTML is used design value is set to null + design: String } input DeleteEmailTemplateRequest { - id: ID! + id: ID! } input VerifyOTPRequest { - email: String! - otp: 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 + email: String! + otp: 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 ResendOTPRequest { - email: 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 + email: 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 } type Mutation { - signup(params: SignUpInput!): AuthResponse! - login(params: LoginInput!): AuthResponse! - magic_link_login(params: MagicLinkLoginInput!): Response! - logout: Response! - update_profile(params: UpdateProfileInput!): Response! - verify_email(params: VerifyEmailInput!): AuthResponse! - resend_verify_email(params: ResendVerifyEmailInput!): Response! - forgot_password(params: ForgotPasswordInput!): Response! - reset_password(params: ResetPasswordInput!): Response! - revoke(params: OAuthRevokeInput!): Response! - verify_otp(params: VerifyOTPRequest!): AuthResponse! - resend_otp(params: ResendOTPRequest!): Response! - # admin only apis - _delete_user(params: DeleteUserInput!): Response! - _update_user(params: UpdateUserInput!): User! - _admin_signup(params: AdminSignupInput!): Response! - _admin_login(params: AdminLoginInput!): Response! - _admin_logout: Response! - _update_env(params: UpdateEnvInput!): Response! - _invite_members(params: InviteMemberInput!): Response! - _revoke_access(param: UpdateAccessInput!): Response! - _enable_access(param: UpdateAccessInput!): Response! - _generate_jwt_keys(params: GenerateJWTKeysInput!): GenerateJWTKeysResponse! - _add_webhook(params: AddWebhookRequest!): Response! - _update_webhook(params: UpdateWebhookRequest!): Response! - _delete_webhook(params: WebhookRequest!): Response! - _test_endpoint(params: TestEndpointRequest!): TestEndpointResponse! - _add_email_template(params: AddEmailTemplateRequest!): Response! - _update_email_template(params: UpdateEmailTemplateRequest!): Response! - _delete_email_template(params: DeleteEmailTemplateRequest!): Response! + signup(params: SignUpInput!): AuthResponse! + mobile_basic_auth_signup(params: MobileBasicAuthSignUpUpInput): AuthResponse! + login(params: LoginInput!): AuthResponse! + magic_link_login(params: MagicLinkLoginInput!): Response! + logout: Response! + update_profile(params: UpdateProfileInput!): Response! + verify_email(params: VerifyEmailInput!): AuthResponse! + resend_verify_email(params: ResendVerifyEmailInput!): Response! + forgot_password(params: ForgotPasswordInput!): Response! + reset_password(params: ResetPasswordInput!): Response! + revoke(params: OAuthRevokeInput!): Response! + verify_otp(params: VerifyOTPRequest!): AuthResponse! + resend_otp(params: ResendOTPRequest!): Response! + # admin only apis + _delete_user(params: DeleteUserInput!): Response! + _update_user(params: UpdateUserInput!): User! + _admin_signup(params: AdminSignupInput!): Response! + _admin_login(params: AdminLoginInput!): Response! + _admin_logout: Response! + _update_env(params: UpdateEnvInput!): Response! + _invite_members(params: InviteMemberInput!): Response! + _revoke_access(param: UpdateAccessInput!): Response! + _enable_access(param: UpdateAccessInput!): Response! + _generate_jwt_keys(params: GenerateJWTKeysInput!): GenerateJWTKeysResponse! + _add_webhook(params: AddWebhookRequest!): Response! + _update_webhook(params: UpdateWebhookRequest!): Response! + _delete_webhook(params: WebhookRequest!): Response! + _test_endpoint(params: TestEndpointRequest!): TestEndpointResponse! + _add_email_template(params: AddEmailTemplateRequest!): Response! + _update_email_template(params: UpdateEmailTemplateRequest!): Response! + _delete_email_template(params: DeleteEmailTemplateRequest!): Response! } type Query { - meta: Meta! - session(params: SessionQueryInput): AuthResponse! - profile: User! - validate_jwt_token(params: ValidateJWTTokenInput!): ValidateJWTTokenResponse! - # admin only apis - _users(params: PaginatedInput): Users! - _verification_requests(params: PaginatedInput): VerificationRequests! - _admin_session: Response! - _env: Env! - _webhook(params: WebhookRequest!): Webhook! - _webhooks(params: PaginatedInput): Webhooks! - _webhook_logs(params: ListWebhookLogRequest): WebhookLogs! - _email_templates(params: PaginatedInput): EmailTemplates! + meta: Meta! + session(params: SessionQueryInput): AuthResponse! + profile: User! + validate_jwt_token(params: ValidateJWTTokenInput!): ValidateJWTTokenResponse! + # admin only apis + _users(params: PaginatedInput): Users! + _verification_requests(params: PaginatedInput): VerificationRequests! + _admin_session: Response! + _env: Env! + _webhook(params: WebhookRequest!): Webhook! + _webhooks(params: PaginatedInput): Webhooks! + _webhook_logs(params: ListWebhookLogRequest): WebhookLogs! + _email_templates(params: PaginatedInput): EmailTemplates! } diff --git a/server/graph/schema.resolvers.go b/server/graph/schema.resolvers.go index 33da289..4cbd9f0 100644 --- a/server/graph/schema.resolvers.go +++ b/server/graph/schema.resolvers.go @@ -16,6 +16,11 @@ func (r *mutationResolver) Signup(ctx context.Context, params model.SignUpInput) return resolvers.SignupResolver(ctx, params) } +// MobileBasicAuthSignup is the resolver for the mobile_basic_auth_signup field. +func (r *mutationResolver) MobileBasicAuthSignup(ctx context.Context, params *model.MobileBasicAuthSignUpUpInput) (*model.AuthResponse, error) { + return resolvers.MobileBasicAuthSignupResolver(ctx, params) +} + // Login is the resolver for the login field. func (r *mutationResolver) Login(ctx context.Context, params model.LoginInput) (*model.AuthResponse, error) { return resolvers.LoginResolver(ctx, params) diff --git a/server/memorystore/memory_store.go b/server/memorystore/memory_store.go index 15b7248..4285860 100644 --- a/server/memorystore/memory_store.go +++ b/server/memorystore/memory_store.go @@ -26,6 +26,7 @@ func InitMemStore() error { // boolean envs constants.EnvKeyDisableBasicAuthentication: false, + constants.EnvKeyDisableMobileBasicAuthentication: false, constants.EnvKeyDisableMagicLinkLogin: false, constants.EnvKeyDisableEmailVerification: false, constants.EnvKeyDisableLoginPage: false, diff --git a/server/memorystore/providers/redis/store.go b/server/memorystore/providers/redis/store.go index 7cb7b72..4e08f99 100644 --- a/server/memorystore/providers/redis/store.go +++ b/server/memorystore/providers/redis/store.go @@ -161,7 +161,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 || key == constants.EnvKeyDisableMultiFactorAuthentication || key == constants.EnvKeyAppCookieSecure || key == constants.EnvKeyAdminCookieSecure { + if key == constants.EnvKeyDisableBasicAuthentication || key == constants.EnvKeyDisableMobileBasicAuthentication || 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 || key == constants.EnvKeyAppCookieSecure || key == constants.EnvKeyAdminCookieSecure { boolValue, err := strconv.ParseBool(value) if err != nil { return res, err diff --git a/server/resolvers/mobile_basic_auth_signup.go b/server/resolvers/mobile_basic_auth_signup.go new file mode 100644 index 0000000..b626fae --- /dev/null +++ b/server/resolvers/mobile_basic_auth_signup.go @@ -0,0 +1,270 @@ +package resolvers + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/google/uuid" + log "github.com/sirupsen/logrus" + + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/cookie" + "github.com/authorizerdev/authorizer/server/crypto" + "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" +) + +// MobileBasicAuthSignupResolver is a resolver for mobile_basic_auth_signup mutation +func MobileBasicAuthSignupResolver(ctx context.Context, params *model.MobileBasicAuthSignUpUpInput) (*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 + } + + isSignupDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableSignUp) + if err != nil { + log.Debug("Error getting signup disabled: ", err) + isSignupDisabled = true + } + if isSignupDisabled { + log.Debug("Signup is disabled") + return res, fmt.Errorf(`signup is disabled for this instance`) + } + + isBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication) + if err != nil { + log.Debug("Error getting basic auth disabled: ", err) + isBasicAuthDisabled = true + } + + if isBasicAuthDisabled { + log.Debug("Mobile based Basic authentication is disabled") + return res, fmt.Errorf(`phone number based basic authentication is disabled for this instance`) + } + + if params.ConfirmPassword != params.Password { + log.Debug("Passwords do not match") + return res, fmt.Errorf(`password and confirm password does not match`) + } + + if err := validators.IsValidPassword(params.Password); err != nil { + log.Debug("Invalid password") + return res, err + } + + mobile := strings.TrimSpace(params.PhoneNumber) + if mobile == "" || len(mobile) < 10 { + log.Debug("Invalid phone number") + return res, fmt.Errorf("invalid phone number") + } + + emailInput := strings.ToLower(strings.TrimSpace(refs.StringValue(params.Email))) + + // if email is null set random dummy email for db constraint + + if emailInput != "" && !validators.IsValidEmail(emailInput) { + log.Debug("Invalid email: ", emailInput) + return res, fmt.Errorf(`invalid email address`) + } + + if emailInput == "" { + emailInput = mobile + "@authorizer.dev" + } + + log := log.WithFields(log.Fields{ + "email": emailInput, + "phone_number": mobile, + }) + // find user with email + existingUser, err := db.Provider.GetUserByPhoneNumber(ctx, mobile) + if err != nil { + log.Debug("Failed to get user by email: ", err) + } + + if existingUser != nil { + if existingUser.PhoneNumberVerifiedAt != nil { + // email is verified + log.Debug("Phone number is already verified and signed up.") + return res, fmt.Errorf(`%s has already signed up`, mobile) + } else if existingUser.ID != "" && existingUser.PhoneNumberVerifiedAt == nil { + log.Debug("Phone number is already signed up. Verification pending...") + return res, fmt.Errorf("%s has already signed up. please complete the phone number verification process or reset the password", mobile) + } + } + + inputRoles := []string{} + + if len(params.Roles) > 0 { + // check if roles exists + rolesString, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyRoles) + roles := []string{} + if err != nil { + log.Debug("Error getting roles: ", err) + return res, err + } else { + roles = strings.Split(rolesString, ",") + } + if !validators.IsValidRoles(params.Roles, roles) { + log.Debug("Invalid roles: ", params.Roles) + return res, fmt.Errorf(`invalid roles`) + } else { + inputRoles = params.Roles + } + } else { + inputRolesString, err := memorystore.Provider.GetStringStoreEnvVariable(constants.EnvKeyDefaultRoles) + if err != nil { + log.Debug("Error getting default roles: ", err) + return res, err + } else { + inputRoles = strings.Split(inputRolesString, ",") + } + } + + now := time.Now().Unix() + user := models.User{ + Email: emailInput, + PhoneNumber: &mobile, + PhoneNumberVerifiedAt: &now, + } + + user.Roles = strings.Join(inputRoles, ",") + + password, _ := crypto.EncryptPassword(params.Password) + user.Password = &password + + if params.GivenName != nil { + user.GivenName = params.GivenName + } + + if params.FamilyName != nil { + user.FamilyName = params.FamilyName + } + + if params.MiddleName != nil { + user.MiddleName = params.MiddleName + } + + if params.Nickname != nil { + user.Nickname = params.Nickname + } + + if params.Gender != nil { + user.Gender = params.Gender + } + + if params.Birthdate != nil { + user.Birthdate = params.Birthdate + } + + if params.Picture != nil { + user.Picture = params.Picture + } + + if params.IsMultiFactorAuthEnabled != nil { + 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.AuthRecipeMethodMobileBasicAuth + user, err = db.Provider.AddUser(ctx, user) + if err != nil { + log.Debug("Failed to add user: ", err) + return res, err + } + roles := strings.Split(user.Roles, ",") + userToReturn := user.AsAPIUser() + + scope := []string{"openid", "email", "profile"} + if params.Scope != nil && len(scope) > 0 { + scope = params.Scope + } + + 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.AuthRecipeMethodBasicAuth, nonce, code) + if err != nil { + log.Debug("Failed to create auth token: ", err) + return res, err + } + + // 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: `Signed up successfully.`, + AccessToken: &authToken.AccessToken.Token, + ExpiresIn: &expiresIn, + User: userToReturn, + } + + sessionKey := constants.AuthRecipeMethodBasicAuth + ":" + user.ID + cookie.SetSession(gc, authToken.FingerPrintHash) + memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeSessionToken+"_"+authToken.FingerPrint, authToken.FingerPrintHash) + memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeAccessToken+"_"+authToken.FingerPrint, authToken.AccessToken.Token) + + if authToken.RefreshToken != nil { + res.RefreshToken = &authToken.RefreshToken.Token + memorystore.Provider.SetUserSession(sessionKey, constants.TokenTypeRefreshToken+"_"+authToken.FingerPrint, authToken.RefreshToken.Token) + } + + go func() { + utils.RegisterEvent(ctx, constants.UserSignUpWebhookEvent, constants.AuthRecipeMethodBasicAuth, user) + db.Provider.AddSession(ctx, models.Session{ + UserID: user.ID, + UserAgent: utils.GetUserAgent(gc.Request), + IP: utils.GetIP(gc.Request), + }) + }() + + return res, nil +} diff --git a/server/resolvers/update_env.go b/server/resolvers/update_env.go index 44ad00b..76264a0 100644 --- a/server/resolvers/update_env.go +++ b/server/resolvers/update_env.go @@ -25,6 +25,7 @@ import ( // remove the session tokens for those methods func clearSessionIfRequired(currentData, updatedData map[string]interface{}) { isCurrentBasicAuthEnabled := !currentData[constants.EnvKeyDisableBasicAuthentication].(bool) + isCurrentMobileBasicAuthEnabled := !currentData[constants.EnvKeyDisableMobileBasicAuthentication].(bool) isCurrentMagicLinkLoginEnabled := !currentData[constants.EnvKeyDisableMagicLinkLogin].(bool) isCurrentAppleLoginEnabled := currentData[constants.EnvKeyAppleClientID] != nil && currentData[constants.EnvKeyAppleClientSecret] != nil && currentData[constants.EnvKeyAppleClientID].(string) != "" && currentData[constants.EnvKeyAppleClientSecret].(string) != "" isCurrentFacebookLoginEnabled := currentData[constants.EnvKeyFacebookClientID] != nil && currentData[constants.EnvKeyFacebookClientSecret] != nil && currentData[constants.EnvKeyFacebookClientID].(string) != "" && currentData[constants.EnvKeyFacebookClientSecret].(string) != "" @@ -34,6 +35,7 @@ func clearSessionIfRequired(currentData, updatedData map[string]interface{}) { isCurrentTwitterLoginEnabled := currentData[constants.EnvKeyTwitterClientID] != nil && currentData[constants.EnvKeyTwitterClientSecret] != nil && currentData[constants.EnvKeyTwitterClientID].(string) != "" && currentData[constants.EnvKeyTwitterClientSecret].(string) != "" isUpdatedBasicAuthEnabled := !updatedData[constants.EnvKeyDisableBasicAuthentication].(bool) + isUpdatedMobileBasicAuthEnabled := !updatedData[constants.EnvKeyDisableMobileBasicAuthentication].(bool) isUpdatedMagicLinkLoginEnabled := !updatedData[constants.EnvKeyDisableMagicLinkLogin].(bool) isUpdatedAppleLoginEnabled := updatedData[constants.EnvKeyAppleClientID] != nil && updatedData[constants.EnvKeyAppleClientSecret] != nil && updatedData[constants.EnvKeyAppleClientID].(string) != "" && updatedData[constants.EnvKeyAppleClientSecret].(string) != "" isUpdatedFacebookLoginEnabled := updatedData[constants.EnvKeyFacebookClientID] != nil && updatedData[constants.EnvKeyFacebookClientSecret] != nil && updatedData[constants.EnvKeyFacebookClientID].(string) != "" && updatedData[constants.EnvKeyFacebookClientSecret].(string) != "" @@ -46,6 +48,10 @@ func clearSessionIfRequired(currentData, updatedData map[string]interface{}) { memorystore.Provider.DeleteSessionForNamespace(constants.AuthRecipeMethodBasicAuth) } + if isCurrentMobileBasicAuthEnabled && !isUpdatedMobileBasicAuthEnabled { + memorystore.Provider.DeleteSessionForNamespace(constants.AuthRecipeMethodMobileBasicAuth) + } + if isCurrentMagicLinkLoginEnabled && !isUpdatedMagicLinkLoginEnabled { memorystore.Provider.DeleteSessionForNamespace(constants.AuthRecipeMethodMagicLinkLogin) } diff --git a/server/resolvers/update_profile.go b/server/resolvers/update_profile.go index 3263974..1d45b61 100644 --- a/server/resolvers/update_profile.go +++ b/server/resolvers/update_profile.go @@ -154,8 +154,14 @@ func UpdateProfileResolver(ctx context.Context, params model.UpdateProfileInput) isBasicAuthDisabled = true } + isMobileBasicAuthDisabled, err := memorystore.Provider.GetBoolStoreEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication) + if err != nil { + log.Debug("Error getting mobile basic auth disabled: ", err) + isBasicAuthDisabled = true + } + if params.NewPassword != nil && params.ConfirmNewPassword != nil { - if isBasicAuthDisabled { + if isBasicAuthDisabled || isMobileBasicAuthDisabled { log.Debug("Cannot update password as basic authentication is disabled") return res, fmt.Errorf(`basic authentication is disabled for this instance`) } diff --git a/server/test/mobile_basic_auth_signup_test.go b/server/test/mobile_basic_auth_signup_test.go new file mode 100644 index 0000000..17002e9 --- /dev/null +++ b/server/test/mobile_basic_auth_signup_test.go @@ -0,0 +1,82 @@ +package test + +import ( + "testing" + + "github.com/authorizerdev/authorizer/server/constants" + "github.com/authorizerdev/authorizer/server/graph/model" + "github.com/authorizerdev/authorizer/server/memorystore" + "github.com/authorizerdev/authorizer/server/refs" + "github.com/authorizerdev/authorizer/server/resolvers" + "github.com/stretchr/testify/assert" +) + +func mobileBasicAuthSingupTest(t *testing.T, s TestSetup) { + t.Helper() + t.Run(`should complete the signup with mobile and check duplicates`, func(t *testing.T) { + _, ctx := createContext(s) + email := "mobile_basic_auth_signup." + s.TestInfo.Email + res, err := resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{ + Email: refs.NewStringRef(email), + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password + "s", + }) + assert.NotNil(t, err, "invalid password") + assert.Nil(t, res) + + res, err = resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{ + Email: refs.NewStringRef(email), + Password: "test", + ConfirmPassword: "test", + }) + assert.NotNil(t, err, "invalid password") + + memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableSignUp, true) + res, err = resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{ + Email: refs.NewStringRef(email), + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + assert.NotNil(t, err, "singup disabled") + memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableSignUp, false) + + memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication, true) + res, err = resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{ + Email: refs.NewStringRef(email), + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + assert.NotNil(t, err, "singup disabled") + memorystore.Provider.UpdateEnvVariable(constants.EnvKeyDisableMobileBasicAuthentication, false) + + res, err = resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{ + PhoneNumber: " ", + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + assert.NotNil(t, err, "invalid mobile") + + res, err = resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{ + PhoneNumber: "test", + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + assert.NotNil(t, err, "invalid mobile") + + res, err = resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{ + PhoneNumber: "1234567890", + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + assert.NoError(t, err) + assert.NotEmpty(t, res.AccessToken) + assert.Equal(t, "1234567890@authorizer.dev", res.User.Email) + + res, err = resolvers.MobileBasicAuthSignupResolver(ctx, &model.MobileBasicAuthSignUpUpInput{ + PhoneNumber: "1234567890", + Password: s.TestInfo.Password, + ConfirmPassword: s.TestInfo.Password, + }) + assert.Error(t, err, "user exists") + }) +} diff --git a/server/test/resolvers_test.go b/server/test/resolvers_test.go index 4fb05ca..bd27c11 100644 --- a/server/test/resolvers_test.go +++ b/server/test/resolvers_test.go @@ -111,6 +111,7 @@ func TestResolvers(t *testing.T) { // user resolvers tests loginTests(t, s) signupTests(t, s) + mobileBasicAuthSingupTest(t, s) forgotPasswordTest(t, s) resendVerifyEmailTests(t, s) resetPasswordTest(t, s)