millions-van-17299
06/14/2025, 5:02 PMui.nodes
in the response when calling /self-service/login/browser?aal=aal2
?brief-egg-61061
06/14/2025, 5:02 PMbrief-egg-61061
06/14/2025, 5:02 PMui.nodes
array in the response when calling /self-service/login/browser?aal=aal2
through the API request itself. The ui.nodes
are generated by Ory Kratos based on your server-side configuration (such as enabled login methods, MFA requirements, etc.) and the current flow context. You can, however, control which authentication methods are available (e.g., password, OIDC, TOTP) and how they are configured by adjusting your Ory Identities (Kratos) configuration. This will affect which nodes are included in the response, but you cannot arbitrarily add, remove, or mutate nodes via the API call or query parameters. If you want to change how the UI is rendered, you should do so on the client side by filtering, grouping, or reordering the nodes as needed for your custom UI. The backend does not provide a way to customize the structure of ui.nodes
per request; it only groups nodes by method (e.g., "password", "oidc", "totp") using the group
property, which you can use to organize your UI rendering logic. This is a common point of discussion in the community, and while some have suggested more explicit grouping or form structures in the API response, this is not currently supported by Ory Kratos out of the box [Understanding UI nodes and error messages](https://www.ory.sh/docs/kratos/concepts/ui-user-interface) [Github Issue: Incorrect UI nodes for using OIDC and password flow together](https://github.com/ory/network/issues/143) [UI rendering is becoming extremely convoluted and complicated](https://github.com/ory/kratos/discussions/4152). In summary: - You cannot customize ui.nodes
via the API call. - You can influence which nodes appear by changing your server-side configuration. - For custom UI needs, process and render the nodes as you wish on the client side.millions-van-17299
06/14/2025, 5:04 PMbrief-egg-61061
06/14/2025, 5:04 PMbrief-egg-61061
06/14/2025, 5:04 PM/sessions/whoami
will only return user information if the session meets the required AAL (e.g., after 2FA is completed). If the session does not meet the required AAL, Ory will return a 403 error and will not leak user information. You can enforce this by setting: yaml session: whoami: required_aal: highest_available
or, for specific flows like settings: yaml selfservice: flows: settings: required_aal: highest_available
With this configuration, user info is only accessible after the user has completed 2FA, and attempts to fetch it before that will be blocked [Step-up authentication](https://www.ory.sh/docs/kratos/mfa/step-up-authentication) [Check AAL](https://www.ory.sh/docs/identities/get-started/mfa#check-aal) [Configure multi-factor authentication in Ory Kratos](https://www.ory.sh/docs/kratos/self-hosted/mfa). If you are building a custom UI, you should also ensure that you do not display any user-identifying information until the session is verified to be at the required AAL. This approach is recommended to prevent account enumeration and information leakage [Github Issue: Ensure that login, registration and recovery do not leak identity information](https://github.com/ory/kratos/issues/133). Summary: Set required_aal: highest_available
for sensitive endpoints and flows to ensure user info is only accessible after 2FA is complete. This prevents leaking user emails or other sensitive data before full authentication.millions-van-17299
06/15/2025, 9:57 PMcode
method flow and passwordless_enabled
with email and/or sms, do emails and sms need to be unique? What about when using password flow together with mfa_enabled
code flow?brief-egg-61061
06/15/2025, 9:57 PMbrief-egg-61061
06/15/2025, 9:58 PMcode
method flow with passwordless_enabled
for email and/or SMS, the identifiers (email or phone number) must be unique per credential type. This means: - If you use email or phone number as the identifier for the code
credential (passwordless), each email or phone number can only be associated with one identity for that credential type. - The same applies to the password
credential: the identifier (email, username, or phone number) must be unique for that credential type. However, the same identifier (e.g., an email address) can be used across different credential types for the same or different identities. For example, one identity could have the same email as both a password
and a code
identifier, or two different identities could have the same email if one uses it for password
and the other for oidc
(OpenID Connect), but not for the same credential type. > Each credential - regardless of its type - has one or more identifiers attached to it. Each identifier is universally unique. > ... While credentials must be unique per type, there can be duplicates amongst multiple types > ... If you tried to create (or update) another identity with the same identifier (<mailto:john.doe@acme.com|john.doe@acme.com>
), the system would reject the request with a 409 Conflict state. > [Credentials - Ory Kratos Concepts](https://www.ory.sh/docs/kratos/concepts/credentials) When using the password
flow together with mfa_enabled
code flow (for example, password login plus code-based MFA), the uniqueness requirement still applies per credential type. So, the email or phone number used as an identifier for the password
credential must be unique among all identities for password
, and the same for the code
credential. Summary: - Email and phone numbers must be unique per credential type (e.g., unique for all identities using code
, unique for all identities using password
). - The same identifier can be used across different credential types (e.g., the same email for both password
and oidc
). - This applies whether you use passwordless (code
with passwordless_enabled
) or combine password
with mfa_enabled
code flow. If you need email or phone number uniqueness to be scoped differently (e.g., per OIDC provider), this is not supported—identifiers are globally unique per credential type [kratos, login with different oidc providers, with same email address](https://github.com/ory/kratos/discussions/4162).millions-van-17299
06/16/2025, 4:27 PMbrief-egg-61061
06/16/2025, 4:27 PMbrief-egg-61061
06/16/2025, 4:27 PMidentity.schema.json
for MFA where the first factor is username and password and the second factor is a one-time code (OTP) sent to either email or phone number. This is followed by a sample Kratos configuration YAML to enable this flow. --- ### identity.schema.json
This schema allows users to log in with a username and password, and then receive an OTP code for MFA via either email or phone number. json { "$id": "<https://example.com/registration.schema.json>", "$schema": "<http://json-schema.org/draft-07/schema#>", "title": "Person", "type": "object", "properties": { "traits": { "type": "object", "properties": { "username": { "type": "string", "minLength": 3, "<http://ory.sh/kratos|ory.sh/kratos>": { "credentials": { "password": { "identifier": true } } } }, "email": { "type": "string", "format": "email", "<http://ory.sh/kratos|ory.sh/kratos>": { "credentials": { "code": { "identifier": true, "via": "email" } }, "recovery": { "via": "email" }, "verification": { "via": "email" } } }, "phone": { "type": "string", "format": "tel", "<http://ory.sh/kratos|ory.sh/kratos>": { "credentials": { "code": { "identifier": true, "via": "sms" } }, "recovery": { "via": "sms" }, "verification": { "via": "sms" } } } }, "required": ["username"], "additionalProperties": false } } }
- This schema enables username/password login and allows OTP codes to be sent to either email or phone for MFA. - You can require either email or phone by adding them to the required
array as needed. References: [Customize identity schema: username and email and password](https://www.ory.sh/docs/kratos/manage-identities/customize-identity-schema#username-and-email-and-password) [Passwordless one-time code login identifier](https://www.ory.sh/docs/kratos/manage-identities/customize-identity-schema#passwordless-one-time-code-login-identifier) [MFA via SMS and Email](https://www.ory.sh/docs/kratos/mfa/mfa-via-sms) --- ### kratos.config.yaml
Below is a minimal configuration to enable password login and code-based MFA (OTP via email or SMS): yaml selfservice: methods: password: enabled: true code: enabled: true mfa_enabled: true flows: settings: required_aal: highest_available session: whoami: required_aal: highest_available
- This configuration enables both password and code methods. - mfa_enabled: true
for the code
method enables OTP as a second factor. - required_aal: highest_available
ensures that sensitive operations require the highest authentication level (i.e., after MFA is completed). References: [MFA via SMS and Email](https://www.ory.sh/docs/kratos/mfa/mfa-via-sms) [Configure multi-factor authentication in Ory Kratos](https://www.ory.sh/docs/kratos/self-hosted/mfa) --- If you need to configure the email/SMS delivery provider, refer to the [sending emails and SMS documentation](https://www.ory.sh/docs/kratos/emails-sms/sending-emails-smtp) for the required courier setup. If you need further customization or have a specific use case, please let me know!millions-van-17299
06/16/2025, 4:29 PMbrief-egg-61061
06/16/2025, 4:29 PMbrief-egg-61061
06/16/2025, 4:29 PMcode
credential (used for OTP), then each email or phone number can only be associated with one identity for the code
credential. - The same applies to the password
credential: the username must be unique for all identities using password
. It is not possible for two users to have the same email or phone number if those fields are used as identifiers for the code
credential. Ory Kratos enforces uniqueness of identifiers per credential type. If you try to create a second identity with the same email or phone number for the code
credential, you will get a conflict error [Credentials - Ory Kratos Concepts](https://www.ory.sh/docs/kratos/concepts/credentials). > Each credential - regardless of its type - has one or more identifiers attached to it. Each identifier is universally unique. ... While credentials must be unique per type, there can be duplicates amongst multiple types. So, if you want only the username to be unique and allow duplicate emails or phone numbers, you cannot use those fields as identifiers for the code
credential. If you use them for OTP delivery and as identifiers, they must be unique. If you need to allow duplicate emails/phones, you would have to not use them as identifiers for any credential type, which is not compatible with using them for passwordless or code-based login/MFA in Ory Kratos [kratos, login with different oidc providers, with same email address](https://github.com/ory/kratos/discussions/4162).millions-van-17299
06/17/2025, 7:34 AMPlease note that theWhat should be used instead ofparameter is deprecated when performing SMS 2FA. It will be removed in a future version. If the parameter is not included in the request, the user will see all their phone/email addresses from which to perform the flow.via
via
parameter? Give me correct identity schema json for MFAbrief-egg-61061
06/17/2025, 7:34 AMbrief-egg-61061
06/17/2025, 7:35 AMvia
parameter for SMS 2FA is deprecated and will be removed in a future version. The release notes state that if the parameter is not included, the user will be presented with all their phone/email addresses to choose from when performing the flow. However, the knowledge sources do not provide a concrete example of the updated identity schema JSON that omits the via
parameter, nor do they specify what should be used in its place. The documentation and issues confirm that the via
parameter is still present in current schema examples, but also highlight that its use for SMS recovery is not supported and can cause errors. For example, using "recovery": { "via": "sms" }
in the schema will result in a validation error, and only "email"
is accepted for recovery. For verification and code credentials, the via
parameter is still shown in examples, but the deprecation notice suggests it should be omitted for future compatibility [Setting up identity schema with "Recovery via SMS" throwing error](https://github.com/ory/kratos/issues/4262) [Cannot Verify Phone](https://github.com/ory/network/issues/406). In summary: - The knowledge sources confirm the deprecation of the via
parameter for SMS 2FA, but do not provide a new, correct identity schema JSON for MFA without it. - There is no explicit replacement for the via
parameter documented in the provided sources. - If you omit the via
parameter, Ory will present all available addresses to the user for 2FA, but the exact schema format is not shown in the sources. If you want to future-proof your schema, you should omit the via
parameter for SMS/email in the code
credential and verification
sections, but please note that the knowledge sources do not provide a validated example of this. I'm sorry, but the knowledge sources do not contain enough information to provide a definitive, correct identity schema JSON for MFA without the via
parameter.millions-van-17299
06/18/2025, 7:31 PMbrief-egg-61061
06/18/2025, 7:31 PMbrief-egg-61061
06/18/2025, 7:31 PMpassword
and code
(for SMS or email), and you can choose which method to use at the start of the login flow [Login flow methods](https://www.ory.sh/docs/kratos/self-service/flows/user-login). However, for multi-factor authentication (MFA), the typical flow is: - First factor: The user logs in with their primary credential (e.g., password). - Second factor: The user is then prompted for the second factor (e.g., OTP code via SMS). The sources do not describe a flow where you log in first with the code (SMS) and then with the password as a second factor. The standard MFA setup is password (or another primary method) first, then code as the second factor [MFA overview](https://www.ory.sh/docs/kratos/mfa/overview) [MFA via SMS](https://www.ory.sh/docs/kratos/mfa/mfa-via-sms). In summary: - You can log in with either password or code as the first factor, depending on your configuration. - For MFA, the expected flow is password first, then code as the second factor. - The knowledge sources do not mention a supported flow where you log in with code first and then password as the second factor. If you need a specific order or combination, you must configure your flows accordingly, but the sources do not provide details on reversing the typical MFA order.millions-van-17299
06/18/2025, 9:07 PMbrief-egg-61061
06/18/2025, 9:07 PMbrief-egg-61061
06/18/2025, 9:07 PMpassword
, oidc
, code
, etc.) and flows (like registration, login, recovery, settings, verification) must be set in your configuration file or through the Ory Console. For example, to enable password and OIDC login, you would configure: yaml selfservice: methods: password: enabled: true oidc: enabled: true
If you do not explicitly enable a method or flow, it will not be available to users. The documentation provides configuration examples for each method and flow, and there is no mention of any self-service methods or flows being enabled by default [Registration flow methods](https://www.ory.sh/docs/kratos/self-service/flows/user-registration) [Login flow methods](https://www.ory.sh/docs/kratos/self-service/flows/user-login) [Settings flow methods](https://www.ory.sh/docs/kratos/self-service/flows/user-settings). Summary: You must explicitly enable the self-service methods and flows you want; they are not enabled by default.millions-van-17299
06/19/2025, 8:06 AMbrief-egg-61061
06/19/2025, 8:06 AMbrief-egg-61061
06/19/2025, 8:07 AMExpiresInMinutes
variable in your email template. For the login_code.valid
template, the following variables are available: - `To`: the email address to whom the email is sent - `LoginCode`: the login code - `Identity`: the identity of the email address - `ExpiresInMinutes`: the expiration time of the code in minutes You can use {{ .ExpiresInMinutes }}
in your template to display the expiration time. For example: Your login code is: {{ .LoginCode }} This code will expire in {{ .ExpiresInMinutes }} minutes.
This will render the expiration time in the email sent to the user [Available variables for templates](https://www.ory.sh/docs/kratos/emails-sms/custom-email-templates#available-variables).millions-van-17299
06/19/2025, 8:08 AMkratos-1 | time=2025-06-19T08:04:16Z level=error msg=Unable to get email body from template. audience=application error=map[message:template: email.body.gotmpl:40:35: executing "email.body.gotmpl" at <.ExpiresInMinutes>: can't evaluate field ExpiresInMinutes in type *email.LoginCodeValidModel stack_trace:stack trace could not be recovered from error type template.ExecError] message_from=no-reply@ory.kratos.sh message_id=300e4d5a-6634-47e7-aee7-0581cb5cdabd message_nid=aa6f7269-be76-4297-8ebe-dc73527b9018 message_subject=Mobile Stock Take Login Code message_template_type=login_code_valid message_type=email service_name=Ory Kratos service_version=v1.3.1 smtp_server=mailslurper:1025 smtp_ssl_enabled=true
Kratos v1.3.1brief-egg-61061
06/19/2025, 8:08 AMbrief-egg-61061
06/19/2025, 8:08 AMexecuting "email.body.gotmpl" at <.ExpiresInMinutes>: can't evaluate field ExpiresInMinutes in type *email.LoginCodeValidModel
indicates that the template is trying to use the variable ExpiresInMinutes
, but it is not available in the context for the login_code.valid
template in your version of Ory Kratos (v1.3.1). According to the [official documentation](https://www.ory.sh/docs/kratos/emails-sms/custom-email-templates#available-variables), ExpiresInMinutes
should be available for the login_code.valid
template. However, your error suggests that in Kratos v1.3.1, this variable is not actually present in the template context for this message type. What you can do: - Double-check that you are using the correct template type (login_code.valid
). - If you are on Ory Kratos v1.3.1 and the variable is missing, it may be a bug or a documentation mismatch for this version. - As a workaround, you may need to omit {{ .ExpiresInMinutes }}
from your template until this variable is supported for login_code.valid
in your version. Summary: Although the documentation lists ExpiresInMinutes
as available for login_code.valid
, your error and the current knowledge sources indicate it is not present in Kratos v1.3.1 for this template. You will need to remove or replace {{ .ExpiresInMinutes }}
in your template to resolve the error [Available variables for templates](https://www.ory.sh/docs/kratos/emails-sms/custom-email-templates#available-variables).millions-van-17299
06/19/2025, 8:09 AMbrief-egg-61061
06/19/2025, 8:09 AMbrief-egg-61061
06/19/2025, 8:09 AMcode
method), you typically submit a request body with the method
set to "code"
and include the relevant identifier (such as email
or identifier
). To trigger a resend, you include a field like resend
set to "code"
. Here is an example of a request body to resend a code during a login flow: json { "method": "code", "identifier": "<mailto:user@example.com|user@example.com>", "resend": "code", "csrf_token": "your-csrf-token" }
- `method`: Must be "code"
to indicate the code-based login. - `identifier`: The user's email or phone number (depending on your configuration). - `resend`: Set to "code"
to trigger resending the code. - `csrf_token`: Required for browser flows. This structure is reflected in the UI nodes and API documentation, where a "Resend code" button is associated with a submit action containing the resend
field [Github Issue: Requesting a login code returns HTTP 400](https://github.com/ory/kratos/issues/4052). Note: The exact field names may vary depending on the flow (login, recovery, verification) and your identity schema, but the pattern of including resend: "code"
is consistent for resending codes.millions-van-17299
06/19/2025, 9:41 AMmethod: 'code', identifier: '<mailto:test@example.com|test@example.com>'
brief-egg-61061
06/19/2025, 9:41 AMbrief-egg-61061
06/19/2025, 9:42 AMmethod: 'code'
and `identifier: 'test@example.com'`: json { "id": "367c97cc-0499-47ec-9a3c-208d2c308445", "organization_id": null, "type": "api", "expires_at": "2024-08-22T10:19:49.24531247Z", "issued_at": "2024-08-22T10:09:49.24531247Z", "request_url": "<http://localhost:4433/self-service/login/api>", "active": "code", "ui": { "action": "<http://127.0.0.1:4433/self-service/login?flow=367c97cc-0499-47ec-9a3c-208d2c308445>", "method": "POST", "nodes": [ { "type": "input", "group": "default", "attributes": { "name": "csrf_token", "type": "hidden", "value": "", "required": true, "disabled": false, "node_type": "input" }, "messages": [], "meta": {} }, { "type": "input", "group": "default", "attributes": { "name": "identifier", "type": "hidden", "value": "<mailto:test@example.com|test@example.com>", "required": true, "disabled": false, "node_type": "input" }, "messages": [], "meta": { "label": { "id": 1070002, "text": "E-Mail", "type": "info", "context": { "title": "E-Mail" } } } }, { "type": "input", "group": "code", "attributes": { "name": "method", "type": "hidden", "value": "code", "disabled": false, "node_type": "input" }, "messages": [], "meta": {} }, { "type": "input", "group": "code", "attributes": { "name": "code", "type": "text", "required": true, "disabled": false, "node_type": "input" }, "messages": [], "meta": { "label": { "id": 1070013, "text": "Login code", "type": "info" } } }, { "type": "input", "group": "code", "attributes": { "name": "method", "type": "submit", "value": "code", "disabled": false, "node_type": "input" }, "messages": [], "meta": { "label": { "id": 1070005, "text": "Submit", "type": "info" } } }, { "type": "input", "group": "code", "attributes": { "name": "resend", "type": "submit", "value": "code", "disabled": false, "node_type": "input" }, "messages": [], "meta": { "label": { "id": 1070008, "text": "Resend code", "type": "info" } } } ], "messages": [ { "id": 1010014, "text": "An email containing a code has been sent to the email address you provided. If you have not received an email, check the spelling of the address and retry the login.", "type": "info" } ] }, "created_at": "2024-08-22T10:09:49.251261Z", "updated_at": "2024-08-22T10:09:49.251261Z", "refresh": false, "requested_aal": "aal1", "state": "sent_email", "transient_payload": {} }
This response includes the flow ID, UI nodes for entering the code, and a message indicating that an email with the code has been sent to the provided address [Requesting a login code returns HTTP 400](https://github.com/ory/kratos/issues/4052).millions-van-17299
06/19/2025, 9:43 AMmethod: 'code', identifier: '<tel:+4912345678|+4912345678>'
brief-egg-61061
06/19/2025, 9:43 AMbrief-egg-61061
06/19/2025, 9:43 AMmethod: 'code'
and identifier: '+4912345678'
(a phone number), Ory Kratos will process the request similarly to the email case, but will send the login code via SMS to the provided phone number, assuming your identity schema and configuration support code-based login via SMS. The response will include a login flow object with UI nodes for entering the code, and a message indicating that an SMS containing a code has been sent to the provided phone number. The structure of the response is similar to the email case, but the message will reference SMS instead of email. For example: json { "ui": { "nodes": [ // ... nodes for csrf_token, identifier, method, code input, submit, resend ... ], "messages": [ { "id": 1010015, "text": "An SMS containing a code has been sent to the phone number you provided. If you have not received an SMS, check the spelling of the number and retry the login.", "type": "info" } ] }, // ... other flow properties ... }
This behavior is confirmed by the documentation, which states that Ory will deliver a 6-digit code to an SMS gateway of your choice, such as Twilio or Amazon SNS, and the user will be prompted to enter the code to complete the login flow [MFA via SMS and Email](https://www.ory.sh/docs/kratos/mfa/mfa-via-sms). If your identity schema is configured to allow code login via SMS (i.e., the phone trait has "credentials": { "code": { "identifier": true, "via": "sms" } }
), this flow will work as described. If not, the request will result in an error. Summary: - The response structure is the same as for email, but the message will reference SMS. - The code will be sent to the provided phone number if your configuration supports it. - The user will see UI nodes to enter the code and options to submit or resend the code [MFA via SMS and Email](https://www.ory.sh/docs/kratos/mfa/mfa-via-sms).millions-van-17299
06/19/2025, 9:44 AMstate
property when code is sent via sms?brief-egg-61061
06/19/2025, 9:44 AMbrief-egg-61061
06/19/2025, 9:44 AMstate
property in the response is set to sent_email
according to the available knowledge sources. This is the same state used when a code is sent via email. The documentation and example responses indicate that the state
property can have values such as: - `choose_method`: ask the user to choose a method to sign in with - `sent_email`: the email has been sent to the user - `passed_challenge`: the request was successful and the login challenge was passed There is no mention of a separate sent_sms
state; the system uses sent_email
for both email and SMS code delivery [Kratos API Reference - getLoginFlow](https://www.ory.sh/docs/kratos/reference/api). Summary: The state
property is set to sent_email
when a code is sent via SMS during the login flow, based on the current documentation and API references.millions-van-17299
06/20/2025, 9:36 AMmillions-van-17299
06/20/2025, 9:37 AMchannels:
- id: sms
type: http
request_config:
url: <http://echoserver:80/sms>
method: POST
headers:
Content-Type: 'application/json'
Accept: 'application/json'
body: file:///etc/config/kratos/sms-body.jsonnet
auth:
type: api_key
config:
name: 'api-key'
value: 'BREVO_SMS_API_KEY'
in: header
Which variables do I have access to isnide sms-body.jsonnet
?brief-egg-61061
06/20/2025, 9:37 AMbrief-egg-61061
06/20/2025, 9:37 AMsms-body.jsonnet
file, you have access to the following variables via the ctx
object: - `recipient`: The recipient's phone number. - `body`: The message body. - `template_type`: The template type, e.g., verification_code
. - `template_data`: The template data, e.g., { "VerificationCode": "1234", Identity: { ... } }
. - `message_type`: The message type, e.g., sms
. A typical Jsonnet template for SMS might look like: jsonnet function(ctx) { To: ctx.recipient, Body: ctx.body, }
You can use these fields to construct the payload sent to your SMS provider. For more details, see the [Ory Kratos SMS documentation](https://www.ory.sh/docs/kratos/emails-sms/sending-sms). If you need to access specific template variables (like the code or identity), you can reference them through ctx.template_data
as appropriate for your template type [Send SMS to your users](https://www.ory.sh/docs/kratos/emails-sms/sending-sms).millions-van-17299
06/20/2025, 9:38 AMtemplate_data
populated?brief-egg-61061
06/20/2025, 9:38 AMbrief-egg-61061
06/20/2025, 9:38 AMtemplate_data
object is populated by Ory Kratos based on the template type and the context of the message being sent. For each type of message (such as verification code, recovery code, login code, etc.), Ory Kratos includes specific variables in template_data
that are relevant to that template. For example, for an SMS or email containing a verification code, template_data
will include fields like VerificationCode
, VerificationURL
, Identity
, and others, depending on the template type. The exact variables available for each template type are documented in the [available variables section](https://www.ory.sh/docs/kratos/emails-sms/custom-email-templates#available-variables): - For verification_code.valid
, template_data
includes: - To
- VerificationCode
- VerificationURL
- Identity
- ExpiresInMinutes
- For login_code.valid
, template_data
includes: - To
- LoginCode
- Identity
- ExpiresInMinutes
- For SMS, the message body is typically set to the code or message you want to send, and you can access these fields via ctx.template_data
in your Jsonnet template [Send SMS to your users](https://www.ory.sh/docs/kratos/emails-sms/sending-sms). In summary, Ory Kratos automatically populates template_data
with the variables relevant to the message type being sent, and you can reference these variables in your Jsonnet template for custom SMS or email delivery.millions-van-17299
06/20/2025, 9:39 AMbrief-egg-61061
06/20/2025, 9:39 AMbrief-egg-61061
06/20/2025, 9:39 AMtemplate_data
object in Ory Kratos. The documentation describes how Ory Kratos automatically populates template_data
with variables relevant to the message type, but it does not mention a supported way to inject additional custom variables per environment or per request [Send SMS to your users](https://www.ory.sh/docs/kratos/emails-sms/sending-sms) [Available variables for templates](https://www.ory.sh/docs/kratos/emails-sms/custom-email-templates#available-variables). If you need environment-specific values like a Sender ID, you may need to handle this logic within your Jsonnet template by referencing environment variables or by using different Jsonnet files/configurations per environment, but this approach is not documented in the provided sources.millions-van-17299
06/20/2025, 9:45 AMbrief-egg-61061
06/20/2025, 9:45 AMbrief-egg-61061
06/20/2025, 9:45 AMmillions-van-17299
06/22/2025, 3:03 PMlogin_code/valid/email.subject.gotmpl
template?brief-egg-61061
06/22/2025, 3:03 PMbrief-egg-61061
06/22/2025, 3:03 PMlogin_code/valid/email.subject.gotmpl
template, you can use the .Identity.traits
object. For example, if your identity schema includes a trait called first_name
, you can reference it in your template like this: Login code for {{ .Identity.traits.first_name }}
This approach is documented for customizing template content based on the identity of the recipient. The Identity
object is available in all templates and contains all the attributes defined in your identity schema, including traits
[Customizing template content for specific users](https://www.ory.sh/docs/kratos/emails-sms/custom-email-templates#customizing-template-content-for-specific-users).millions-van-17299
06/22/2025, 3:11 PMbrief-egg-61061
06/22/2025, 3:11 PMbrief-egg-61061
06/22/2025, 3:11 PMlifespan
property for the code
method in your Ory Identities (Kratos) configuration. This can be done via the Ory Console or by editing your configuration file. Example configuration in `identity-config.yaml`: yaml selfservice: methods: code: passwordless_enabled: true enabled: true config: lifespan: 15m # The code will be valid for 15 minutes
You can also set this using the Ory Console by navigating to Authentication → Passwordless login and adjusting the code lifespan setting [Email and SMS one-time-code authentication](https://www.ory.sh/docs/kratos/passwordless/one-time-code). The lifespan
value defines how long the code is valid (e.g., 15m
for 15 minutes, 1h
for one hour). If not set, it defaults to 1 hour [Account recovery and password reset configuration](https://www.ory.sh/docs/kratos/self-service/flows/account-recovery-password-reset#configuration).millions-van-17299
06/22/2025, 3:18 PMlifespan
in this block mean?
flows:
login:
ui_url: <http://localhost:3000/login>
lifespan: 30m
brief-egg-61061
06/22/2025, 3:18 PMbrief-egg-61061
06/22/2025, 3:18 PMlifespan
property in the flows.login
block specifies how long a login flow is valid after it is initiated. For example, with lifespan: 30m
, the user has 30 minutes to complete the login process before the flow expires and they need to start a new one. This setting controls the expiration time for the login flow, not the session or the code itself [Session-management > Session lifespan](https://www.ory.sh/docs/kratos/session-management/session-lifespan) [Github Issue: Account recovery and password reset configuration](https://github.com/ory/network/issues/409).brief-egg-61061
06/22/2025, 3:18 PMbrief-egg-61061
06/22/2025, 3:19 PMlifespan
property in the flows.login
block defines how long a login flow is valid after it is initiated. In your example: yaml flows: login: ui_url: <http://localhost:3000/login> lifespan: 30m
the login flow will expire 30 minutes after it starts. If the user does not complete the login within this time, the flow becomes invalid and they must start a new login flow [Session-management > Session lifespan](https://www.ory.sh/docs/kratos/session-management/session-lifespan) [Account recovery and password reset configuration](https://github.com/ory/network/issues/409).millions-van-17299
06/22/2025, 3:19 PMbrief-egg-61061
06/22/2025, 3:19 PMbrief-egg-61061
06/22/2025, 3:20 PMlifespan
in the login flow (flows.login.lifespan
) and the lifespan
in the code
method (methods.code.config.lifespan
) control different aspects of the authentication process: - flows.login.lifespan
: This sets how long the entire login flow is valid after it is initiated. If the user does not complete the login within this time, the flow expires and must be restarted. - methods.code.config.lifespan
: This sets how long the one-time code (OTP) sent to the user (via email or SMS) is valid. If the user does not use the code within this time, the code expires and a new one must be requested. Relationship: The knowledge sources do not specify a strict required relationship between these two values, but for a smooth user experience, the code lifespan should generally be less than or equal to the login flow lifespan. This ensures that the code does not outlive the flow itself, preventing situations where a user receives a valid code but cannot use it because the flow has already expired. For example, you might set: yaml flows: login: lifespan: 30m methods: code: config: lifespan: 15m
This way, the user has 30 minutes to complete the login flow, but each code is only valid for 15 minutes within that window. Summary: - The code lifespan should not exceed the login flow lifespan. - There is no enforced relationship, but aligning them as described above avoids user confusion and errors. This is supported by the configuration examples and explanations in the documentation [Account recovery and password reset configuration](https://www.ory.sh/docs/kratos/self-service/flows/account-recovery-password-reset#configuration) [Address verification](https://www.ory.sh/docs/kratos/self-service/flows/verify-email-account-activation).millions-van-17299
06/22/2025, 3:46 PMemail.body.gotmpl
• email.body.plaintext.gotmpl
I am testing with mailslurper and I only see plaintext emails being receivedbrief-egg-61061
06/22/2025, 3:46 PMbrief-egg-61061
06/22/2025, 3:46 PMemail.body.gotmpl
(HTML) and email.body.plaintext.gotmpl
(plain text) templates in your login_code/valid
directory, Ory Kratos will attempt to send both versions as alternatives in the email. However, whether you see the HTML or plain text version in your email client (in this case, MailSlurper) depends on how the client renders multipart emails. According to the documentation, both HTML and plain text templates are required, and the mail courier uses them as alternatives for fallback. If the email client (MailSlurper) does not render or display the HTML part, you will only see the plain text version [Email templates in self-hosted Ory Kratos](https://www.ory.sh/docs/kratos/self-hosted/mail-courier-selfhosted#template-override-path) [Mandatory template formats](https://www.ory.sh/docs/kratos/emails-sms/custom-email-templates#mandatory-template-formats). Summary: - Ory Kratos sends both HTML and plain text versions if both templates are present. - If you only see the plain text email in MailSlurper, it is likely due to how MailSlurper displays emails, not an issue with your Ory Kratos configuration. - To verify the HTML template, try viewing the raw email source in MailSlurper or test with a different email client that supports HTML rendering.millions-van-17299
06/22/2025, 5:13 PMbrief-egg-61061
06/22/2025, 5:13 PMbrief-egg-61061
06/22/2025, 5:13 PMmillions-van-17299
06/23/2025, 11:50 AMbrief-egg-61061
06/23/2025, 11:50 AMbrief-egg-61061
06/23/2025, 11:50 AMcode
credential (used for passwordless login) with email as the identifier, Ory Kratos enforces that each email address can only be associated with one identity for the code
credential type. If you try to register or update another identity with the same email for the code
credential, the system will reject the request with a conflict error [Credentials - Ory Kratos Concepts](https://www.ory.sh/docs/kratos/concepts/credentials). > Each credential - regardless of its type - has one or more identifiers attached to it. Each identifier is universally unique. ... While credentials must be unique per type, there can be duplicates amongst multiple types. So, for passwordless login via email, the email address must be unique among all identities using that login method.millions-van-17299
06/23/2025, 11:51 AMcode
passwordless login, can I have a user with multiple emails as identifiers?brief-egg-61061
06/23/2025, 11:51 AMbrief-egg-61061
06/23/2025, 11:52 AMcode
method. This is done by defining the emails
trait as an array in your identity schema, and marking each email as an identifier for the code
credential. Here is an example from the documentation: json { "$id": "<https://example.com/registration.schema.json>", "$schema": "<http://json-schema.org/draft-07/schema#>", "title": "Person", "type": "object", "properties": { "traits": { "type": "object", "properties": { "emails": { "type": "array", "items": { "type": "string", "format": "email", "<http://ory.sh/kratos|ory.sh/kratos>": { "credentials": { "code": { "identifier": true, "via": "email" } } } } } } } } }
With this schema, each email in the emails
array can be used as an identifier for passwordless login via the code
method [Customize identity schemas: Multiple identifiers](https://www.ory.sh/docs/kratos/manage-identities/customize-identity-schema#multiple-identifiers) [Customize identity schemas: Examples](https://www.ory.sh/docs/kratos/manage-identities/customize-identity-schema#examples). Note: Each email address must still be unique across all identities for the code
credential type.millions-van-17299
06/23/2025, 11:54 AMcode
method with mfa_enabled: true
? User logs in with username and password, then with codebrief-egg-61061
06/23/2025, 11:54 AMbrief-egg-61061
06/23/2025, 11:54 AMcode
method with mfa_enabled: true
, the typical flow is that the user first logs in with their primary credential (such as username and password), and then completes a second authentication step by entering a code (OTP) sent via email or SMS. This is a standard multi-factor authentication (MFA) setup in Ory Kratos. If your identity schema allows multiple emails as identifiers for the code
credential (for example, using an array of emails), the user can choose which email to receive the OTP code on during the MFA step. However, each email used as an identifier for the code
credential must still be unique across all identities [Customize identity schemas: Multiple identifiers](https://www.ory.sh/docs/kratos/manage-identities/customize-identity-schema#multiple-identifiers). So, in this scenario: - The user logs in with their username and password (first factor). - For the second factor, Ory Kratos will prompt for a code sent to one of the user's registered email addresses (or phone numbers, if configured). - The user can select from their available identifiers (emails/phones) to receive the code, provided your schema supports multiple identifiers. Summary: Yes, you can use multiple emails as identifiers for the code
method with mfa_enabled: true
. The user logs in with username and password, then completes MFA with a code sent to one of their unique email addresses configured as identifiers for the code
credential [Customize identity schemas: Multiple identifiers](https://www.ory.sh/docs/kratos/manage-identities/customize-identity-schema#multiple-identifiers).millions-van-17299
07/02/2025, 10:24 AMbrief-egg-61061
07/02/2025, 10:24 AMbrief-egg-61061
07/02/2025, 10:24 AMlogin_code/valid/email.body.gotmpl
template that uses if-else logic to render different content based on the user's language trait (identity.traits.language
). This approach is documented in the Ory Kratos documentation for customizing template content and internationalization (i18n): gotmpl {{- if eq .Identity.traits.language "DE" -}} Hallo {{ .Identity.traits.first_name }}, Ihr Login-Code lautet: {{ .LoginCode }} {{- else -}} Hello {{ .Identity.traits.first_name }}, Your login code is: {{ .LoginCode }} {{- end -}}
This template checks if the user's language trait is "DE"
(German) and renders a German message; otherwise, it defaults to English. You can adapt the trait name and message as needed for your schema. This pattern is described in the documentation for translated templates (i18n) and customizing template content for specific users [Customizing template content for specific users](https://www.ory.sh/docs/kratos/emails-sms/custom-email-templates#customizing-template-content-for-specific-users).millions-van-17299
07/02/2025, 10:26 AMbrief-egg-61061
07/02/2025, 10:26 AMbrief-egg-61061
07/02/2025, 10:26 AM{{-
or `-}}`—are used to control whitespace trimming. Specifically: - {{-
(dash on the left): Trims all whitespace to the left of the tag. - -}}
(dash on the right): Trims all whitespace to the right of the tag. This helps you control the formatting of the rendered output, making it easier to avoid unwanted spaces or newlines in your emails. This behavior is part of the Go template engine, which Ory uses for rendering email templates [Creating templates](https://www.ory.sh/docs/kratos/emails-sms/custom-email-templates#creating-templates).millions-van-17299
07/07/2025, 1:15 PMbrief-egg-61061
07/07/2025, 1:15 PMbrief-egg-61061
07/07/2025, 1:15 PMidentity
into the context. For example, when defining a webhook or action that runs after a self-service flow (such as registration or login), the identity
object is available as ctx.identity
in your Jsonnet template. This allows you to access traits and other identity properties: jsonnet function(ctx) { user_id: ctx.identity.id, email: ctx.identity.traits.email, // ...other fields... }
This is documented in the Ory webhook and actions guides, which show that the identity
object is available in the ctx
context for after-hooks and actions [Define HTTP request](https://www.ory.sh/docs/guides/integrate-with-ory-cloud-through-webhooks#define-http-request) [Jsonnet templating](https://www.ory.sh/docs/guides/integrate-with-ory-cloud-through-webhooks#jsonnet-templating). Note: For email/SMS courier templates (not webhooks/actions), the context is different and you typically access identity data via ctx.template_data.Identity
in Jsonnet, depending on the template type [Send SMS to your users](https://www.ory.sh/docs/kratos/emails-sms/sending-sms). Summary: - For webhooks/actions: access identity as ctx.identity
. - For courier templates: access identity as ctx.template_data.Identity
(if available for that template type).millions-van-17299
07/07/2025, 1:16 PMbrief-egg-61061
07/07/2025, 1:16 PMbrief-egg-61061
07/07/2025, 1:16 PMif ... then ... else ...
syntax. For example: jsonnet local claims = std.extVar('claims'); { identity: { traits: { [if "website" in claims then "website" else null]: claims.website, }, }, }
This example shows how to conditionally include a field based on whether "website"
exists in the claims
object [Write a Jsonnet data mapper](https://www.ory.sh/docs/kratos/social-signin/data-mapping#write-a-jsonnet-data-mapper). You can use similar if-else logic for more complex conditions in your Jsonnet code.millions-van-17299
07/07/2025, 1:17 PMtemplateId
based on identity.traits.language
property (1 for EN, 2 for DE, 3 for others)brief-egg-61061
07/07/2025, 1:17 PMbrief-egg-61061
07/07/2025, 1:17 PMtemplateId
in a Jsonnet file based on the value of identity.traits.language
, using if-else logic: jsonnet function(ctx) { templateId: if ctx.identity.traits.language == "EN" then 1 else if ctx.identity.traits.language == "DE" then 2 else 3 }
This uses standard Jsonnet conditional expressions, as shown in the documentation for data mapping and Jsonnet usage [Write a Jsonnet data mapper](https://www.ory.sh/docs/kratos/social-signin/data-mapping#write-a-jsonnet-data-mapper). - ctx.identity.traits.language
accesses the language property of the identity. - The templateId
is set to 1
for English, 2
for German, and 3
for any other language. You can use this pattern in webhooks, actions, or any context where the identity
object is available in Jsonnet [Jsonnet templating](https://www.ory.sh/docs/guides/integrate-with-ory-cloud-through-webhooks#jsonnet-templating).millions-van-17299
07/07/2025, 1:27 PMjsonnetsecure: ERROR: RUNTIME ERROR: Field does not exist: identity\n\tfile:///etc/config/kratos/login_code.email.jsonnet
brief-egg-61061
07/07/2025, 1:28 PMbrief-egg-61061
07/07/2025, 1:28 PMjsonnetsecure: ERROR: RUNTIME ERROR: Field does not exist: identity
indicates that the identity
field is not available in the context when your Jsonnet template is being executed for the email courier. According to the knowledge sources, for email and SMS delivery via courier, the variables available in the Jsonnet context are accessed through the ctx
object, and identity information (if available) is typically found under ctx.template_data.Identity
(not ctx.identity
). For example, the documentation for SMS delivery shows: - `ctx.template_data`: The template data, e.g., { "VerificationCode": "1234", Identity: { ... } }
- To access the identity, you would use ctx.template_data.Identity
[Send SMS to your users](https://www.ory.sh/docs/kratos/emails-sms/sending-sms). How to fix your Jsonnet: Instead of: jsonnet ctx.identity.traits.language
use: jsonnet ctx.template_data.Identity.traits.language
Example: jsonnet function(ctx) { templateId: if ctx.template_data.Identity.traits.language == "EN" then 1 else if ctx.template_data.Identity.traits.language == "DE" then 2 else 3 }
Summary: In courier Jsonnet templates, use ctx.template_data.Identity
to access identity fields, not ctx.identity
. The error occurs because identity
is not present at the top level of ctx
in this context [Send SMS to your users](https://www.ory.sh/docs/kratos/emails-sms/sending-sms).millions-van-17299
07/07/2025, 1:30 PMjsonnetsecure: ERROR: RUNTIME ERROR: Field does not exist: Identity\n\tfile:///etc/config/kratos/login_code.email.jsonnet
brief-egg-61061
07/07/2025, 1:30 PMbrief-egg-61061
07/07/2025, 1:31 PMjsonnetsecure: ERROR: RUNTIME ERROR: Field does not exist: Identity
means that Identity
is not present in ctx.template_data
in your current context. Based on the knowledge sources, the variables available in ctx.template_data
depend on the template type. For the login_code.valid
template, the documentation states that Identity
should be available in template_data
[Available variables for templates](https://www.ory.sh/docs/kratos/emails-sms/custom-email-templates#available-variables). However, your error suggests that in your specific setup or Ory version, it is not present. Possible reasons and what you can do: 1. Template Type Limitation or Version Difference: The knowledge sources indicate Identity
should be present, but if it is not, it may be due to a version difference or a bug. The documentation does not provide a workaround for when Identity
is missing. 2. Check What Is Available: You can debug what fields are present by temporarily using a Jsonnet template like: jsonnet function(ctx) ctx
This will output the entire context and help you see what fields are actually available. 3. Fallback: If Identity
is not available in your context, you cannot access it in the Jsonnet template for that message type, and the knowledge sources do not provide a supported workaround. Summary: Although the documentation says Identity
should be available in ctx.template_data
for login_code.valid
, your error shows it is not. The knowledge sources do not contain enough information to resolve this discrepancy or provide an alternative way to access identity traits in this context.millions-van-17299
07/07/2025, 1:32 PMbrief-egg-61061
07/07/2025, 1:32 PMbrief-egg-61061
07/07/2025, 1:32 PMgotmpl
) files used for email and SMS content, not to Jsonnet templates used for HTTP/SMS/Email courier payloads. For Jsonnet templates (used in courier HTTP/SMS/email integrations), the available variables are provided via the ctx
object, and the structure of ctx
is different from the Go template context. In Jsonnet, you typically have access to fields like ctx.recipient
, ctx.body
, ctx.template_type
, and ctx.template_data
. The contents of ctx.template_data
depend on the flow and may not always include the full Identity
object, even if it is available in Go templates [Send SMS to your users](https://www.ory.sh/docs/kratos/emails-sms/sending-sms). Summary: - The variables listed in the "available variables for templates" documentation are for Go templates (gotmpl
), not Jsonnet. - Jsonnet templates use the ctx
object, and the available fields may differ. - If you need to know what is available in Jsonnet, inspect the ctx
object as described in the courier integration documentation.