Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: passwordless SMS and expiry notice in code / link templates #4104

Merged
merged 18 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/clidoc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ func init() {
"NewInfoSelfServiceLoginContinue": text.NewInfoSelfServiceLoginContinue(),
"NewErrorValidationSuchNoWebAuthnUser": text.NewErrorValidationSuchNoWebAuthnUser(),
"NewRegistrationEmailWithCodeSent": text.NewRegistrationEmailWithCodeSent(),
"NewLoginEmailWithCodeSent": text.NewLoginEmailWithCodeSent(),
"NewLoginCodeSent": text.NewLoginCodeSent(),
"NewErrorValidationRegistrationCodeInvalidOrAlreadyUsed": text.NewErrorValidationRegistrationCodeInvalidOrAlreadyUsed(),
"NewErrorValidationLoginCodeInvalidOrAlreadyUsed": text.NewErrorValidationLoginCodeInvalidOrAlreadyUsed(),
"NewErrorValidationNoCodeUser": text.NewErrorValidationNoCodeUser(),
Expand Down
36 changes: 8 additions & 28 deletions courier/courier.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,42 +47,22 @@ type (
}

courier struct {
courierChannels map[string]Channel
deps Dependencies
failOnDispatchError bool
backoff backoff.BackOff
deps Dependencies
failOnDispatchError bool
backoff backoff.BackOff
newEmailTemplateFromMessage func(d template.Dependencies, msg Message) (EmailTemplate, error)
}
)

func NewCourier(ctx context.Context, deps Dependencies) (Courier, error) {
return NewCourierWithCustomTemplates(ctx, deps, NewEmailTemplateFromMessage)
}

func NewCourierWithCustomTemplates(ctx context.Context, deps Dependencies, newEmailTemplateFromMessage func(d template.Dependencies, msg Message) (EmailTemplate, error)) (Courier, error) {
cs, err := deps.CourierConfig().CourierChannels(ctx)
if err != nil {
return nil, err
}
channels := make(map[string]Channel, len(cs))
for _, c := range cs {
switch c.Type {
case "smtp":
ch, err := NewSMTPChannelWithCustomTemplates(deps, c.SMTPConfig, newEmailTemplateFromMessage)
if err != nil {
return nil, err
}
channels[ch.ID()] = ch
case "http":
channels[c.ID] = newHttpChannel(c.ID, c.RequestConfig, deps)
default:
return nil, errors.Errorf("unknown courier channel type: %s", c.Type)
}
}

func NewCourierWithCustomTemplates(_ context.Context, deps Dependencies, newEmailTemplateFromMessage func(d template.Dependencies, msg Message) (EmailTemplate, error)) (Courier, error) {
return &courier{
deps: deps,
backoff: backoff.NewExponentialBackOff(),
courierChannels: channels,
deps: deps,
backoff: backoff.NewExponentialBackOff(),
newEmailTemplateFromMessage: newEmailTemplateFromMessage,
}, nil
}

Expand Down
36 changes: 33 additions & 3 deletions courier/courier_dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,33 @@
"github.com/pkg/errors"
)

func (c *courier) channels(ctx context.Context, id string) (Channel, error) {
cs, err := c.deps.CourierConfig().CourierChannels(ctx)
if err != nil {
return nil, err

Check warning on line 15 in courier/courier_dispatcher.go

View check run for this annotation

Codecov / codecov/patch

courier/courier_dispatcher.go#L15

Added line #L15 was not covered by tests
}

for _, channel := range cs {
if channel.ID != id {
continue
}
switch channel.Type {
case "smtp":
courierChannel, err := NewSMTPChannelWithCustomTemplates(c.deps, channel.SMTPConfig, c.newEmailTemplateFromMessage)
if err != nil {
return nil, err

Check warning on line 26 in courier/courier_dispatcher.go

View check run for this annotation

Codecov / codecov/patch

courier/courier_dispatcher.go#L26

Added line #L26 was not covered by tests
}
return courierChannel, nil
case "http":
return newHttpChannel(channel.ID, channel.RequestConfig, c.deps), nil
default:
return nil, errors.Errorf("unknown courier channel type: %s", channel.Type)

Check warning on line 32 in courier/courier_dispatcher.go

View check run for this annotation

Codecov / codecov/patch

courier/courier_dispatcher.go#L31-L32

Added lines #L31 - L32 were not covered by tests
}
}

return nil, errors.Errorf("no courier channels configured")
}

