Skip to content

Commit c80b0d7

Browse files
authored
Merge pull request #368 from authorizerdev/fix-sms-verification-for-alldb
Move sms verificaiton to otp models
2 parents 87a9625 + 55fc4b2 commit c80b0d7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+585
-849
lines changed

.env.test

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,9 @@ SMTP_PORT=2525
77
SMTP_USERNAME=test
88
SMTP_PASSWORD=test
99
SENDER_EMAIL="[email protected]"
10+
TWILIO_API_KEY=test
11+
TWILIO_API_SECRET=test
12+
TWILIO_ACCOUNT_SID=ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
13+
TWILIO_SENDER=909921212112
1014
SENDER_NAME="Authorizer"
1115
AWS_REGION=ap-south-1

server/constants/auth_methods.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const (
77
AuthRecipeMethodMobileBasicAuth = "mobile_basic_auth"
88
// AuthRecipeMethodMagicLinkLogin is the magic_link_login auth method
99
AuthRecipeMethodMagicLinkLogin = "magic_link_login"
10+
// AuthRecipeMethodMobileOTP is the mobile_otp auth method
11+
AuthRecipeMethodMobileOTP = "mobile_otp"
1012
// AuthRecipeMethodGoogle is the google auth method
1113
AuthRecipeMethodGoogle = "google"
1214
// AuthRecipeMethodGithub is the github auth method

server/constants/env.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ const (
6666
EnvKeySenderName = "SENDER_NAME"
6767
// EnvKeyIsEmailServiceEnabled key for env variable IS_EMAIL_SERVICE_ENABLED
6868
EnvKeyIsEmailServiceEnabled = "IS_EMAIL_SERVICE_ENABLED"
69+
// EnvKeyIsSMSServiceEnabled key for env variable IS_SMS_SERVICE_ENABLED
70+
EnvKeyIsSMSServiceEnabled = "IS_SMS_SERVICE_ENABLED"
6971
// EnvKeyAppCookieSecure key for env variable APP_COOKIE_SECURE
7072
EnvKeyAppCookieSecure = "APP_COOKIE_SECURE"
7173
// EnvKeyAdminCookieSecure key for env variable ADMIN_COOKIE_SECURE
@@ -158,6 +160,9 @@ const (
158160
// EnvKeyDisableMultiFactorAuthentication is key for env variable DISABLE_MULTI_FACTOR_AUTHENTICATION
159161
// this variable is used to completely disable multi factor authentication. It will have no effect on profile preference
160162
EnvKeyDisableMultiFactorAuthentication = "DISABLE_MULTI_FACTOR_AUTHENTICATION"
163+
// EnvKeyDisablePhoneVerification is key for env variable DISABLE_PHONE_VERIFICATION
164+
// this variable is used to disable phone verification
165+
EnvKeyDisablePhoneVerification = "DISABLE_PHONE_VERIFICATION"
161166

162167
// Slice variables
163168
// EnvKeyRoles key for env variable ROLES
@@ -177,17 +182,13 @@ const (
177182
// This env is used for setting default response mode in authorize handler
178183
EnvKeyDefaultAuthorizeResponseMode = "DEFAULT_AUTHORIZE_RESPONSE_MODE"
179184

180-
// Phone verification setting
181-
EnvKeyDisablePhoneVerification = "DISABLE_PHONE_VERIFICATION"
182-
183185
// Twilio env variables
184-
185186
// EnvKeyTwilioAPIKey key for env variable TWILIO_API_KEY
186187
EnvKeyTwilioAPIKey = "TWILIO_API_KEY"
187188
// EnvKeyTwilioAPISecret key for env variable TWILIO_API_SECRET
188189
EnvKeyTwilioAPISecret = "TWILIO_API_SECRET"
189190
// EnvKeyTwilioAccountSID key for env variable TWILIO_ACCOUNT_SID
190191
EnvKeyTwilioAccountSID = "TWILIO_ACCOUNT_SID"
191-
// EnvKeyTwilioSenderFrom key for env variable TWILIO_SENDER_FROM
192-
EnvKeyTwilioSenderFrom = "TWILIO_SENDER_FROM"
192+
// EnvKeyTwilioSender key for env variable TWILIO_SENDER
193+
EnvKeyTwilioSender = "TWILIO_SENDER"
193194
)

server/db/models/otp.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
package models
22

3+
const (
4+
// FieldName email is the field name for email
5+
FieldNameEmail = "email"
6+
// FieldNamePhoneNumber is the field name for phone number
7+
FieldNamePhoneNumber = "phone_number"
8+
)
9+
310
// OTP model for database
411
type OTP struct {
5-
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
6-
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
7-
Email string `gorm:"unique" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"`
8-
Otp string `json:"otp" bson:"otp" cql:"otp" dynamo:"otp"`
9-
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"`
10-
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
11-
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
12+
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
13+
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
14+
Email string `gorm:"unique" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"`
15+
PhoneNumber string `gorm:"index:unique_index_phone_number,unique" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number"`
16+
Otp string `json:"otp" bson:"otp" cql:"otp" dynamo:"otp"`
17+
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"`
18+
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
19+
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
1220
}
1321

1422
type Paging struct {

server/db/models/sms_verification_requests.go

Lines changed: 0 additions & 11 deletions
This file was deleted.

server/db/providers/arangodb/otp.go

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package arangodb
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"time"
78

@@ -12,17 +13,31 @@ import (
1213

1314
// UpsertOTP to add or update otp
1415
func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models.OTP, error) {
15-
otp, _ := p.GetOTPByEmail(ctx, otpParam.Email)
16+
// check if email or phone number is present
17+
if otpParam.Email == "" && otpParam.PhoneNumber == "" {
18+
return nil, errors.New("email or phone_number is required")
19+
}
20+
uniqueField := models.FieldNameEmail
21+
if otpParam.Email == "" && otpParam.PhoneNumber != "" {
22+
uniqueField = models.FieldNamePhoneNumber
23+
}
24+
var otp *models.OTP
25+
if uniqueField == models.FieldNameEmail {
26+
otp, _ = p.GetOTPByEmail(ctx, otpParam.Email)
27+
} else {
28+
otp, _ = p.GetOTPByPhoneNumber(ctx, otpParam.PhoneNumber)
29+
}
1630
shouldCreate := false
1731
if otp == nil {
1832
id := uuid.NewString()
1933
otp = &models.OTP{
20-
ID: id,
21-
Key: id,
22-
Otp: otpParam.Otp,
23-
Email: otpParam.Email,
24-
ExpiresAt: otpParam.ExpiresAt,
25-
CreatedAt: time.Now().Unix(),
34+
ID: id,
35+
Key: id,
36+
Otp: otpParam.Otp,
37+
Email: otpParam.Email,
38+
PhoneNumber: otpParam.PhoneNumber,
39+
ExpiresAt: otpParam.ExpiresAt,
40+
CreatedAt: time.Now().Unix(),
2641
}
2742
shouldCreate = true
2843
} else {
@@ -67,7 +82,35 @@ func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*mod
6782
for {
6883
if !cursor.HasMore() {
6984
if otp.Key == "" {
70-
return nil, fmt.Errorf("email template not found")
85+
return nil, fmt.Errorf("otp with given email not found")
86+
}
87+
break
88+
}
89+
_, err := cursor.ReadDocument(ctx, &otp)
90+
if err != nil {
91+
return nil, err
92+
}
93+
}
94+
95+
return &otp, nil
96+
}
97+
98+
// GetOTPByPhoneNumber to get otp for a given phone number
99+
func (p *provider) GetOTPByPhoneNumber(ctx context.Context, phoneNumber string) (*models.OTP, error) {
100+
var otp models.OTP
101+
query := fmt.Sprintf("FOR d in %s FILTER d.phone_number == @phone_number RETURN d", models.Collections.OTP)
102+
bindVars := map[string]interface{}{
103+
"phone_number": phoneNumber,
104+
}
105+
cursor, err := p.db.Query(ctx, query, bindVars)
106+
if err != nil {
107+
return nil, err
108+
}
109+
defer cursor.Close()
110+
for {
111+
if !cursor.HasMore() {
112+
if otp.Key == "" {
113+
return nil, fmt.Errorf("otp with given phone_number not found")
71114
}
72115
break
73116
}

server/db/providers/arangodb/provider.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,16 +225,14 @@ func NewProvider() (*provider, error) {
225225
return nil, err
226226
}
227227
}
228-
229228
otpCollection, err := arangodb.Collection(ctx, models.Collections.OTP)
230229
if err != nil {
231230
return nil, err
232231
}
233-
otpCollection.EnsureHashIndex(ctx, []string{"email"}, &arangoDriver.EnsureHashIndexOptions{
232+
otpCollection.EnsureHashIndex(ctx, []string{models.FieldNameEmail, models.FieldNamePhoneNumber}, &arangoDriver.EnsureHashIndexOptions{
234233
Unique: true,
235234
Sparse: true,
236235
})
237-
238236
return &provider{
239237
db: arangodb,
240238
}, err

server/db/providers/arangodb/sms_verification_requests.go

Lines changed: 0 additions & 22 deletions
This file was deleted.

server/db/providers/cassandradb/otp.go

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cassandradb
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
67
"time"
78

@@ -12,17 +13,31 @@ import (
1213

1314
// UpsertOTP to add or update otp
1415
func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models.OTP, error) {
15-
otp, _ := p.GetOTPByEmail(ctx, otpParam.Email)
16+
// check if email or phone number is present
17+
if otpParam.Email == "" && otpParam.PhoneNumber == "" {
18+
return nil, errors.New("email or phone_number is required")
19+
}
20+
uniqueField := models.FieldNameEmail
21+
if otpParam.Email == "" && otpParam.PhoneNumber != "" {
22+
uniqueField = models.FieldNamePhoneNumber
23+
}
24+
var otp *models.OTP
25+
if uniqueField == models.FieldNameEmail {
26+
otp, _ = p.GetOTPByEmail(ctx, otpParam.Email)
27+
} else {
28+
otp, _ = p.GetOTPByPhoneNumber(ctx, otpParam.PhoneNumber)
29+
}
1630
shouldCreate := false
1731
if otp == nil {
1832
shouldCreate = true
1933
otp = &models.OTP{
20-
ID: uuid.NewString(),
21-
Otp: otpParam.Otp,
22-
Email: otpParam.Email,
23-
ExpiresAt: otpParam.ExpiresAt,
24-
CreatedAt: time.Now().Unix(),
25-
UpdatedAt: time.Now().Unix(),
34+
ID: uuid.NewString(),
35+
Otp: otpParam.Otp,
36+
Email: otpParam.Email,
37+
PhoneNumber: otpParam.PhoneNumber,
38+
ExpiresAt: otpParam.ExpiresAt,
39+
CreatedAt: time.Now().Unix(),
40+
UpdatedAt: time.Now().Unix(),
2641
}
2742
} else {
2843
otp.Otp = otpParam.Otp
@@ -32,7 +47,7 @@ func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models
3247
otp.UpdatedAt = time.Now().Unix()
3348
query := ""
3449
if shouldCreate {
35-
query = fmt.Sprintf(`INSERT INTO %s (id, email, otp, expires_at, created_at, updated_at) VALUES ('%s', '%s', '%s', %d, %d, %d)`, KeySpace+"."+models.Collections.OTP, otp.ID, otp.Email, otp.Otp, otp.ExpiresAt, otp.CreatedAt, otp.UpdatedAt)
50+
query = fmt.Sprintf(`INSERT INTO %s (id, email, phone_number, otp, expires_at, created_at, updated_at) VALUES ('%s', '%s', '%s', '%s', %d, %d, %d)`, KeySpace+"."+models.Collections.OTP, otp.ID, otp.Email, otp.PhoneNumber, otp.Otp, otp.ExpiresAt, otp.CreatedAt, otp.UpdatedAt)
3651
} else {
3752
query = fmt.Sprintf(`UPDATE %s SET otp = '%s', expires_at = %d, updated_at = %d WHERE id = '%s'`, KeySpace+"."+models.Collections.OTP, otp.Otp, otp.ExpiresAt, otp.UpdatedAt, otp.ID)
3853
}
@@ -48,8 +63,19 @@ func (p *provider) UpsertOTP(ctx context.Context, otpParam *models.OTP) (*models
4863
// GetOTPByEmail to get otp for a given email address
4964
func (p *provider) GetOTPByEmail(ctx context.Context, emailAddress string) (*models.OTP, error) {
5065
var otp models.OTP
51-
query := fmt.Sprintf(`SELECT id, email, otp, expires_at, created_at, updated_at FROM %s WHERE email = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.OTP, emailAddress)
52-
err := p.db.Query(query).Consistency(gocql.One).Scan(&otp.ID, &otp.Email, &otp.Otp, &otp.ExpiresAt, &otp.CreatedAt, &otp.UpdatedAt)
66+
query := fmt.Sprintf(`SELECT id, email, phone_number, otp, expires_at, created_at, updated_at FROM %s WHERE email = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.OTP, emailAddress)
67+
err := p.db.Query(query).Consistency(gocql.One).Scan(&otp.ID, &otp.Email, &otp.PhoneNumber, &otp.Otp, &otp.ExpiresAt, &otp.CreatedAt, &otp.UpdatedAt)
68+
if err != nil {
69+
return nil, err
70+
}
71+
return &otp, nil
72+
}
73+
74+
// GetOTPByPhoneNumber to get otp for a given phone number
75+
func (p *provider) GetOTPByPhoneNumber(ctx context.Context, phoneNumber string) (*models.OTP, error) {
76+
var otp models.OTP
77+
query := fmt.Sprintf(`SELECT id, email, phone_number, otp, expires_at, created_at, updated_at FROM %s WHERE phone_number = '%s' LIMIT 1 ALLOW FILTERING`, KeySpace+"."+models.Collections.OTP, phoneNumber)
78+
err := p.db.Query(query).Consistency(gocql.One).Scan(&otp.ID, &otp.Email, &otp.PhoneNumber, &otp.Otp, &otp.ExpiresAt, &otp.CreatedAt, &otp.UpdatedAt)
5379
if err != nil {
5480
return nil, err
5581
}

server/db/providers/cassandradb/provider.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,19 @@ func NewProvider() (*provider, error) {
254254
if err != nil {
255255
return nil, err
256256
}
257-
257+
// Add phone_number column to otp table
258+
otpAlterQuery := fmt.Sprintf(`ALTER TABLE %s.%s ADD (phone_number text);`, KeySpace, models.Collections.OTP)
259+
err = session.Query(otpAlterQuery).Exec()
260+
if err != nil {
261+
log.Debug("Failed to alter table as column exists: ", err)
262+
// continue
263+
}
264+
// Add phone number index
265+
otpIndexQueryPhoneNumber := fmt.Sprintf("CREATE INDEX IF NOT EXISTS authorizer_otp_phone_number ON %s.%s (phone_number)", KeySpace, models.Collections.OTP)
266+
err = session.Query(otpIndexQueryPhoneNumber).Exec()
267+
if err != nil {
268+
return nil, err
269+
}
258270
return &provider{
259271
db: session,
260272
}, err

0 commit comments

Comments
 (0)