quiet-painting-32952
02/02/2025, 4:21 PMdocker run --rm -it -e ORY_PROJECT_API_KEY=<project_key> -e ORY_PROJECT=<projectID> oryd/ory tunnel --dev <http://localhost:5173>
But it still asks me to do a browser loginbrief-egg-61061
02/02/2025, 4:21 PMbrief-egg-61061
02/02/2025, 4:21 PMory tunnel --project <project-id-or-slug> --dev <http://localhost:5173>
This command format is consistent with the examples provided in the [Ory Tunnel documentation](https://www.ory.sh/docs/cli/ory-tunnel). If you want to use API keys for authentication in automated environments (like CI/CD pipelines) to avoid browser login, you should set the API keys as environment variables. Here's how you can do it: export ORY_PROJECT_API_KEY=<project-api-key> ory tunnel --project <project-id-or-slug> --dev --quiet <http://localhost:5173>
The --quiet
flag prevents the browser window from opening, as mentioned in the [Ory Tunnel documentation](https://www.ory.sh/docs/cli/ory-tunnel#connecting-in-automated-environments). If you're still experiencing issues after trying these suggestions, it might be helpful to check if you're using the latest version of the Ory CLI and ensure that your project ID and API key are correct.quiet-painting-32952
05/10/2025, 9:06 AMbrief-egg-61061
05/10/2025, 9:07 AMbrief-egg-61061
05/10/2025, 9:07 AMweb_hook
, which lets you call any external service or function you want. 2. Set up your webhook endpoint to receive and process the event. Example: Add a webhook to be called after login You can add a webhook using the Ory CLI like this: bash ory patch identity-config <project_id> \ --add '/selfservice/flows/login/after/hooks=[{"hook": "web_hook", "config": {"url": "<https://your-webhook-endpoint>"}}]'
This will call your function (the webhook) every time a user logs in successfully, including logins from the Experience UI. Where to configure: - In the Ory Console: Go to Authentication → Actions and add an "after login" action with the type "webhook" and your endpoint URL. - Or use the CLI as shown above. References: - [Ory Actions overview and configuration](https://www.ory.sh/docs/kratos/hooks/configure-hooks#available-actions) - [Customize login and registration behavior](https://www.ory.sh/docs/identities/sign-in/actions#customize-login-and-registration-behavior) If you need to trigger more complex logic, your webhook endpoint can execute any function you want when it receives the login event. Note: - The Experience UI is fully compatible with Ory Actions/hooks, so this approach works regardless of which UI is used. - If you need to trigger the function only for specific login methods (e.g., password, OIDC), you can further customize the hook configuration as described in the documentation. Let me know if you need an example webhook payload or more details!quiet-painting-32952
05/17/2025, 9:03 PMbrief-egg-61061
05/17/2025, 9:03 PMbrief-egg-61061
05/17/2025, 9:03 PMinactive
, they are prevented from logging in. You can change the state back to active
to allow the user to log in again. This can be done using the Ory SDKs or API by patching the identity's state. Here is an example using the JavaScript SDK: js import { Configuration, IdentityApi } from "@ory/client" import { JsonPatchOpEnum } from "@ory/client/api" const identity = new IdentityApi( new Configuration({ basePath: `https://${process.env.ORY_PROJECT_SLUG}.<http://projects.oryapis.com|projects.oryapis.com>`, accessToken: process.env.ORY_API_KEY, }), ) export async function setState( identityId: string, state: "active" | "inactive", ) { return await identity .patchIdentity({ id: identityId, jsonPatch: [ { op: JsonPatchOpEnum.Replace, value: state, path: "/state", }, ], }) .then(({ data }) => data) }
You can use a similar approach with other SDKs or directly via the API. This allows you to both deactivate and reactivate users as needed [Identity state](https://www.ory.sh/docs/identities/model/activate-deactivate-user-login-state/) [Disable User and Revoke all sessions](https://github.com/ory/network/issues/415).quiet-painting-32952
05/17/2025, 9:17 PMasync def create_user(self, email: str, user_id: str, company: Company) -> OryUser:
request_body = {
"schema_id": "<preset://email>",
"traits": {
"email": email
},
"metadata_public": {
"userId": user_id,
"tenantId": company.tenant_name
}
}
return await <http://self.httpClient.post|self.httpClient.post>(
f"{self.ory_base_uri}/admin/identities",
data=request_body,
headers={'Authorization': f"Bearer {self.config.ory_api_key}", 'Content-Type': 'application/json'},
map_to=OryUser
)
how do I get it to send an invite link via email to the user in questionbrief-egg-61061
05/17/2025, 9:17 PMbrief-egg-61061
05/17/2025, 9:17 PMpython response = await <http://self.httpClient.post|self.httpClient.post>( f"{self.ory_base_uri}/admin/recovery/link", data={ "identity_id": created_user.id, "expires_in": "12h" }, headers={ 'Authorization': f"Bearer {self.config.ory_api_key}", 'Content-Type': 'application/json' } ) recovery_link = response['recovery_link']
3. Send the recovery link to the user's email using your own email service. > It is currently not possible to send the recovery link directly to a user's email, this feature is tracked as [#595](https://github.com/ory/kratos/issues/595) [Invite users](https://www.ory.sh/docs/kratos/manage-identities/invite-users/). Summary: You must send the invite/recovery link yourself after generating it with the Admin API. Ory does not send this email automatically.quiet-painting-32952
05/18/2025, 9:26 AMbrief-egg-61061
05/18/2025, 9:26 AMbrief-egg-61061
05/18/2025, 9:26 AMquiet-painting-32952
05/18/2025, 10:27 AMbrief-egg-61061
05/18/2025, 10:27 AMbrief-egg-61061
05/18/2025, 10:27 AMpassword
method. This is a self-service flow designed for end-users to update their own passwords. The flow can be initiated for both browser-based and API clients. For API clients (e.g., mobile apps, backend services): 1. Initialize the settings flow (authenticated as the user whose password you want to change). 2. Submit the new password using the flow's action URL and the password
method. Example API call: bash curl -s -X POST -H "Accept: application/json" -H "Content-Type: application/json" \ -H "Authorization: bearer $sessionToken" \ -d '{"password": "newPassword123", "method": "password"}' \ "$actionUrl"
- $sessionToken
is the user's session token. - $actionUrl
is obtained from the initialized settings flow. - The payload must include the new password and set "method": "password"
. Note: - This flow is intended for users to change their own passwords. There is no documented admin API endpoint to directly set or change another user's password after creation; password changes are handled through the self-service settings flow [Settings and profile updates](https://www.ory.sh/docs/kratos/self-service/flows/user-settings/) [Update password](https://www.ory.sh/docs/kratos/self-service/flows/user-settings/#update-password-1). If you need to reset a user's password as an admin (for example, if they forgot it), you should use the account recovery flow instead. If you need to import or set a password during identity creation or update, you can do so by providing the password in the credentials field, but this is not intended for regular password changes [Importing credentials](https://www.ory.sh/docs/kratos/manage-identities/import-user-accounts-identities/#importing-credentials).quiet-painting-32952
05/18/2025, 10:31 AMexport const ory = new FrontendApi(
new Configuration({
basePath: Config.getAuthBasePath(),
baseOptions: {
withCredentials: true,
},
}),
);
brief-egg-61061
05/18/2025, 10:32 AMbrief-egg-61061
05/18/2025, 10:32 AMFrontendApi
instance, you should use the createBrowserSettingsFlow
(for browser-based apps) or createNativeSettingsFlow
(for native/mobile apps) method. These methods are the updated way to start a settings flow, as described in the SDK v1 upgrade guide [SDK V1 upgrade guide](https://www.ory.sh/docs/guides/upgrade/sdk-v1/#frontend-apis). Example for a browser-based app: js const flow = await ory.frontend.createBrowserSettingsFlow() .then(({ data }) => data)
Example for a native app: js const flow = await ory.frontend.createNativeSettingsFlow() .then(({ data }) => data)
After initializing the flow, you will receive a flow object containing the flow ID and the action URL. You can then use this information to submit the new password as described previously. References: - The method names and usage are confirmed in the [SDK V1 upgrade guide](https://www.ory.sh/docs/guides/upgrade/sdk-v1/#frontend-apis). - The flow for updating settings (including password) is described in the [settings flow documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-settings/#user-and-profile-settings-for-server-side-browser-clients). If you need to fetch an existing flow (for example, after a redirect), use: js const flow = await ory.frontend.getSettingsFlow({ id: flowId }) .then(({ data }) => data)
This approach is fully supported by the Ory SDK and aligns with the documented flows.quiet-painting-32952
05/18/2025, 10:59 AMconst flow = await ory.createBrowserSettingsFlow()
I get two nodes as a result of password group
{
"type": "input",
"group": "password",
"attributes": {
"name": "password",
"type": "password",
"required": true,
"autocomplete": "new-password",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1070001,
"text": "Password",
"type": "info"
}
}
},
{
"type": "input",
"group": "password",
"attributes": {
"name": "method",
"type": "submit",
"value": "password",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1070003,
"text": "Save",
"type": "info"
}
}
}
How do I reset the password now?brief-egg-61061
05/18/2025, 10:59 AMbrief-egg-61061
05/18/2025, 11:00 AMaction
URL provided in the flow's ui
object. The payload should include the new password and the method, as specified by the nodes. Here's how you can do it: 1. Extract the action URL and method from the flow: - flow.ui.action
(the URL to POST to) - flow.ui.method
(should be "POST") 2. Build the payload using the node attributes: - `"password"`: the new password you want to set - `"method"`: must be "password"
3. Send the POST request: js await ory.updateSettingsFlow({ flow: flow.id, updateSettingsFlowBody: { password: "your-new-password", method: "password" } })
Or, if you are submitting directly to the action URL (for example, from a form): js fetch(flow.ui.action, { method: flow.ui.method, credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ password: "your-new-password", method: "password" }) })
Summary: - Use the action
URL from the flow. - POST the new password and "method": "password"
as the payload. This process is described in the [settings flow payloads documentation](https://www.ory.sh/docs/kratos/self-service/flows/user-settings/#settings-flow-payloads) and the [update password section](https://www.ory.sh/docs/kratos/self-service/flows/user-settings/#update-password-1). If you encounter validation errors (e.g., password too short), they will be returned in the flow's response, attached to the relevant node as messages [Update password](https://www.ory.sh/docs/kratos/self-service/flows/user-settings/#update-password-1).quiet-painting-32952
05/18/2025, 11:05 AM{
"error": {
"id": "security_csrf_violation",
"code": 403,
"status": "Forbidden",
"request": "8d739fa2-d7e8-96e2-9c74-91e84e9f2318",
"reason": "Please retry the flow and optionally clear your cookies. The request was rejected to protect you from Cross-Site-Request-Forgery (CSRF) which could cause account takeover, leaking personal information, and other serious security issues.",
"details": {
"docs": "<https://www.ory.sh/kratos/docs/debug/csrf>",
"hint": "The anti-CSRF cookie was found but the CSRF token was not included in the HTTP request body (csrf_token) nor in the HTTP Header (X-CSRF-Token).",
"reject_reason": "The HTTP Cookie Header was set and a CSRF token was sent but they do not match. We recommend deleting all cookies for this domain and retrying the flow."
},
"message": "the request was rejected to protect you from Cross-Site-Request-Forgery"
}
}
brief-egg-61061
05/18/2025, 11:05 AMbrief-egg-61061
05/18/2025, 11:05 AMcsrf_token
. - You must extract this value from the flow and include it in your payload when submitting the password change. 2. Example: js await ory.updateSettingsFlow({ flow: flow.id, updateSettingsFlowBody: { password: "your-new-password", method: "password", csrf_token: csrfTokenFromUiNode // <-- this must be set! } })
Where csrfTokenFromUiNode
is the value from the node with name: "csrf_token"
in flow.ui.nodes
. 3. Why this is necessary: Ory uses the synchronizer token pattern for CSRF protection. The value in the hidden input (csrf_token
) must match the anti-CSRF cookie sent by the browser. If they do not match, or if the token is missing, you will get a 403 error [Defenses against cross-site request forgery](https://www.ory.sh/docs/kratos/concepts/security/#defenses-against-cross-site-request-forgery) [Custom UI CSRF handling](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration/#debug). 4. If you continue to get this error: - Try clearing your cookies for the domain and retrying the flow, as mismatched or stale cookies can also cause this error. - Make sure you are sending all cookies and the CSRF token together in the same request. References: - [Ory Kratos CSRF troubleshooting](https://www.ory.sh/docs/kratos/debug/csrf) - [Bring-your-own-UI CSRF handling](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration/#debug) - [Security concepts: CSRF](https://www.ory.sh/docs/kratos/concepts/security/#defenses-against-cross-site-request-forgery) Summary: You must extract the csrf_token
from the flow's UI nodes and include it in your request body when submitting the password change. This is required for CSRF protection in Ory.quiet-painting-32952
05/18/2025, 11:13 AMawait fetch(data.ui.action, {
method: data.ui.method,
credentials: "include",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
password: newPassword,
method: "password",
csrf_token: csrfNode.attributes.node_type === 'input' && csrfNode.attributes.value,
})
})
but I get a 403brief-egg-61061
05/18/2025, 11:13 AMbrief-egg-61061
05/18/2025, 11:14 AMattributes.name === "csrf_token"
and attributes.type === "hidden"
. Double-check that csrfNode
is the correct node and that csrfNode.attributes.value
is the actual token value. 2. Cookies: The request must include the session and CSRF cookies. Using credentials: "include"
is correct for browser fetch, but ensure that the cookies are actually present in the browser for the domain you are calling. If you are running on localhost with Ory Tunnel, make sure the tunnel is set up correctly and both your app and Ory are on the same (sub)domain, as cookies are domain-specific [CSRF troubleshooting](https://www.ory.sh/docs/troubleshooting/csrf/). 3. Payload: The payload must include: - `password`: the new password - `method`: "password" - `csrf_token`: the value from the CSRF node Example: js { password: newPassword, method: "password", csrf_token: csrfNode.attributes.value }
4. Clear Cookies and Retry: If you see the error "The HTTP Cookie Header was set and a CSRF token was sent but they do not match," clear your cookies for the domain and retry the flow, as stale or mismatched cookies can cause this [CSRF troubleshooting](https://www.ory.sh/docs/troubleshooting/csrf/). 5. Check Domain and Tunnel: If you are using Ory Tunnel, ensure your frontend and Ory are on the same domain (e.g., via the tunnel URL), as CORS and cookie issues can otherwise occur [Bring-your-own-ui CSRF debug](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration/#debug). Summary of what to check: - The CSRF token is correctly extracted and sent. - Cookies are present and sent with the request. - The frontend and Ory are on the same domain (especially in local dev). - If you get a mismatch error, clear cookies and retry the flow. If all of the above are correct and you still get a 403, it is likely a cookie or domain issue. Double-check your tunnel and browser cookie state. References: - [CSRF troubleshooting](https://www.ory.sh/docs/troubleshooting/csrf/) - [Bring-your-own-ui CSRF debug](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration/#debug)quiet-painting-32952
05/18/2025, 11:20 AMbrief-egg-61061
05/18/2025, 11:20 AMbrief-egg-61061
05/18/2025, 11:20 AMyaml selfservice: methods: totp: config: issuer: <http://ExampleIssuer.com|ExampleIssuer.com> enabled: true
You can do this via the Ory Console or CLI [Time-based one-time passwords (TOTP)](https://www.ory.sh/docs/kratos/mfa/totp/). 2. Initiate the settings flow using the Ory client In your application, use the Ory client to create a settings flow for the authenticated user: js const flow = await ory.createBrowserSettingsFlow();
This flow will include nodes for enabling 2FA methods (e.g., TOTP setup) if they are enabled in your config. 3. Present the 2FA setup UI to the user For TOTP, the flow will include a QR code and a field to enter the code from the authenticator app. For WebAuthn, the flow will prompt for device registration. The UI nodes for these are included in the flow response [Settings and profile updates](https://www.ory.sh/docs/kratos/self-service/flows/user-settings/). 4. Submit the 2FA setup data using the Ory client When the user completes the 2FA setup (e.g., scans the QR code and enters the code), submit the data back to Ory using the flow’s action URL or the client’s update method: js await ory.updateSettingsFlow({ flow: flow.id, updateSettingsFlowBody: { totp_code: "123456", // or the relevant field for your 2FA method method: "totp" } });
Make sure to include the CSRF token if required. Supported 2FA methods: - TOTP (Authenticator apps) - WebAuthn (FIDO2, YubiKey, biometrics) - SMS/Email one-time codes - Lookup Secrets (backup codes) References: - [2FA Overview](https://www.ory.sh/docs/kratos/mfa/overview/) - [TOTP Setup](https://www.ory.sh/docs/kratos/mfa/totp/) - [WebAuthn Setup](https://www.ory.sh/docs/kratos/mfa/webauthn-fido-yubikey/) - [Settings Flow](https://www.ory.sh/docs/kratos/self-service/flows/user-settings/) If you need a code example for a specific 2FA method, let me know which one!quiet-painting-32952
05/18/2025, 11:23 AMquiet-painting-32952
05/18/2025, 11:23 AMbrief-egg-61061
05/18/2025, 11:23 AMbrief-egg-61061
05/18/2025, 11:23 AMbash ory get identity-config --project <project-id> --workspace <workspace-id> --format yaml > identity-config.yaml
2. Edit the configuration file to enable the desired 2FA methods. For example, to enable TOTP and Lookup Secrets: yaml selfservice: methods: totp: config: issuer: <http://ExampleIssuer.com|ExampleIssuer.com> enabled: true lookup_secret: enabled: true
3. Update your configuration: bash ory update identity-config --project <project-id> --workspace <workspace-id> --file identity-config.yaml
--- You can enable other methods (like WebAuthn or One-Time Codes) in a similar way by setting their enabled
property to true
in the configuration file or toggling them in the Console. For more details and method-specific options, refer to the relevant sections in the Ory documentation [Multi-factor authentication](https://www.ory.sh/docs/identities/get-started/mfa/) [Time-based one-time passwords (TOTP)](https://www.ory.sh/docs/kratos/mfa/totp/) [WebAuthn and FIDO2 (YubiKey)](https://www.ory.sh/docs/kratos/mfa/webauthn-fido-yubikey/) [Lookup Secrets](https://www.ory.sh/docs/kratos/mfa/lookup-secrets/).quiet-painting-32952
05/18/2025, 11:29 AMbrief-egg-61061
05/18/2025, 11:29 AMbrief-egg-61061
05/18/2025, 11:29 AMrequired_aal
(Authentication Assurance Level) setting for flows like settings or login. If you set required_aal: highest_available
for a flow (such as the settings flow), Ory will require users who have a second factor configured to use it, but will not force users without a second factor to enroll. This means MFA is optional: users can choose to set up a second factor (like SMS/Email one-time codes) in their account settings, and only those who have set it up will be required to use it for higher-assurance actions. Users who have not set up a second factor can continue with just their primary authentication method. How to make one-time code MFA optional: 1. Enable one-time code MFA in the Ory Console under Authentication → Two-factor auth → One-time codes. 2. Set the flow’s required_aal
to highest_available
(not aal2
), for example in the settings flow: yaml selfservice: flows: settings: required_aal: highest_available
This ensures that only users who have enrolled a second factor will be prompted for it, while others are not forced to enroll or use it [Account recovery and password reset configuration](https://www.ory.sh/docs/kratos/self-service/flows/account-recovery-password-reset/#configuration). 3. Let users enroll their preferred second factor (SMS or Email) in their account settings. The method (SMS or Email) is determined by your identity schema and which traits (email, phone) are available and configured for the user [SMS and Email 2FA](https://www.ory.sh/docs/kratos/mfa/mfa-via-sms/). Where is the code sent? - The code is sent to the trait (email or phone) that the user has configured and that is marked as an identifier for the code
method in your identity schema. Users can only receive codes via channels (email or SMS) that are enabled and present in their identity. Summary: - Set required_aal: highest_available
to make MFA optional. - Users can choose to enroll in SMS/Email one-time code MFA in their settings. - The code is sent to the email or phone number configured in the user’s identity schema. If you want users to choose between SMS or Email, ensure both are present and properly configured in your identity schema. The user will then be able to enroll the method they prefer [SMS and Email 2FA](https://www.ory.sh/docs/kratos/mfa/mfa-via-sms/). If you need more granular control (such as letting users pick between multiple available channels at login), the knowledge sources do not provide explicit details on customizing this selection UI or logic.quiet-painting-32952
05/18/2025, 11:31 AMrequired_aal
to highest_available
(not aal2
), for example in the settings flow:brief-egg-61061
05/18/2025, 11:31 AMbrief-egg-61061
05/18/2025, 11:32 AMrequired_aal
to highest_available
(instead of aal2
), you need to update your Ory project’s configuration for the relevant self-service flow (such as the settings flow). You can do this either via the Ory Console or the Ory CLI. Using the Ory Console: 1. Go to the [Ory Console](https://console.ory.sh/). 2. Select your project. 3. Navigate to Authentication → Sessions or the specific flow you want to configure (e.g., Settings). 4. Find the Authenticator Assurance Level (AAL) setting and set it to highest_available
. 5. Save your changes. Using the Ory CLI: 1. Download your current configuration: bash ory get identity-config --project <project-id> --workspace <workspace-id> --format yaml > identity-config.yaml
2. Edit the configuration file and set the desired flow’s required_aal
to highest_available
. For example: yaml selfservice: flows: settings: required_aal: highest_available
3. Upload the updated configuration: bash ory update identity-config --project <project-id> --workspace <workspace-id> --file identity-config.yaml
This configuration ensures that only users who have set up a second factor will be prompted for it, making MFA optional for others [Step-up authentication](https://www.ory.sh/docs/kratos/mfa/step-up-authentication/) [Account recovery and password reset configuration](https://www.ory.sh/docs/kratos/self-service/flows/account-recovery-password-reset/#configuration).quiet-painting-32952
05/18/2025, 11:38 AMory get identity-config
The requested action was forbidden
I have the right ORY_PROJECT_API_KEY and ORY_SDK_URL configuredbrief-egg-61061
05/18/2025, 11:38 AMbrief-egg-61061
05/18/2025, 11:38 AMThe requested action was forbidden
when running ory get identity-config
, even though you have set the correct ORY_PROJECT_API_KEY
and ORY_SDK_URL
, it likely means that your API key does not have the required permissions for this operation. According to the knowledge sources, there are two types of API keys in Ory Network: - Project API Keys: Used to manage project data (like identities). - Workspace API Keys: Used to manage project configuration (such as identity config). To get or update the identity configuration, you need a Workspace API Key, not a Project API Key. The Project API Key does not have sufficient permissions for configuration management operations like ory get identity-config
[Authorizing Ory Network Workspace Admin APIs](https://www.ory.sh/docs/concepts/personal-access-token/#authorizing-ory-network-workspace-admin-apis). What you should do: 1. Create a Workspace API Key in the Ory Console under Workspace settings → API Keys. 2. Set the environment variable: bash export ORY_WORKSPACE_API_KEY=<your-workspace-api-key>
3. Run your command again (you do not need to set ORY_SDK_URL
for this operation). Summary: You are getting this error because a Project API Key cannot be used to fetch or update project configuration. Use a Workspace API Key instead [Authorizing Ory Network Workspace Admin APIs](https://www.ory.sh/docs/concepts/personal-access-token/#authorizing-ory-network-workspace-admin-apis).quiet-painting-32952
05/18/2025, 11:46 AMbrief-egg-61061
05/18/2025, 11:46 AMbrief-egg-61061
05/18/2025, 11:46 AMrequired_aal
are: - aal1: Requires only one authentication factor (e.g., password). This is the basic level of authentication. - aal2: Requires two authentication factors (e.g., password + TOTP or another second factor). This enforces multi-factor authentication. - aal3: Represents an even higher level of assurance, typically involving hardware-backed authenticators or similar advanced methods. - highest_available: Enforces the highest Authenticator Assurance Level (AAL) that the user has configured. If the user has set up a second factor, it will be required; if not, only the first factor is required. This is the default in Ory Network for strict multi-factor authentication enforcement. A higher number means it is harder for an attacker to compromise the account. For more details, see the [Ory documentation on credentials and AAL](https://www.ory.sh/kratos/docs/concepts/credentials) and [step-up authentication](https://www.ory.sh/docs/kratos/mfa/step-up-authentication/) [Step-up authentication](https://www.ory.sh/docs/kratos/mfa/step-up-authentication/).quiet-painting-32952
05/18/2025, 11:47 AMbrief-egg-61061
05/18/2025, 11:47 AMbrief-egg-61061
05/18/2025, 11:48 AMrequired_aal
to aal1
, users will only be required to authenticate with a single factor (such as a password or OIDC). Even if a user has configured a second factor (like TOTP), the system will not require them to use it for login or the relevant flow. The user will not be prompted for the second factor, and login with just the password will be sufficient. In other words, with aal1
, users cannot choose to use both password and TOTP for a higher level of assurance during login—the system will only enforce the first factor. If you want users who have set up a second factor to be required to use it, you should use highest_available
or aal2
as the value for required_aal
[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). Summary: - `aal1`: Only one factor is required, even if the user has set up a second factor. - Users cannot opt-in to use both password and TOTP at login if aal1
is set.quiet-painting-32952
05/18/2025, 11:50 AMbrief-egg-61061
05/18/2025, 11:50 AMbrief-egg-61061
05/18/2025, 11:50 AMrequired_aal: highest_available
and enable One-Time Codes (email or SMS) for MFA, Ory will require users who have a second factor configured to use it. However, if you notice that all users are being forced to use an email code after password authentication—even those who have not explicitly enrolled in 2FA—this is likely due to how your identity schema and credential configuration are set up. Why is this happening? - If your identity schema marks the email trait as an identifier for the code
credential (e.g., "credentials": { "code": { "identifier": true, "via": "email" } }
), then every user with an email address is considered to have the code credential available for MFA. - With required_aal: highest_available
, Ory will prompt for the second factor (the email code) for all users who have this credential available, not just those who have explicitly enrolled in 2FA. Are all my users set up to have email 2FA? - If your schema is configured as above, then yes—all users with an email address are effectively set up to use email as a second factor, even if they did not explicitly enroll. This is because the system treats the presence of the email trait and the credential configuration as "MFA available" for those users [SMS and Email 2FA](https://www.ory.sh/docs/kratos/mfa/mfa-via-sms/). How do I check which users have email 2FA? - The knowledge sources do not provide a direct API or method to list which users have 2FA enabled for email codes. However, if your identity schema is set up to always provide the code credential via email, then all users with an email address are considered to have email 2FA available. - If you want only some users to have email 2FA, you need to adjust your schema so that the code credential is not always available, or use a different trait for the code credential that is only set when a user enrolls in 2FA. Summary: With your current configuration, all users with an email address are treated as having email 2FA available, which is why Ory enforces the second factor for everyone when required_aal: highest_available
is set [SMS and Email 2FA](https://www.ory.sh/docs/kratos/mfa/mfa-via-sms/). If you want 2FA to be optional, you need to adjust your identity schema and credential configuration accordingly.quiet-painting-32952
05/18/2025, 12:01 PMclients:
web_hook:
header_allowlist:
- Accept
- Accept-Encoding
- Accept-Language
- Content-Length
- Content-Type
- Origin
- Priority
- Referer
- Sec-Ch-Ua
- Sec-Ch-Ua-Mobile
- Sec-Ch-Ua-Platform
- Sec-Fetch-Dest
- Sec-Fetch-Mode
- Sec-Fetch-Site
- Sec-Fetch-User
- True-Client-Ip
- User-Agent
- X-Forwarded-Host
- Ory-Base-Url-Rewrite
- Ory-Base-Url-Rewrite-Token
- X-Ory-Original-Host
- Ory-No-Custom-Domain-Redirect
cookies:
domain: <redacted>
path: /
same_site: Lax
courier:
delivery_strategy: smtp
smtp:
connection_uri: <redacted>
from_address: <redacted>
from_name: <redacted>
headers: {}
templates:
login_code:
valid:
email:
body: {}
sms:
body: {}
recovery:
invalid:
email:
body: {}
valid:
email:
body: {}
recovery_code:
invalid:
email:
body: {}
valid:
email:
body: {}
registration_code:
valid:
email:
body: {}
sms:
body: {}
verification:
invalid:
email:
body: {}
valid:
email:
body: {}
verification_code:
invalid:
email:
body: {}
valid:
email:
body: {}
sms:
body: {}
feature_flags:
cacheable_sessions: false
cacheable_sessions_max_age: 2m30s
password_profile_registration_node_group: password
identity:
default_schema_id: <redacted>
schemas: <redacted>
oauth2_provider:
override_return_to: true
preview:
default_read_consistency_level: strong
security:
account_enumeration:
mitigate: false
selfservice:
allowed_return_urls: <redacted>
default_browser_return_url: /ui/welcome
flows:
error:
ui_url: <redacted>
login:
after:
code:
hooks: []
default_browser_return_url: <redacted>
hooks: []
lookup_secret:
hooks: []
oidc:
hooks: []
passkey:
hooks: []
password:
hooks: []
saml:
hooks: []
totp:
hooks: []
webauthn:
hooks: []
before:
hooks: []
style: unified
ui_url: <redacted>
logout:
after:
default_browser_return_url: <redacted>
recovery:
after:
default_browser_return_url: <redacted>
hooks: []
before:
hooks: []
enabled: true
notify_unknown_recipients: false
ui_url: <redacted>
use: code
registration:
after:
code:
hooks:
- hook: session
default_browser_return_url: <redacted>
hooks: []
oidc:
hooks:
- hook: session
passkey:
hooks:
- hook: session
password:
hooks:
- hook: session
saml:
hooks:
- hook: session
webauthn:
hooks:
- hook: session
before:
hooks: []
enable_legacy_one_step: true
enabled: false
login_hints: true
style: unified
ui_url: <redacted>
settings:
after:
default_browser_return_url: <redacted>
hooks: []
lookup_secret:
hooks: []
oidc:
hooks: []
passkey:
hooks: []
password:
hooks: []
profile:
hooks: []
saml:
hooks: []
totp:
hooks: []
webauthn:
hooks: []
before:
hooks: []
privileged_session_max_age: 15m0s
required_aal: highest_available
ui_url: <redacted>
verification:
after:
default_browser_return_url: <redacted>
hooks: []
before:
hooks: []
enabled: true
notify_unknown_recipients: false
ui_url: <redacted>
use: code
methods:
b2b:
config:
organizations: []
captcha:
config:
provider: cf-turnstile
enabled: false
code:
config:
missing_credential_fallback_enabled: true
mfa_enabled: false
link:
config:
base_url: <redacted>
enabled: true
lookup_secret: {}
oidc:
config:
providers: []
enabled: false
passkey:
config:
rp:
display_name: <redacted>
id: <redacted>
origins:
- <redacted>
password:
config: {}
enabled: true
profile: {}
totp:
config:
issuer: <redacted>
enabled: true
webauthn:
config:
passwordless: false
rp:
display_name: <redacted>
id: <redacted>
origins:
- <redacted>
serve:
admin:
base_url: <redacted>
request_log:
disable_for_health: true
public:
base_url: <redacted>
cors:
enabled: false
request_log:
disable_for_health: true
session:
cookie:
domain: <redacted>
name: <redacted>
path: /
same_site: Lax
lifespan: 72h0m0s
whoami:
required_aal: highest_available
tokenizer:
templates: {}
I dont see "credentials": { "code": { "identifier": true, "via": "email" } }, so not sure why all users are enforced to login with email code.
In the ory console, there's this warning in the on-time codes toggle
Your configuration requires users to have a specific AAL level after logging in or when accessing self service settings. Enabling Enable one-time code MFA will force users to solve the MFA challenge before they are completely logged in or can access the self service settings.
brief-egg-61061
05/18/2025, 12:01 PMbrief-egg-61061
05/18/2025, 12:01 PMrequired_aal: highest_available
set for both the settings flow and the session (session.whoami.required_aal
). - In your selfservice.methods.code
section, you have mfa_enabled: false
, which should mean one-time code MFA is not enforced. However, you mentioned that in the Ory Console, you enabled "Enable one-time code MFA," and the warning appears. Key points from the knowledge sources: - When you enable one-time code MFA and set required_aal: highest_available
, Ory will require users who have a second factor available to use it. If the system considers all users to have a second factor (for example, if the identity schema or configuration makes the code credential available to all users), then all users will be forced to use the code as MFA [SMS and Email 2FA](https://www.ory.sh/docs/kratos/mfa/mfa-via-sms/). - The warning in the Ory Console is expected: "Your configuration requires users to have a specific AAL level after logging in or when accessing self service settings. Enabling Enable one-time code MFA will force users to solve the MFA challenge before they are completely logged in or can access the self service settings." This means that, with your current settings, enabling one-time code MFA will enforce the MFA challenge for all users. Why is this happening even though you don’t see credentials: { code: ... }
in your config? - The knowledge sources indicate that if the code method is enabled and the required AAL is set to highest_available
, Ory may treat all users as having the second factor available, depending on how the identity schema and code method are set up. If the code method is globally enabled, and the system can send a code to the user (e.g., via their email trait), it may consider the second factor available for all users [Github Issue #409](https://github.com/ory/network/issues/409). How to make one-time code MFA optional: - Set mfa_enabled: false
for the code method (which you have already done). - If you want users to opt-in to MFA, you may need to adjust your identity schema or the way the code method is configured so that not all users are considered to have the second factor available. The knowledge sources do not provide a detailed step-by-step for this, but they do mention that the presence of the code credential (e.g., via email) can make Ory treat all users as MFA-enabled [Github Issue #409](https://github.com/ory/network/issues/409). Summary: With your current configuration and the Console toggle enabled, Ory enforces the MFA challenge for all users because it considers the second factor available to everyone. This is expected behavior given the warning in the Console and the way Ory handles the code method and AAL settings. If you want to make MFA optional, you may need to further adjust your schema or code method configuration, but the knowledge sources do not provide explicit instructions for this scenario.quiet-painting-32952
05/18/2025, 12:20 PMory.createBrowserSettingsFlow()
[
{
"type": "input",
"group": "default",
"attributes": {
"name": "csrf_token",
"type": "hidden",
"value": "Dpu46kKZDJ/Vm+yXz0gZHgRYoF9/J2YvfeFN8X0WW6+MbUtEpL67E4d4Wey67lzH7BUZKqwoYkAn/YsUHCARxw==",
"required": true,
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {}
},
{
"type": "input",
"group": "profile",
"attributes": {
"name": "traits.email",
"type": "email",
"value": "<mailto:paco@afternoon.co.uk|paco@afternoon.co.uk>",
"required": true,
"autocomplete": "email",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1070002,
"text": "E-Mail",
"type": "info",
"context": {
"title": "E-Mail"
}
}
}
},
{
"type": "input",
"group": "profile",
"attributes": {
"name": "method",
"type": "submit",
"value": "profile",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1070003,
"text": "Save",
"type": "info"
}
}
},
{
"type": "input",
"group": "password",
"attributes": {
"name": "password",
"type": "password",
"required": true,
"autocomplete": "new-password",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1070001,
"text": "Password",
"type": "info"
}
}
},
{
"type": "input",
"group": "password",
"attributes": {
"name": "method",
"type": "submit",
"value": "password",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1070003,
"text": "Save",
"type": "info"
}
}
},
{
"type": "input",
"group": "lookup_secret",
"attributes": {
"name": "lookup_secret_regenerate",
"type": "submit",
"value": "true",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1050008,
"text": "Generate new backup recovery codes",
"type": "info"
}
}
},
{
"type": "img",
"group": "totp",
"attributes": {
"src": "data:image/png;base64,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",
"id": "totp_qr",
"width": 256,
"height": 256,
"node_type": "img"
},
"messages": [],
"meta": {
"label": {
"id": 1050005,
"text": "Authenticator app QR code",
"type": "info"
}
}
},
{
"type": "text",
"group": "totp",
"attributes": {
"text": {
"id": 1050006,
"text": "HWQDQP5QZQJQ4ZLG6FOMBX4SPMZBCWGG",
"type": "info",
"context": {
"secret": "HWQDQP5QZQJQ4ZLG6FOMBX4SPMZBCWGG"
}
},
"id": "totp_secret_key",
"node_type": "text"
},
"messages": [],
"meta": {
"label": {
"id": 1050017,
"text": "This is your authenticator app secret. Use it if you can not scan the QR code.",
"type": "info"
}
}
},
{
"type": "input",
"group": "totp",
"attributes": {
"name": "totp_code",
"type": "text",
"required": true,
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1070006,
"text": "Verify code",
"type": "info"
}
}
},
{
"type": "input",
"group": "totp",
"attributes": {
"name": "method",
"type": "submit",
"value": "totp",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1070003,
"text": "Save",
"type": "info"
}
}
}
]
Given that, how do I configure sms otp for this userbrief-egg-61061
05/18/2025, 12:20 PMbrief-egg-61061
05/18/2025, 12:21 PMjson { "traits": { "phone": { "type": "string", "format": "tel", "<http://ory.sh/kratos|ory.sh/kratos>": { "credentials": { "code": { "identifier": true, "via": "sms" } }, "recovery": { "via": "sms" }, "verification": { "via": "sms" } } } } }
This configuration ensures that the phone number can be used for SMS OTP, recovery, and verification flows ([Phone number and password](https://www.ory.sh/docs/kratos/manage-identities/customize-identity-schema/#phone-number-and-password)). ### 2. Enable SMS OTP in Ory - In the Ory Console, go to Authentication → Two-factor auth → One-time codes and enable it. - Make sure you have configured an SMS delivery provider under Authentication → SMS configuration ([Send SMS to your users](https://www.ory.sh/docs/kratos/emails-sms/sending-sms/)). ### 3. User Experience Once the above is set up: - When the user goes to their account settings (using the Experience UI or your custom UI), they should see an option to enroll SMS OTP if their identity has a phone number trait. - The UI nodes for SMS OTP will appear similarly to how TOTP nodes appear, but for the code
method and referencing the phone number. ### 4. If you do not see SMS OTP nodes If you do not see the SMS OTP nodes in the settings flow: - Double-check that the user’s identity has a valid phone number trait. - Ensure your identity schema is configured as shown above. - Make sure the SMS channel is properly configured in your Ory project. Note: The knowledge sources do not provide a step-by-step UI screenshot for SMS OTP enrollment, but the process is analogous to TOTP: the user will be prompted to enter a code sent to their phone when enrolling or using SMS OTP ([Enable SMS one-time-code (OTP)](https://www.ory.sh/docs/kratos/passwordless/one-time-code/#enable-sms-one-time-code-otp)). --- Summary: - Update your identity schema to support SMS OTP. - Ensure the user has a phone number trait. - Enable and configure SMS OTP and an SMS provider in Ory. - The user will then be able to enroll SMS OTP via the settings flow. If you have done all of the above and still do not see the SMS OTP option, the knowledge sources do not provide further troubleshooting steps.quiet-painting-32952
05/18/2025, 12:22 PMbrief-egg-61061
05/18/2025, 12:22 PMbrief-egg-61061
05/18/2025, 12:23 PMtotp.enabled: true
), and the settings flow nodes you shared include the TOTP setup (QR code, secret, and code input). This means users can enroll and use TOTP as a second factor [Time-based one-time passwords (TOTP)](https://www.ory.sh/docs/kratos/mfa/totp/). - Lookup Secrets (Backup Codes): The settings flow nodes include an option to generate backup recovery codes, which means users can set up and use lookup secrets as a 2FA fail-safe [Lookup Secrets - a MFA fail-safe](https://www.ory.sh/docs/kratos/mfa/lookup-secrets/). - Password: While not a second factor, password authentication is enabled and present in your flow. What is not available based on your config and nodes: - SMS/Email One-Time Codes: Although your config has the code method present, mfa_enabled
is set to false
for the code method, and your settings flow nodes do not show any UI for SMS or email one-time codes. Additionally, your identity schema (as shown in your config) does not include a phone number trait or code credential via SMS/email, so users cannot enroll in SMS or email OTP as a second factor [SMS and Email 2FA](https://www.ory.sh/docs/kratos/mfa/mfa-via-sms/). - WebAuthn (Passkeys, Security Keys): Your config has WebAuthn enabled, but your settings flow nodes do not show any WebAuthn enrollment options. This may be due to the identity schema or other project settings, but based on the nodes you provided, WebAuthn is not currently available for this user. Summary: With your current configuration and schema, users can set up TOTP and lookup secrets as 2FA methods. SMS/Email OTP and WebAuthn are not available for enrollment based on the nodes and schema you provided. If you want to enable additional 2FA methods, you will need to update your identity schema and configuration accordingly.