Skip to content

Commit 1eb8965

Browse files
committed
feat: add mobile based basic auth
1 parent 1c4e29f commit 1eb8965

File tree

25 files changed

+1624
-781
lines changed

25 files changed

+1624
-781
lines changed

server/constants/auth_methods.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package constants
33
const (
44
// AuthRecipeMethodBasicAuth is the basic_auth auth method
55
AuthRecipeMethodBasicAuth = "basic_auth"
6+
// AuthRecipeMethodMobileBasicAuth is the mobile basic_auth method, where user can signup using mobile number and password
7+
AuthRecipeMethodMobileBasicAuth = "mobile_basic_auth"
68
// AuthRecipeMethodMagicLinkLogin is the magic_link_login auth method
79
AuthRecipeMethodMagicLinkLogin = "magic_link_login"
810
// AuthRecipeMethodGoogle is the google auth method

server/constants/env.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ const (
125125
EnvKeyDisableEmailVerification = "DISABLE_EMAIL_VERIFICATION"
126126
// EnvKeyDisableBasicAuthentication key for env variable DISABLE_BASIC_AUTH
127127
EnvKeyDisableBasicAuthentication = "DISABLE_BASIC_AUTHENTICATION"
128+
// EnvKeyDisableBasicAuthentication key for env variable DISABLE_MOBILE_BASIC_AUTH
129+
EnvKeyDisableMobileBasicAuthentication = "DISABLE_MOBILE_BASIC_AUTHENTICATION"
128130
// EnvKeyDisableMagicLinkLogin key for env variable DISABLE_MAGIC_LINK_LOGIN
129131
EnvKeyDisableMagicLinkLogin = "DISABLE_MAGIC_LINK_LOGIN"
130132
// EnvKeyDisableLoginPage key for env variable DISABLE_LOGIN_PAGE

server/db/models/user.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ type User struct {
2525
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname" dynamo:"nickname"`
2626
Gender *string `json:"gender" bson:"gender" cql:"gender" dynamo:"gender"`
2727
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate" dynamo:"birthdate"`
28-
PhoneNumber *string `gorm:"index" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number"`
28+
PhoneNumber *string `gorm:"index" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number" index:"phone_number,hash"`
2929
PhoneNumberVerifiedAt *int64 `json:"phone_number_verified_at" bson:"phone_number_verified_at" cql:"phone_number_verified_at" dynamo:"phone_number_verified_at"`
3030
Picture *string `json:"picture" bson:"picture" cql:"picture" dynamo:"picture"`
3131
Roles string `json:"roles" bson:"roles" cql:"roles" dynamo:"roles"`

server/db/providers/arangodb/user.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/authorizerdev/authorizer/server/db/models"
1616
"github.com/authorizerdev/authorizer/server/graph/model"
1717
"github.com/authorizerdev/authorizer/server/memorystore"
18+
"github.com/authorizerdev/authorizer/server/refs"
1819
)
1920

2021
// AddUser to save user information in database
@@ -32,6 +33,12 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
3233
user.Roles = defaultRoles
3334
}
3435

36+
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
37+
if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil && u.ID != user.ID {
38+
return user, fmt.Errorf("user with given phone number already exists")
39+
}
40+
}
41+
3542
user.CreatedAt = time.Now().Unix()
3643
user.UpdatedAt = time.Now().Unix()
3744
userCollection, _ := p.db.Collection(ctx, models.Collections.User)
@@ -48,6 +55,7 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
4855
// UpdateUser to update user information in database
4956
func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.User, error) {
5057
user.UpdatedAt = time.Now().Unix()
58+
5159
collection, _ := p.db.Collection(ctx, models.Collections.User)
5260
meta, err := collection.UpdateDocument(ctx, user.Key, user)
5361
if err != nil {
@@ -211,3 +219,34 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
211219

212220
return nil
213221
}
222+
223+
// GetUserByPhoneNumber to get user information from database using phone number
224+
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
225+
var user models.User
226+
227+
query := fmt.Sprintf("FOR d in %s FILTER d.phone_number == @phone_number RETURN d", models.Collections.User)
228+
bindVars := map[string]interface{}{
229+
"phone_number": phoneNumber,
230+
}
231+
232+
cursor, err := p.db.Query(ctx, query, bindVars)
233+
if err != nil {
234+
return nil, err
235+
}
236+
defer cursor.Close()
237+
238+
for {
239+
if !cursor.HasMore() {
240+
if user.Key == "" {
241+
return nil, fmt.Errorf("user not found")
242+
}
243+
break
244+
}
245+
_, err := cursor.ReadDocument(ctx, &user)
246+
if err != nil {
247+
return nil, err
248+
}
249+
}
250+
251+
return &user, nil
252+
}