func (c *courier) DispatchMessage(ctx context.Context, msg Message) error {
logger := c.deps.Logger().
WithField("message_id", msg.ID).
Expand All @@ -24,9 +51,9 @@
return err
}

channel, ok := c.courierChannels[msg.Channel.String()]
if !ok {
return errors.Errorf("message %s has unknown channel %q", msg.ID.String(), msg.Channel)
channel, err := c.channels(ctx, msg.Channel.String())
if err != nil {
return err
}

logger = logger.
Expand Down Expand Up @@ -80,6 +107,9 @@
logger.
Warnf(`Message was abandoned because it did not deliver after %d attempts`, msg.SendCount)
} else if err := c.DispatchMessage(ctx, msg); err != nil {
logger.
WithError(err).
Warn(`Unable to dispatch message.`)
if err := c.deps.CourierPersister().RecordDispatch(ctx, msg.ID, CourierMessageDispatchStatusFailed, err); err != nil {
logger.
WithError(err).
Expand Down
6 changes: 6 additions & 0 deletions courier/sms_templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,14 @@
return nil, err
}
return sms.NewLoginCodeValid(d, &t), nil
case template.TypeRegistrationCodeValid:
var t sms.RegistrationCodeValidModel
if err := json.Unmarshal(m.TemplateData, &t); err != nil {
return nil, err

Check warning on line 46 in courier/sms_templates.go

View check run for this annotation

Codecov / codecov/patch

courier/sms_templates.go#L43-L46

Added lines #L43 - L46 were not covered by tests
}
return sms.NewRegistrationCodeValid(d, &t), nil

default:

Check warning on line 50 in courier/sms_templates.go

View check run for this annotation

Codecov / codecov/patch

courier/sms_templates.go#L48-L50

Added lines #L48 - L50 were not covered by tests
return nil, errors.Errorf("received unexpected message template type: %s", m.TemplateType)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Hi,

please login to your account by entering the following code:
Login to your account with the following code:

{{ .LoginCode }}

It expires in {{ .ExpiresInMinutes }} minutes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Hi,

please login to your account by entering the following code:
Login to your account with the following code:

{{ .LoginCode }}

It expires in {{ .ExpiresInMinutes }} minutes.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Login to your account
Use code {{ .LoginCode }} to log in
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
Your login code is: {{ .LoginCode }}

It expires in {{ .ExpiresInMinutes }} minutes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Hi,

please recover access to your account by clicking the following link:
Recover access to your account by clicking the following link:

<a href="{{ .RecoveryURL }}">{{ .RecoveryURL }}</a>

If this was not you, do nothing. This link expires in {{ .ExpiresInMinutes }} minutes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Hi,

please recover access to your account by clicking the following link:
Recover access to your account by clicking the following link:

{{ .RecoveryURL }}

If this was not you, do nothing. This link expires in {{ .ExpiresInMinutes }} minutes.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Hi,

please recover access to your account by entering the following code:
Recover access to your account by entering the following code:

{{ .RecoveryCode }}

If this was not you, do nothing. This code expires in {{ .ExpiresInMinutes }} minutes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Hi,

please recover access to your account by entering the following code:
Recover access to your account by entering the following code:

{{ .RecoveryCode }}

If this was not you, do nothing. This code expires in {{ .ExpiresInMinutes }} minutes.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Recover access to your account
Use code {{ .RecoveryCode }} to recover access to your account
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Hi,

please complete your account registration by entering the following code:
Complete your account registration with the following code:

{{ .RegistrationCode }}

This code expires in {{ .ExpiresInMinutes }} minutes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Hi,

please complete your account registration by entering the following code:
Complete your account registration with the following code:

{{ .RegistrationCode }}

This code expires in {{ .ExpiresInMinutes }} minutes.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Complete your account registration
Use code {{ .RegistrationCode }} to complete your account registration
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Your registration code is: {{ .RegistrationCode }}

It expires in {{ .ExpiresInMinutes }} minutes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
Hi,

someone asked to verify this email address, but we were unable to find an account for this address.
Someone asked to verify this email address, but we were unable to find an account for this address.

If this was you, check if you signed up using a different address.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
Hi,

someone asked to verify this email address, but we were unable to find an account for this address.
Someone asked to verify this email address, but we were unable to find an account for this address.

If this was you, check if you signed up using a different address.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
Hi, please verify your account by clicking the following link:
Verify your account by opening the following link:

<a href="{{ .VerificationURL }}">{{ .VerificationURL }}</a>

If this was not you, do nothing. This link expires in {{ .ExpiresInMinutes }} minutes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Hi, please verify your account by clicking the following link:
Verify your account by opening the following link:

{{ .VerificationURL }}

If this was not you, do nothing. This link expires in {{ .ExpiresInMinutes }} minutes.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
Hi,

please verify your account by entering the following code:
Verify your account with the following code:

{{ .VerificationCode }}

or clicking the following link:

<a href="{{ .VerificationURL }}">{{ .VerificationURL }}</a>

If this was not you, do nothing. This code / link expires in {{ .ExpiresInMinutes }} minutes.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
Hi,

please verify your account by entering the following code:
Verify your account with the following code:

{{ .VerificationCode }}

or clicking the following link:

{{ .VerificationURL }}

If this was not you, do nothing. This code / link expires in {{ .ExpiresInMinutes }} minutes.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Please verify your email address
Use code {{ .VerificationCode }} to verify your account
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
Your verification code is: {{ .VerificationCode }}

If this was not you, do nothing. It expires in {{ .ExpiresInMinutes }} minutes.
1 change: 1 addition & 0 deletions courier/template/email/login_code_valid.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type (
Identity map[string]interface{} `json:"identity"`
RequestURL string `json:"request_url"`
TransientPayload map[string]interface{} `json:"transient_payload"`
ExpiresInMinutes int `json:"expires_in_minutes"`
}
)

