square-napkin-92357
06/02/2025, 9:12 AMbrief-egg-61061
06/02/2025, 9:12 AMbrief-egg-61061
06/02/2025, 9:12 AMbash ory list workspaces ory list projects --workspace <workspace-id> ory get identity-config --project <project-id> --workspace <workspace-id> --format yaml > identity-config.yaml
2. Edit the configuration file (identity-config.yaml
) to enable one-time codes for MFA: yaml selfservice: methods: code: enabled: true mfa_enabled: true
3. Update your configuration: bash ory update identity-config --project <project-id> --workspace <workspace-id> --file identity-config.yaml
--- ### 3. Ensure Your Identity Schema Supports Email as a Code Identifier Your identity schema should mark the email trait as an identifier for the code
method: json { "traits": { "email": { "type": "string", "format": "email", "<http://ory.sh/kratos|ory.sh/kratos>": { "credentials": { "code": { "identifier": true, "via": "email" } } } } } }
This ensures that the system knows to send the one-time code to the user's email address for 2FA challenges. --- ### 4. What Users Experience - After logging in, users who have 2FA enabled will see a second authentication screen prompting them to enter a code sent to their email. - Users without a second factor configured can access settings after signing in with their password, but those with 2FA set up must complete the email code challenge to proceed. --- ### 5. Configure Email Delivery Make sure you have set up an SMTP provider so Ory can send the one-time codes to users’ email addresses. See the [sending emails documentation](https://www.ory.sh/docs/kratos/emails-sms/sending-emails-smtp) for details. --- Note: If you enable MFA via code to email, this method cannot be used for passwordless login at the same time. The code method for MFA and passwordless are mutually exclusive in Ory Kratos. --- For more details, see the official documentation on [MFA via email](https://www.ory.sh/docs/kratos/mfa/mfa-via-sms) and [enabling MFA](https://www.ory.sh/docs/identities/get-started/mfa).square-napkin-92357
06/02/2025, 9:14 AMcurl --request GET \
--url '<http://127.0.0.1:4455/kratos/self-service/login/browser?aal=aal2&refresh=true>' \
--header 'accept: application/json' \
--header 'cookie: {{oryKratosSession}}' \
--cookie '{{oryKratosSession}}'
i get messages
"messages": [
{
"id": 1010003,
"text": "Please confirm this action by verifying that it is you.",
"type": "info"
},
{
"id": 1010004,
"text": "Please complete the second authentication challenge.",
"type": "info"
}
]
brief-egg-61061
06/02/2025, 9:14 AMbrief-egg-61061
06/02/2025, 9:16 AMaal=aal2
and refresh=true
, you are explicitly requesting a step-up authentication (AAL2, e.g., 2FA) and also asking to refresh the session (i.e., re-authenticate the user, resetting the authenticated_at
time). This is a valid use case in Ory Kratos, especially for sensitive operations or when you want to ensure the user is present and completes a fresh 2FA challenge. The messages you receive: - 1010003: "Please confirm this action by verifying that it is you." - 1010004: "Please complete the second authentication challenge." These are standard UI messages for this scenario. The first message prompts the user to confirm their identity (often shown when a session refresh is required), and the second message instructs the user to complete the 2FA challenge (AAL2). It is expected to see both messages when both refresh=true
and aal=aal2
are set, as both flows are being triggered: session refresh and step-up authentication. This behavior is also discussed in the Ory community, where it is noted that the flow may contain messages for both "refresh" and "AAL2" scenarios when both parameters are present. The recommended approach is to handle both messages in your UI, or, as some implementations do, ignore the refresh
value if requested_aal
is aal2
and focus on the 2FA challenge for the user experience [[GitHub Issue Discussion](https://github.com/ory/network/issues/375)]. Summary: The messages you see are expected and indicate that the user must confirm their identity and complete the second authentication factor. This is the correct behavior for a login flow with both aal=aal2
and refresh=true
parameters set [[UI Message Codes](https://www.ory.sh/docs/kratos/concepts/ui-messages)]. If you want to customize the user experience, you can choose to display only the most relevant message to the user, depending on your application's needs.square-napkin-92357
06/02/2025, 9:18 AM"messages": [
{
"id": 1010003,
"text": "Please confirm this action by verifying that it is you.",
"type": "info"
},
{
"id": 1010004,
"text": "Please complete the second authentication challenge.",
"type": "info"
}
]
brief-egg-61061
06/02/2025, 9:18 AMbrief-egg-61061
06/02/2025, 9:18 AMjson { "id": 1010003, "text": "Please confirm this action by verifying that it is you.", "type": "info" }, { "id": 1010004, "text": "Please complete the second authentication challenge.", "type": "info" }
are standard informational messages from Ory Kratos when a login flow is initiated with both aal=aal2
(requesting two-factor authentication) and refresh=true
(requesting session refresh). This is expected behavior and not an error. Both messages are shown because both a session refresh and a step-up authentication are being requested at the same time. If you want to "fix" this from a user experience perspective (for example, to avoid showing redundant or confusing messages), the recommended approach—also discussed by Ory team members—is to ignore the refresh
value when requested_aal
is aal2
and focus your UI on the 2FA challenge. This means you can choose to display only the most relevant message to your users, typically the one about completing the second authentication challenge, and suppress the other in your frontend logic. This is a UI/UX decision and not a backend misconfiguration [[GitHub Issue Discussion](https://github.com/ory/network/issues/375)]. There is no backend "fix" required; the messages are expected for this flow. Adjust your frontend to handle or filter these messages as appropriate for your application.square-napkin-92357
06/03/2025, 2:18 AMcurl --request GET \
--url '<http://127.0.0.1:4455/kratos/self-service/login/browser?aal=aal2>' \
--header 'accept: application/json' \
--header 'cookie: {{oryKratosSession}}' \
--cookie '{{oryKratosSession}}'
and the response is
{
"id": "72daada3-ba9e-4411-a281-191a5ff9f1f1",
"organization_id": null,
"type": "browser",
"expires_at": "2025-06-04T02:17:35.455855596Z",
"issued_at": "2025-06-03T02:17:35.455855596Z",
"request_url": "<http://127.0.0.1:4455/self-service/login/browser?aal=aal2>",
"ui": {
"action": "<http://kratos:4433/self-service/login?flow=72daada3-ba9e-4411-a281-191a5ff9f1f1>",
"method": "POST",
"nodes": [
{
"type": "input",
"group": "default",
"attributes": {
"name": "csrf_token",
"type": "hidden",
"value": "93uVAICMk7jdyK42OX7zo2ZsVbWwBP2rKz3FJvZyeAnJZUg0Nf2UD0z4G4Y40+dpRkXWrfwtNu1lyqRyctKjfQ==",
"required": true,
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {}
},
{
"type": "input",
"group": "code",
"attributes": {
"name": "address",
"type": "submit",
"value": "<mailto:vnsuser001khanh2@gmail.com|vnsuser001khanh2@gmail.com>",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1010023,
"text": "Send code to <mailto:vnsuser001khanh2@gmail.com|vnsuser001khanh2@gmail.com>",
"type": "info",
"context": {
"address": "<mailto:vnsuser001khanh2@gmail.com|vnsuser001khanh2@gmail.com>",
"channel": "email"
}
}
}
}
],
"messages": [
{
"id": 1010004,
"text": "Please complete the second authentication challenge.",
"type": "info"
}
]
},
"created_at": "2025-06-03T02:17:35.463373Z",
"updated_at": "2025-06-03T02:17:35.463373Z",
"refresh": false,
"requested_aal": "aal2",
"state": "choose_method"
}
what is the next step?brief-egg-61061
06/03/2025, 2:18 AMbrief-egg-61061
06/03/2025, 2:19 AMaction
URL provided in the response (`http://kratos:4433/self-service/login?flow=72daada3-ba9e-4411-a281-191a5ff9f1f1`). Include the required fields: - csrf_token
(from the response) - The submit button for your email address Example cURL command: bash curl -X POST \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -d '{"csrf_token":"93uVAICMk7jdyK42OX7zo2ZsVbWwBP2rKz3FJvZyeAnJZUg0Nf2UD0z4G4Y40+dpRkXWrfwtNu1lyqRyctKjfQ==","method":"code"}' \ '<http://kratos:4433/self-service/login?flow=72daada3-ba9e-4411-a281-191a5ff9f1f1>'
(If your UI nodes require a different field name for the method or email, adjust accordingly.) 2. Check your email for the code After submitting, Ory Kratos will send a one-time code to your email address. 3. Submit the code to complete authentication The next UI step will prompt you to enter the code you received. You will POST the code (and the CSRF token) to the same flow action URL. Example: bash curl -X POST \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -d '{"csrf_token":"<your-csrf-token>","code":"<the-code-from-email>","method":"code"}' \ '<http://kratos:4433/self-service/login?flow=72daada3-ba9e-4411-a281-191a5ff9f1f1>'
If successful, your session will be upgraded to AAL2 and you will be authenticated with 2FA via email. This flow is described in the Ory documentation for 2FA and step-up authentication [[Check AAL](https://www.ory.sh/docs/identities/get-started/mfa#check-aal)] and [2FA integration](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-advanced-integration#two-factor-authentication). If you need to resend the code, look for a "Resend code" button or node in the UI nodes array. Summary: - POST to the action URL to send the code. - Check your email. - POST the code to the same action URL to complete 2FA. If you need more details on the exact field names, refer to the nodes
array in your flow response, as it defines the required form fields and their names [[UI nodes and messages](https://www.ory.sh/docs/kratos/concepts/ui-user-interface#human-readable-format)].