server/db/providers/cassandradb/provider.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ func NewProvider() (*provider, error) {
161161
if err != nil {
162162
return nil, err
163163
}
164+
165+
userPhoneNumberIndexQuery := fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_user_phone_number ON %s.%s (phone_number)", KeySpace, models.Collections.User)
166+
err = session.Query(userPhoneNumberIndexQuery).Exec()
167+
if err != nil {
168+
return nil, err
169+
}
164170
// add is_multi_factor_auth_enabled on users table
165171
userTableAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD is_multi_factor_auth_enabled boolean`, KeySpace, models.Collections.User)
166172
err = session.Query(userTableAlterQuery).Exec()

server/db/providers/cassandradb/user.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/authorizerdev/authorizer/server/db/models"
1313
"github.com/authorizerdev/authorizer/server/graph/model"
1414
"github.com/authorizerdev/authorizer/server/memorystore"
15+
"github.com/authorizerdev/authorizer/server/refs"
1516
"github.com/gocql/gocql"
1617
"github.com/google/uuid"
1718
)
@@ -30,6 +31,12 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
3031
user.Roles = defaultRoles
3132
}
3233

34+
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
35+
if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil && u.ID != user.ID {
36+
return user, fmt.Errorf("user with given phone number already exists")
37+
}
38+
}
39+
3340
user.CreatedAt = time.Now().Unix()
3441
user.UpdatedAt = time.Now().Unix()
3542

@@ -83,6 +90,12 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
8390
func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.User, error) {
8491
user.UpdatedAt = time.Now().Unix()
8592

93+
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
94+
if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil && u.ID != user.ID {
95+
return user, fmt.Errorf("user with given phone number already exists")
96+
}
97+
}
98+
8699
bytes, err := json.Marshal(user)
87100
if err != nil {
88101
return user, err
@@ -299,3 +312,14 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
299312

300313
return nil
301314
}
315+
316+
// GetUserByPhoneNumber to get user information from database using phone number
317+
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
318+
var user models.User
319+
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)
320+
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)
321+
if err != nil {
322+
return nil, err
323+
}
324+
return &user, nil
325+
}

server/db/providers/dynamodb/user.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ package dynamodb
33
import (
44
"context"
55
"errors"
6+
"fmt"
7+
"strings"
68
"time"
79

810
"github.com/authorizerdev/authorizer/server/constants"
911
"github.com/authorizerdev/authorizer/server/db/models"
1012
"github.com/authorizerdev/authorizer/server/graph/model"
1113
"github.com/authorizerdev/authorizer/server/memorystore"
14+
"github.com/authorizerdev/authorizer/server/refs"
1215
"github.com/google/uuid"
1316
"github.com/guregu/dynamo"
1417
log "github.com/sirupsen/logrus"
@@ -30,6 +33,12 @@ func (p *provider) AddUser(ctx context.Context, user models.User) (models.User,
3033
user.Roles = defaultRoles
3134
}
3235

36+
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
37+
if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil {
38+
return user, fmt.Errorf("user with given phone number already exists")
39+
}
40+
}
41+
3342
user.CreatedAt = time.Now().Unix()
3443
user.UpdatedAt = time.Now().Unix()
3544

@@ -49,6 +58,12 @@ func (p *provider) UpdateUser(ctx context.Context, user models.User) (models.Use
4958

5059
user.UpdatedAt = time.Now().Unix()
5160

61+
if user.PhoneNumber != nil && strings.TrimSpace(refs.StringValue(user.PhoneNumber)) != "" {
62+
if u, _ := p.GetUserByPhoneNumber(ctx, refs.StringValue(user.PhoneNumber)); u != nil && u.ID != user.ID {
63+
return user, fmt.Errorf("user with given phone number already exists")
64+
}
65+
}
66+
5267
err := UpdateByHashKey(collection, "id", user.ID, user)
5368
if err != nil {
5469
return user, err
@@ -193,3 +208,23 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
193208
}
194209
return nil
195210
}
211+
212+
// GetUserByPhoneNumber to get user information from database using phone number
213+
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
214+
var users []models.User
215+
var user models.User
216+
217+
collection := p.db.Table(models.Collections.User)
218+
err := collection.Scan().Index("phone_number").Filter("'phone_number' = ?", phoneNumber).AllWithContext(ctx, &users)
219+
220+
if err != nil {
221+
return nil, err
222+
}
223+
224+
if len(users) > 0 {
225+
user = users[0]
226+
return &user, nil
227+
} else {
228+
return nil, errors.New("no record found")
229+
}
230+
}

server/db/providers/mongodb/user.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,16 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
155155
}
156156
return nil
157157
}
158+
159+
// GetUserByPhoneNumber to get user information from database using phone number
160+
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
161+
var user models.User
162+
163+
userCollection := p.db.Collection(models.Collections.User, options.Collection())
164+
err := userCollection.FindOne(ctx, bson.M{"phone_number": phoneNumber}).Decode(&user)
165+
if err != nil {
166+
return nil, err
167+
}
168+
169+
return &user, nil
170+
}

server/db/providers/provider_template/user.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,10 @@ func (p *provider) UpdateUsers(ctx context.Context, data map[string]interface{},
6969

7070
return nil
7171
}
72+
73+
// GetUserByPhoneNumber to get user information from database using phone number
74+
func (p *provider) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error) {
75+
var user *models.User
76+
77+
return user, nil
78+
}

server/db/providers/providers.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ type Provider interface {
1818
ListUsers(ctx context.Context, pagination model.Pagination) (*model.Users, error)
1919
// GetUserByEmail to get user information from database using email address
2020
GetUserByEmail(ctx context.Context, email string) (models.User, error)
21+
// GetUserByPhoneNumber to get user information from database using phone number
22+
GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*models.User, error)
2123
// GetUserByID to get user information from database using user ID
2224
GetUserByID(ctx context.Context, id string) (models.User, error)
2325
// UpdateUsers to update multiple users, with parameters of user IDs slice

0 commit comments

Comments
 (0)