Out-of-band communication via E-Mail and SMS
Ory Kratos sends out-of-band messages via SMS or E-Mail. The following exemplary use cases require these messages:
- Send an account activation email
- Verify an E-Mail address or mobile phone number using SMS
- Preventing Account Enumeration Attacks
- Sending a 2FA Codes
- ...
Mail courier
Ory Kratos processes email dispatch using a mail courier worker, which must run as a singleton in order to process the mail queue. It can be run as a background worker on a single-instance Kratos setup or as a distinct singleton foreground worker in multi-instance deployments.
Single instance
To run the mail courier in the background on your single Kratos instance, add
the --watch-courier
flag to your kratos serve
command, as outlined in the
CLI docs
Multi-instance
If you're running multiple instances of Kratos (for example replicated
Kubernetes deployment), you need to run the mail courier as a separate singleton
job. The courier can be started with the kratos courier watch
command
(CLI docs).
Sending E-Mails via SMTP
To have E-Mail delivery running with Ory Kratos requires an SMTP server. This is
set up in the configuration file using an absolute URL with the smtp
or
smtps
scheme:
# kratos -c path/to/my/kratos/config.yml serve
courier:
smtp:
connection_uri: smtps://foo:bar@my-smtp-server:1234/
# Examples:
# - "smtp://foo:bar@my-mailserver:1234/?disable_starttls=true
# (NOT RECOMMENDED: Cleartext smtp for devel and legacy infrastructure
# only)"
# - smtp://foo:bar@my-mailserver:1234/ (Explicit StartTLS with certificate
# trust verification)
# - "smtp://foo:bar@my-mailserver:1234/?skip_ssl_verify=true (NOT
# RECOMMENDED: Explicit StartTLS without certificate trust verification)"
# - smtps://foo:bar@my-mailserver:1234/ (Implicit TLS with certificate trust
# verification)
# - "smtps://foo:bar@my-mailserver:1234/?skip_ssl_verify=true (NOT
# RECOMMENDED: Implicit TLS without certificate trust verification)"
Sender Address and Template Customization
You can customize the sender address and email templates by overriding path to the templates folder. See more about custom templates in templates section.
# kratos -c path/to/my/kratos/config.yml serve
courier:
## SMTP Sender Address ##
#
# The recipient of an email will see this as the sender address.
#
# Default value: no-reply@ory.kratos.sh
#
# Set this value using environment variables on
# - Linux/macOS:
# export COURIER_SMTP_FROM_ADDRESS=<value>
# - Windows Command Line (CMD):
# > set COURIER_SMTP_FROM_ADDRESS=<value>
#
smtp:
from_address: no-reply@ory.kratos.sh
## Override message templates ##
#
# You can override certain or all message templates by pointing this key to the path where the templates are located.
#
# Examples:
# - /conf/courier-templates
#
# Set this value using environment variables on
# - Linux/macOS:
# export COURIER_TEMPLATE_OVERRIDE_PATH=<value>
# - Windows Command Line (CMD):
# > set COURIER_TEMPLATE_OVERRIDE_PATH=<value>
#
template_override_path: /conf/courier-templates
## Override with remote templates ##
#
# You can specify specific template values to override or the whole template
#
# Supported templates are:
# - verification
# - valid
# - invalid
# - recovery
# - valid
# - invalid
#
# Each template supports the following layout. A singular key can be specified under `email` to override the defaults.
# When specifying `body`, however, Kratos expects `html` and `plaintext` to be set.
# email:
# subject: http(s)://, file://, base64://
# body:
# html: http(s)://, file://, base64://
# plaintext: http(s)://, file://, base64://
templates:
# we can specify here
verification:
valid:
email:
body:
# plaintext and html are required when overriding the body
html: https://some-remote-resource/gotmpl
plaintext: base64://SGV5IHlvdSBkZWNvZGVkIG1lIDop
# optional
subject: file://some-file/subject.gotmpl
# we can also omit the `invalid` field here if you wish to use the default built-in templates
# or template_override_path
invalid:
# same configuration structure as valid
# this is also optional and can be omitted in preference for the default built-in templates
# or template_override_path
recovery:
# the configuration structure is the same as the verification
Custom SMTP Headers
You can configure custom SMTP headers. For example, if integrating with AWS SES SMTP interface, the headers can be configured for cross-account sending:
# kratos -c path/to/my/kratos/config.yml serve
courier:
smtp:
headers:
X-SES-SOURCE-ARN: arn:aws:ses:us-west-2:123456789012:identity/example.com
X-SES-FROM-ARN: arn:aws:ses:us-west-2:123456789012:identity/example.com
X-SES-RETURN-PATH-ARN: arn:aws:ses:us-west-2:123456789012:identity/example.com
Sending SMS
For sending SMS Ory Kratos uses an external SMS gateway, which must have HTTP API (such as Twilio, your local SMS sender, or your own microservice). Request method, headers, body, and content-type are fully configurable using options below.
Configuration
Default configuration of Ory Kratos doesn't include sending SMS. To enable it you need to set "enabled" flag to true, sender URL, authorization (if needed) and request body format.
Sender phone number
SMS message will come to the recipient from this phone number. Depending on your SMS sender settings you can use letters here (for example "Your Org Name"). Default value is equal to "Ory Kratos".
# kratos -c path/to/my/kratos/config.yml serve
courier:
sms:
from: '+12065550110'
Request configuration
# kratos -c path/to/my/kratos/config.yml serve
courier:
sms:
request_config:
url: https://api.twilio.com/2010-04-01/Accounts/YourAccountID/Messages.json
method: POST
body: file://./path/to/path/to/my/kratos/config/twilio.request.jsonnet
header:
'Content-Type': 'application/x-www-form-urlencoded'
auth:
type: basic_auth
config:
user: YourUsername
password: YourPass
The configuration consists of:
url
- the URL, which should be called (mandatory). It needs to be absolute, start with http:// or https:// scheme, and include path part - for example "https://api.sender.com/v1/message".method
- the HTTP method (GET, POST, ...) (mandatory)body
- URI of a JsonNet template, used to render the payload to send (optional). Use afile://path/to/body.jsonnet
URL for referring to local files. This property is ignored for HTTPmethod
s, which don't support sending of HTTP body payloads (TRACE).auth
- configuration of authentication and authorization mechanisms to be used by request
Courier binds the from
, to
, and body
variables into the JsonNet template.
These variables are available through a ctx
object. For example to create a
body looking like { to: "<some-number>" }
to be sent to the SMS provider
endpoint, the jsonnet template would look like this:
function(ctx) {
from: ctx.from,
to: ctx.to,
body: ctx.body
}
Authentication and Authorization
For auth
following mechanisms are supported:
- Authentication via an Api Key. Type must be set to
api_key
. - Authentication via Basic Authentication. Type must be set to
basic_auth
.
For api_key
the config looks as follows:
name: Some-Name
value: The-Value-of-My-Key
in: header # alternatively cookie
All properties are mandatory.
For basic_auth
the config looks as follows:
user: My-User
password: My-Pass-Value
All properties are mandatory.
Message templates
Ory Kratos comes with built-in templates. If you wish to define your own, custom templates, you can use two methods.
- Define each template individually through
templates
as shown above forrecovery.invalid
,recovery.valid
,verification.invalid
andverification.valid
. None of the configurations listed are mandatory and will always fallback to the build-in templates or what's defined bytemplate_override_path
. - Define
template_override_path
, as shown above, to indicate where your custom templates are located. This will become the<template-root>
for your custom templates, as indicated below.
Remote Templates
Templates can be added through http://
, file://
and base64://
URIs in the
configurations. The only mandatory fields are plaintext
and html
when
defining the body
key. All other keys are optional and will always fallback to
the built-in templates or the template_override_path
.
Template Override Path
email.subject.gotmpl
, email.body.gotmpl
and email.body.plaintext.gotmpl
are common template file names expected in the sub directories of the root
directory, corresponding to the respective methods for filling e-mail subject
and body. Both plain text and HTML templates are required. The courier uses them
as
alternatives
for fallback.
Creating Templates
Templates use the golang template engine in the
text/template
package for rendering theemail.subject.gotmpl
andemail.body.plaintext.gotmpl
templates, and thehtml/template
package for rendering theemail.body.gotmpl
template: https://pkg.go.dev/text/template > https://pkg.go.dev/html/templateTemplates can use the Sprig library, which provides more than 100 commonly used template functions: http://masterminds.github.io/sprig/
- recovery: recovery email templates directory, expected to be located in
<root_directory>/recovery
- valid: sub directory, expected to be located in
<template-root>/recovery/valid
, containing templates with variablesTo
,RecoveryURL
andIdentity
for validating a recovery - invalid: sub directory, expected to be located in
<template-root>/recovery/invalid
, containing templates with variablesTo
for invalidating a recovery
- valid: sub directory, expected to be located in
- verification: verification email templates directory, expected to be
located in
<root_directory>/verification
- valid: sub directory, expected to be located in
<template-root>/verification/valid
, containing templates with variablesTo
,VerificationURL
andIdentity
for validating a verification - invalid: sub directory, expected to be located in
<template-root>/verification/invalid
, containing templates with variablesTo
for invalidating a verification
- valid: sub directory, expected to be located in
Hi, please verify your account by clicking the following link:
<a href="{{ .VerificationURL }}">{{ .VerificationURL }}</a>
Hi, please verify your account by clicking the following link: {{ .VerificationURL }}
If you're running multiple instances of Kratos and separate courier job, make sure to provide templates to all instances (both Kratos and courier).
The Identity attribute
To be able to customize the content of templates based on the identity of the
recipient of the e-mail, the identity has been made available as Identity
.
This object is a map containing all the attributes of an identity, such as id
,
state
, recovery_addresses
, verifiable_addresses
and traits
.
Nested templates
You can use nested templates to render email.subject.gotmpl
,
email.body.gotmpl
and email.body.plaintext.gotmpl
templates.
Example: i18n customization
Using nested templates, you can either use in-line template definitions, or as
in this example, use separate templates. In this example, we will define the
email body for recovery e-mails. Assuming that we've an attribute named lang
that contains the required language in the traits
of the identity, we can
define our templates as indicated below.
{{- if eq .Identity.traits.language "de" -}}
{{ template "email.body.de.gotmpl" . }}
{{- else -}}
{{ template "email.body.en.gotmpl" . }}
{{- end -}}
<a href="{{ .RecoveryURL }}">{{.RecoveryURL }}</a>
Hallo {{ upper .Identity.traits.firstName }},
Um Ihr Konto wiederherzustellen, klicken Sie bitte auf den folgenden Link:
Hello {{ upper .Identity.traits.firstName }},
to recover your account, please click on the link below:
As indicated by the example, we need a root template, which is the
email.body.gotmpl
template, and then we define sub templates that conform to
the following pattern: email.body*
. You can also see that the Identity
of
the user is available in all templates, and that you can use Sprig functions
also in the nested templates.
Nested templates with remote templates
When remote templates are used in Kratos, the dynamics of loading nested templates change. The templates can't reference templates outside itself as with templates loaded from a singular directory.
The template will need to contain the nested templates in the same file. See below for an example.
courier:
templates:
verification:
email:
body:
plaintext: https://some-remote-template/tmp.gotmpl
html: https://some-remote-template/tmp.gotmpl
Our template:
{{define "en_US"}}
{{ $l := cat "lang=" .lang }}
{{ nospace $l }}
{{end}}
{{- if eq .lang "en_US" -}}
{{ template "en_US" . }}
{{- end -}}