diff --git a/docs/kratos/bring-your-own-ui/05_custom-ui-basic-integration.mdx b/docs/kratos/bring-your-own-ui/05_custom-ui-basic-integration.mdx index cf726cbfb3..6d22eaa86a 100644 --- a/docs/kratos/bring-your-own-ui/05_custom-ui-basic-integration.mdx +++ b/docs/kratos/bring-your-own-ui/05_custom-ui-basic-integration.mdx @@ -756,7 +756,9 @@ the POST request payload. To learn more about customizing your identity schema p For more complex use cases you can pass more data to the flow using the optional `transient_payload` field. This data gets forwarded to webhooks without being persisted by Ory like identity traits do. To learn how to configure and use a webhook, please -have a look at the [Webhooks](../../guides/integrate-with-ory-cloud-through-webhooks.mdx) documentation. +have a look at the [Webhooks](../../guides/integrate-with-ory-cloud-through-webhooks.mdx) documentation. The transient payload +also reaches email templates: for a worked example that uses it to build branded, localized links, see +[Transient payload in templates](../emails-sms/05_custom-email-templates.mdx#transient-payload-in-templates). For browser applications you must send all cookies and the CSRF token in the request body. The CSRF token value is a hidden input field called `csrf_token`. diff --git a/docs/kratos/emails-sms/01_sending-emails-smtp.mdx b/docs/kratos/emails-sms/01_sending-emails-smtp.mdx index 8038e2d544..9474c9aff5 100644 --- a/docs/kratos/emails-sms/01_sending-emails-smtp.mdx +++ b/docs/kratos/emails-sms/01_sending-emails-smtp.mdx @@ -7,6 +7,7 @@ title: Email delivery configuration import Tabs from "@theme/Tabs" import TabItem from "@theme/TabItem" import CodeBlock from "@theme/CodeBlock" +import CodeFromRemote from "@theme/CodeFromRemote" ``` The Ory Network provides a default SMTP server for sending emails. Ory emails are sent from this address: @@ -428,39 +429,54 @@ service that doesn't provide an SMTP server. ``` -### Payload +The body is built from a Jsonnet template. See [Payload](#payload) for the `ctx` object the template receives and an example +template. -The payload of the HTTP request is a JSON object that's generated using a Jsonnet template. By default, the following Jsonnet -template is used: + + -```jsonnet -function(ctx) { - recipient: ctx.recipient, - template_type: ctx.template_type, - to: if "template_data" in ctx && "to" in ctx.template_data then ctx.template_data.to else null, - recovery_code: if "template_data" in ctx && "recovery_code" in ctx.template_data then ctx.template_data.recovery_code else null, - recovery_url: if "template_data" in ctx && "recovery_url" in ctx.template_data then ctx.template_data.recovery_url else null, - verification_url: if "template_data" in ctx && "verification_url" in ctx.template_data then ctx.template_data.verification_url else null, - verification_code: if "template_data" in ctx && "verification_code" in ctx.template_data then ctx.template_data.verification_code else null, - login_code: if "template_data" in ctx && "login_code" in ctx.template_data then ctx.template_data.login_code else null, - registration_code: if "template_data" in ctx && "registration_code" in ctx.template_data then ctx.template_data.registration_code else null, - subject: if "template_data" in ctx && "subject" in ctx.template_data then ctx.template_data.subject else null, - body: if "template_data" in ctx && "body" in ctx.template_data then ctx.template_data.body else null -} -``` +## Payload {#payload} -The courier passes the following object as the `ctx` parameter into the Jsonnet template: +When you use the HTTP delivery strategy, Ory Identities sends each email as an HTTP request whose body is built from a Jsonnet +template. The template receives a `ctx` object that Ory Identities constructs for every message. -| Variable | Type | Description | -| --------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `recipient` | String | The email address of the recipient. | -| `template_type` | String | The type of the template. See the [list of available templates for a full list](./05_custom-email-templates.mdx#built-in-templates) | -| `template_data` | Object | The data that should be included in the email. See the [list of variables for each template for a full list](./05_custom-email-templates.mdx#available-variables). | +### The `ctx` object -In most cases, the default payload should be sufficient. +The shape of `ctx` is defined by the `httpDataModel` struct in Ory Kratos. - - + + +The JSON tags on the struct are the field names available in `ctx`: + +| Field | Type | Description | +| ----------------- | ------ | -------------------------------------------------------------------------------------------------------------------- | +| `recipient` | String | The email address of the recipient. | +| `subject` | String | The rendered email subject. | +| `body` | String | The rendered plaintext body. | +| `html_body` | String | The rendered HTML body, when available. | +| `template_type` | String | The template type. See [Events that trigger an email](./05_custom-email-templates.mdx#events-that-trigger-an-email). | +| `template_data` | Object | The per-template variables. See [Available variables](./05_custom-email-templates.mdx#available-variables). | +| `message_type` | String | The message channel, for example `email`. | +| `request_headers` | Object | The HTTP headers from the flow request that triggered the email. | + +### Example Jsonnet template + +The following template is the example mailer configuration shipped with Ory Kratos. It maps the most common fields out of `ctx`. + + + +Read fields that the template you target needs from `ctx.template_data`, and read top-level fields such as `recipient` and +`template_type` directly from `ctx`. ## Troubleshooting diff --git a/docs/kratos/emails-sms/05_custom-email-templates.mdx b/docs/kratos/emails-sms/05_custom-email-templates.mdx index 06fa7ff3c9..b72ee16ee0 100644 --- a/docs/kratos/emails-sms/05_custom-email-templates.mdx +++ b/docs/kratos/emails-sms/05_custom-email-templates.mdx @@ -10,6 +10,7 @@ sidebar_label: Email templates import Tabs from "@theme/Tabs" import TabItem from "@theme/TabItem" import CodeBlock from "@theme/CodeBlock" +import CodeFromRemote from "@theme/CodeFromRemote" ``` Ory Identities comes with built-in templates for all messages sent by the system. You can replace the default templates with @@ -81,6 +82,47 @@ To learn more about registration via a one-time code, read the [one-time code](. ``` +## Events that trigger an email + +Ory Identities sends an email for each template type below. The template type (for example `recovery_code_valid`) is the value of +the `template_type` field in the [HTTP delivery payload](./01_sending-emails-smtp.mdx#payload). It differs from the template names +used elsewhere on this page (for example `recovery_code.valid`), which identify the template directory and layout. + +The following list of template types is imported directly from the Ory Kratos source code and stays in sync with it: + + + +The following table is maintained by hand. It documents when each email is sent and the condition that gates it. These semantics +are not represented in the source file above. + +| Template type | When Ory Identities sends it | Condition | +| ---------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | +| `recovery_code_valid` | A recovery flow is started for a known address (recovery method `code`). | Always, when recovery uses `code`. | +| `recovery_code_invalid` | A recovery flow is started for an address with no account (recovery method `code`). | Only if `selfservice.flows.recovery.notify_unknown_recipients` is `true`. | +| `recovery_valid` | A recovery flow is started for a known address (recovery method `link`). | Always, when recovery uses `link`. | +| `recovery_invalid` | A recovery flow is started for an address with no account (recovery method `link`). | Only if `selfservice.flows.recovery.notify_unknown_recipients` is `true`. | +| `verification_code_valid` | A verification flow is started for a known address (method `code`). | Always, when verification uses `code`. | +| `verification_code_invalid` | A verification flow is started for an address with no account (method `code`). | Only if `selfservice.flows.verification.notify_unknown_recipients` is `true`. | +| `verification_valid` | A verification flow is started for a known address (method `link`). | Always, when verification uses `link`. | +| `verification_invalid` | A verification flow is started for an address with no account (method `link`). | Only if `selfservice.flows.verification.notify_unknown_recipients` is `true`. | +| `login_code_valid` | A login flow sends a one-time login code. | When the one-time code login method is enabled. | +| `registration_code_valid` | A registration flow sends a one-time registration code. | When the one-time code registration method is enabled. | +| `verifiable_address_changed` | A verifiable address on an identity changes; sent to the previous address(es). | Only if the `notify_previous_addresses` hook is enabled. | +| `stub` | Not sent in production. | Test stub used by the Ory Kratos test suite. It does not correspond to a real email. | + +:::note + +The `stub` type appears in the embedded source above because it is part of the `TemplateType` enum, but it is a test stub used by +the Ory Kratos test suite and never produces a real email. + +::: + ## Using custom message templates Templates can be customized to fit your own branding and requirements. If you don't customize a specific template, the system @@ -212,7 +254,17 @@ For security reasons, these Sprig functions are disabled in the Ory Network: ### Available variables -The variables available for use in email templates change depending on the flow and the selected method: +The variables available for use in email templates change depending on the flow and the selected method. + +The tables below are maintained by hand. The authoritative list of fields for each template is the per-template model struct in +Ory Kratos, under [`courier/template/email/`](https://github.com/ory/kratos/tree/master/courier/template/email) — for example +[`recovery_code_valid.go`](https://github.com/ory/kratos/blob/master/courier/template/email/recovery_code_valid.go) and +[`verification_code_valid.go`](https://github.com/ory/kratos/blob/master/courier/template/email/verification_code_valid.go). If a +field is missing below, check the matching struct. + +In addition to the variables listed for each template, every template can access `TransientPayload` (see +[Transient payload in templates](#transient-payload-in-templates)) and `RequestURL` (the URL of the flow request that triggered +the email). ```mdx-code-block @@ -319,6 +371,21 @@ For the `registration_code.valid` template, the following variables are availabl | `Traits` | the provided traits as specified by the Identity Schema | | `ExpiresInMinutes` | the expiration time of the code in minutes | + + + + +For the `verifiable_address_changed` template, the following variables are available: + +| Variable | Description | +| --- | --- | +| `To` | the previous address the notification is sent to | +| `Identity` | the identity whose verifiable address changed | +| `ChangedAt` | the time the address changed | + +This template is sent only when the `notify_previous_addresses` hook is enabled. See +[Events that trigger an email](#events-that-trigger-an-email). + ``` @@ -542,3 +609,61 @@ This results into the following email to be sent to the user. BAR

``` + +#### Pass brand and language to email links + +Building on the mechanic above, this example uses the transient payload to carry presentation data - a brand name and a language - +through to the email template. It is useful when one Ory project serves several brands or locales and the email link must match +the context the user started in. + +The example reads a brand and a language when the flow is created, passes them in `transient_payload` when the flow is submitted, +and reads them back in the template to build a branded link. + +1. Read the values when you create the flow. For example, read the brand from the OAuth2 login request's client name and the + language from a URL query parameter: + + ```ts + const brand = flow.oauth2_login_request?.client?.client_name ?? "Example" + const lang = new URL(window.location.href).searchParams.get("lang") ?? "en" + ``` + +2. Inject the values into `transient_payload` when you submit the flow. For a registration flow, call `updateRegistrationFlow`; + for a verification flow, call `updateVerificationFlow`: + + ```ts + // Registration flow + await frontend.updateRegistrationFlow({ + flow: flow.id, + updateRegistrationFlowBody: { + method: "password", + traits: { email: "user@example.com" }, + password: "a-secure-password", + transient_payload: { brand, lang }, + }, + }) + + // Verification flow + await frontend.updateVerificationFlow({ + flow: flow.id, + updateVerificationFlowBody: { + method: "code", + email: "user@example.com", + transient_payload: { brand, lang }, + }, + }) + ``` + + For the underlying request shapes, see + [Submit registration flow](../bring-your-own-ui/05_custom-ui-basic-integration.mdx#submit-registration-flow). + +3. Read the values in the email template and build a branded, localized link: + + ```gotmpl + {{ $brand := index .TransientPayload "brand" }} + {{ $lang := index .TransientPayload "lang" }} + +

Verify your {{ $brand }} account:

+ Verify your email + ``` + +Because the payload lives only for the duration of the flow, supply these values again on every flow submission.