Expand Down
1 change: 1 addition & 0 deletions courier/template/email/recovery_code_valid.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type (
Identity map[string]interface{} `json:"identity"`
RequestURL string `json:"request_url"`
TransientPayload map[string]interface{} `json:"transient_payload"`
ExpiresInMinutes int `json:"expires_in_minutes"`
}
)

Expand Down
1 change: 1 addition & 0 deletions courier/template/email/recovery_valid.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type (
Identity map[string]interface{} `json:"identity"`
RequestURL string `json:"request_url"`
TransientPayload map[string]interface{} `json:"transient_payload"`
ExpiresInMinutes int `json:"expires_in_minutes"`
}
)

Expand Down
1 change: 1 addition & 0 deletions courier/template/email/registration_code_valid.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type (
RegistrationCode string `json:"registration_code"`
RequestURL string `json:"request_url"`
TransientPayload map[string]interface{} `json:"transient_payload"`
ExpiresInMinutes int `json:"expires_in_minutes"`
}
)

Expand Down
1 change: 1 addition & 0 deletions courier/template/email/verification_code_valid.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type (
Identity map[string]interface{} `json:"identity"`
RequestURL string `json:"request_url"`
TransientPayload map[string]interface{} `json:"transient_payload"`
ExpiresInMinutes int `json:"expires_in_minutes"`
}
)

Expand Down
1 change: 1 addition & 0 deletions courier/template/email/verification_valid.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type (
Identity map[string]interface{} `json:"identity"`
RequestURL string `json:"request_url"`
TransientPayload map[string]interface{} `json:"transient_payload"`
ExpiresInMinutes int `json:"expires_in_minutes"`
}
)

Expand Down
1 change: 1 addition & 0 deletions courier/template/sms/login_code_valid.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type (
Identity map[string]interface{} `json:"identity"`
RequestURL string `json:"request_url"`
TransientPayload map[string]interface{} `json:"transient_payload"`
ExpiresInMinutes int `json:"expires_in_minutes"`
}
)

Expand Down
2 changes: 1 addition & 1 deletion courier/template/sms/login_code_valid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestNewLoginCodeValid(t *testing.T) {

tpl := sms.NewLoginCodeValid(reg, &sms.LoginCodeValidModel{To: expectedPhone, LoginCode: otp})

expectedBody := fmt.Sprintf("Your login code is: %s\n", otp)
expectedBody := fmt.Sprintf("Your login code is: %s\n\nIt expires in 0 minutes.\n", otp)

actualBody, err := tpl.SMSBody(context.Background())
require.NoError(t, err)
Expand Down
Loading
Loading