Skip to content

Commit

Permalink
feat: add sms verification for phone numbers
Browse files Browse the repository at this point in the history
  • Loading branch information
jonas-jonas committed Nov 30, 2023
1 parent 7c0e02e commit f0c9554
Show file tree
Hide file tree
Showing 58 changed files with 880 additions and 664 deletions.
31 changes: 31 additions & 0 deletions contrib/quickstart/kratos/phone-password/identity.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"$id": "https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"phone": {
"type": "string",
"format": "tel",
"title": "Phone number",
"minLength": 3,
"ory.sh/kratos": {
"credentials": {
"password": {
"identifier": true
}
},
"verification": {
"via": "phone"
}
}
}
},
"required": ["phone"],
"additionalProperties": false
}
}
}
113 changes: 113 additions & 0 deletions contrib/quickstart/kratos/phone-password/kratos.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
version: v0.13.0

dsn: memory

serve:
public:
base_url: http://127.0.0.1:4433/
cors:
enabled: true
admin:
base_url: http://kratos:4434/

selfservice:
default_browser_return_url: http://127.0.0.1:4455/
allowed_return_urls:
- http://127.0.0.1:4455
- http://localhost:19006/Callback
- exp://localhost:8081/--/Callback

methods:
password:
enabled: true
totp:
config:
issuer: Kratos
enabled: true
lookup_secret:
enabled: true
link:
enabled: true
code:
enabled: true

flows:
error:
ui_url: http://127.0.0.1:4455/error

settings:
ui_url: http://127.0.0.1:4455/settings
privileged_session_max_age: 15m
required_aal: highest_available

recovery:
enabled: true
ui_url: http://127.0.0.1:4455/recovery
use: code

verification:
enabled: true
ui_url: http://127.0.0.1:4455/verification
use: code
after:
default_browser_return_url: http://127.0.0.1:4455/

logout:
after:
default_browser_return_url: http://127.0.0.1:4455/login

login:
ui_url: http://127.0.0.1:4455/login
lifespan: 10m

registration:
lifespan: 10m
ui_url: http://127.0.0.1:4455/registration
after:
password:
hooks:
- hook: session
- hook: show_verification_ui

log:
level: debug
format: text
leak_sensitive_values: true

secrets:
cookie:
- PLEASE-CHANGE-ME-I-AM-VERY-INSECURE
cipher:
- 32-LONG-SECRET-NOT-SECURE-AT-ALL

ciphers:
algorithm: xchacha20-poly1305

hashers:
algorithm: bcrypt
bcrypt:
cost: 8

identity:
default_schema_id: default
schemas:
- id: default
url: file:///etc/config/kratos/identity.schema.json

courier:
channels:
- id: phone
request_config:
url: https://api.twilio.com/2010-04-01/Accounts/AXXXXXXXXXXXXXX/Messages.json
method: POST
body: base64://ZnVuY3Rpb24oY3R4KSB7CkJvZHk6IGN0eC5ib2R5LApUbzogY3R4LnRvLEZyb206IGN0eC5mcm9tCn0=
headers:
Content-Type: application/x-www-form-urlencoded
auth:
type: basic_auth
config:
user: AXXXXXXX
password: XXXX

feature_flags:
use_continue_with_transitions: true
10 changes: 10 additions & 0 deletions courier/channel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package courier

import (
"context"
)

type Channel interface {
ID() string
Dispatch(ctx context.Context, msg Message) error
}
32 changes: 18 additions & 14 deletions courier/courier.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"context"
"time"

"github.com/ory/kratos/courier/template"
"github.com/ory/x/jsonnetsecure"

"github.com/cenkalti/backoff"
Expand All @@ -16,7 +15,6 @@ import (

"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/x"
gomail "github.com/ory/mail/v3"
)

type (
Expand All @@ -33,11 +31,8 @@ type (
Work(ctx context.Context) error
QueueEmail(ctx context.Context, t EmailTemplate) (uuid.UUID, error)
QueueSMS(ctx context.Context, t SMSTemplate) (uuid.UUID, error)
SmtpDialer() *gomail.Dialer
DispatchQueue(ctx context.Context) error
DispatchMessage(ctx context.Context, msg Message) error
SetGetEmailTemplateType(f func(t EmailTemplate) (TemplateType, error))
SetNewEmailTemplateFromMessage(f func(d template.Dependencies, msg Message) (EmailTemplate, error))
UseBackoff(b backoff.BackOff)
FailOnDispatchError()
}
Expand All @@ -51,26 +46,35 @@ type (
}

courier struct {
smsClient *smsClient
smtpClient *smtpClient
httpClient *httpClient
courierChannels map[string]Channel
deps Dependencies
failOnDispatchError bool
backoff backoff.BackOff
}
)

func NewCourier(ctx context.Context, deps Dependencies) (Courier, error) {
smtp, err := newSMTP(ctx, deps)
emailChannel, err := NewEmailChannel(ctx, deps)
if err != nil {
return nil, err
}
return NewCourierWithCustomEmailChannel(ctx, deps, emailChannel)
}

func NewCourierWithCustomEmailChannel(ctx context.Context, deps Dependencies, channel *EmailChannel) (Courier, error) {
channels := make(map[string]Channel, len(deps.CourierConfig().CourierChannels(ctx)))
for _, c := range deps.CourierConfig().CourierChannels(ctx) {
// TODO: add support for more channel types (e.g. SMTP, ..?)
channels[c.ID] = newHttpChannel(c.ID, c.RequestConfig, deps)
}
if _, ok := channels["email"]; !ok {
channels["email"] = channel
}

return &courier{
smsClient: newSMS(ctx, deps),
smtpClient: smtp,
httpClient: newHTTP(ctx, deps),
deps: deps,
backoff: backoff.NewExponentialBackOff(),
deps: deps,
backoff: backoff.NewExponentialBackOff(),
courierChannels: channels,
}, nil
}

Expand Down
24 changes: 13 additions & 11 deletions courier/courier_dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,26 @@ func (c *courier) DispatchMessage(ctx context.Context, msg Message) error {
Error(`Unable to increment the message's "send_count" field`)
return err
}
channelId := msg.Channel
if channelId == "" {
channelId = msg.Type.String()
}

switch msg.Type {
case MessageTypeEmail:
if err := c.dispatchEmail(ctx, msg); err != nil {
return err
}
case MessageTypePhone:
if err := c.dispatchSMS(ctx, msg); err != nil {
return err
}
default:
return errors.Errorf("received unexpected message type: %d", msg.Type)
channel := c.courierChannels[channelId]
if channel == nil {
return errors.Errorf("received unexpected channel: %s", channelId)
}

if err := channel.Dispatch(ctx, msg); err != nil {
return err
}

if err := c.deps.CourierPersister().SetMessageStatus(ctx, msg.ID, MessageStatusSent); err != nil {
c.deps.Logger().
WithError(err).
WithField("message_id", msg.ID).
WithField("message_nid", msg.NID).
WithField("channel", channelId).
Error(`Unable to set the message status to "sent".`)
return err
}
Expand All @@ -47,6 +48,7 @@ func (c *courier) DispatchMessage(ctx context.Context, msg Message) error {
WithField("message_type", msg.Type).
WithField("message_template_type", msg.TemplateType).
WithField("message_subject", msg.Subject).
WithField("channel", channelId).
Debug("Courier sent out message.")

return nil
Expand Down
Loading

0 comments on commit f0c9554

Please sign in to comment.