<@U04UQ68083H> I running the command ```docker run...
# ory-copilot
q
@chilly-king-10285 I running the command
Copy code
docker 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 login
b
A message from kapa.ai
Based on the information provided in the knowledge sources, it appears that you're encountering an issue with the Ory Tunnel command. The Ory Tunnel is designed to allow your application and Ory to run on the same domain during local development. However, the command you're using seems to be slightly different from the recommended format. To address your issue, I suggest trying the following command instead:
Copy code
ory 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:
Copy code
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.
q
@chilly-king-10285 I want a function to be called on successful logins from the Experience UI
b
A message from kapa.ai
To call a function (such as a webhook or custom logic) on successful logins from the Ory Account Experience UI, you should use Ory Actions (also referred to as "hooks" in some documentation). Ory Actions allow you to trigger external logic or webhooks in response to events like user login, regardless of whether the login happens via the Experience UI or a custom UI. How to trigger a function on successful login: 1. Configure an "after login" action using the Ory Console or the Ory CLI. - The most flexible action is the
web_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:
Copy code
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 AuthenticationActions 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!
q
@chilly-king-10285 is it possible to deactivate a user, and potenatially reactivate it?
b
A message from kapa.ai
Yes, it is possible to deactivate a user (identity) in Ory, and you can also reactivate them later. Ory supports two main identity states: - active: The identity can use self-service flows such as sign-in. - inactive: The identity cannot use self-service flows. When an identity is set to
inactive
, 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:
Copy code
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).
q
@chilly-king-10285 I create users with
Copy code
async 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 question
b
A message from kapa.ai
To send an invite link via email to a user after creating them, you need to: 1. Create the user (as you are already doing). 2. Generate a recovery link for that user using the Admin API. 3. Manually send the recovery link to the user's email address. Currently, Ory does not automatically send the invite/recovery link via email when you create a user or generate a recovery link through the Admin API. You must handle the email sending yourself after obtaining the link [Invite users](https://www.ory.sh/docs/kratos/manage-identities/invite-users/) [Magic links](https://www.ory.sh/docs/kratos/manage-identities/account-recovery/#magic-links). Example flow: 1. Create the user (as in your code). 2. Generate the recovery link:
Copy code
python    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.
q
@chilly-king-10285 can I filter a user by trait?
b
A message from kapa.ai
I'm sorry, but the knowledge sources do not explicitly mention support for filtering users (identities) by trait (such as email or custom fields) using the Ory Admin API. The available documentation and a related GitHub issue indicate that while you can list identities and filter by credential type (like username or email), there is currently no built-in way to filter identities by arbitrary traits directly via the API. Workarounds suggested include fetching all identities and filtering on the application side, or making parallel requests for known IDs, but these are not ideal for large datasets [Retrieve subset of identities through admin API](https://github.com/ory/network/issues/305).
q
@chilly-king-10285 how do I change a user's password via the API
b
A message from kapa.ai
To change a user's password via the API in Ory, you should use the Settings Flow with the
password
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:
Copy code
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).
q
@chilly-king-10285 how would I initiate this flow with
Copy code
export const ory = new FrontendApi(
  new Configuration({
    basePath: Config.getAuthBasePath(),
    baseOptions: {
      withCredentials: true,
    },
  }),
);
b
A message from kapa.ai
To initiate a settings flow (for example, to change a user's password) using the Ory JavaScript SDK and your
FrontendApi
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:
Copy code
js const flow = await ory.frontend.createBrowserSettingsFlow()   .then(({ data }) => data)
Example for a native app:
Copy code
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:
Copy code
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.
q
@chilly-king-10285 I find the docs hard to follow. I created a settings flow with
Copy code
const flow = await ory.createBrowserSettingsFlow()
I get two nodes as a result of password group
Copy code
{
                "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?
b
A message from kapa.ai
To reset the password after creating the settings flow and receiving the password nodes, you need to submit the form data to the
action
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:
Copy code
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):
Copy code
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).
q
@chilly-king-10285 I tried what you suggested and got this error back:
Copy code
{
  "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"
  }
}
b
A message from kapa.ai
The error message you received indicates a CSRF (Cross-Site Request Forgery) violation. According to the knowledge sources, this happens when the anti-CSRF cookie is present, but the CSRF token is either missing from the request body or does not match the cookie value. Ory requires both the CSRF cookie and the CSRF token to be sent and to match for security reasons. How to fix this: 1. Include the CSRF token in your request body. - The CSRF token is provided as a hidden input field in the flow's UI nodes, typically named
csrf_token
. - You must extract this value from the flow and include it in your payload when submitting the password change. 2. Example:
Copy code
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.
q
@chilly-king-10285 my fetch looks like this
Copy code
await 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 403
b
A message from kapa.ai
Your fetch code is almost correct, but the 403 CSRF error suggests that either the CSRF token is missing/incorrect, or the cookies are not being sent or do not match the token. Here are the key points to check, based on the knowledge sources: 1. CSRF Token Extraction: Make sure you are extracting the CSRF token from the correct node. The CSRF token is found in a node with
attributes.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:
Copy code
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)
q
@chilly-king-10285 how wouldl I use the ory client to setup 2FA?
b
A message from kapa.ai
To set up 2FA (two-factor authentication) using the Ory client, you need to enable and configure a second-factor method (such as TOTP, WebAuthn, SMS, or Lookup Secrets) in your Ory project, and then use the Ory client to initiate and complete the relevant self-service flows. General steps: 1. Enable 2FA methods in your Ory project configuration For example, to enable TOTP (Time-based One-Time Passwords), update your configuration:
Copy code
yaml    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:
Copy code
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:
Copy code
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!
q
How do I 1. Enable 2FA methods in your Ory project configuration
@chilly-king-10285 how do I 1. Enable 2FA methods in your Ory project configuration
b
A message from kapa.ai
To enable 2FA (multi-factor authentication) methods in your Ory project configuration, you can use either the Ory Console or the Ory CLI. Here’s how you can do it for common 2FA methods: --- ### Using the Ory Console 1. Log in to the [Ory Console](https://console.ory.sh/). 2. Select your workspace and project. 3. Navigate to the Authentication tab. 4. Click on Two-factor auth in the sidebar. 5. Enable the desired 2FA methods (such as TOTP, WebAuthn, One-Time Codes, or Lookup Secrets) by toggling their respective switches and saving your changes. --- ### Using the Ory CLI 1. Download your current identity configuration:
Copy code
bash     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:
Copy code
yaml     selfservice:       methods:         totp:           config:             issuer: <http://ExampleIssuer.com|ExampleIssuer.com>           enabled: true         lookup_secret:           enabled: true
3. Update your configuration:
Copy code
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/).
q
@chilly-king-10285 I would like to enable SMS/Email one-time codes so that users can optionally choose to have this extra layer of authentication. When I enable it in Authentication -> two factor auth -> one-time codes, it enforces the email code. How do I make it optional and let the user choose whether they want it and where do they want the code to be sent?
b
A message from kapa.ai
Based on the knowledge sources, when you enable SMS/Email one-time codes for multi-factor authentication (MFA) in Ory, the enforcement behavior depends on your project’s configuration—specifically, the
required_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:
Copy code
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.
q
@chilly-king-10285 how do I change 2. Set the flow’s
required_aal
to
highest_available
(not
aal2
), for example in the settings flow:
b
A message from kapa.ai
To change the flow’s
required_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 AuthenticationSessions 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:
Copy code
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:
Copy code
yaml    selfservice:      flows:        settings:          required_aal: highest_available
3. Upload the updated configuration:
Copy code
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).
q
@chilly-king-10285 I get this
Copy code
ory get identity-config 
The requested action was forbidden
I have the right ORY_PROJECT_API_KEY and ORY_SDK_URL configured
b
A message from kapa.ai
If you are receiving
The 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:
Copy code
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).
q
@chilly-king-10285 what are the possible values for required_aal and what do they mean?
b
A message from kapa.ai
The possible values for
required_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/).
q
@chilly-king-10285 if I set it to aal1, can users decide wether they should login with password of password + TOTP?
b
A message from kapa.ai
If you set
required_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.
q
@chilly-king-10285 I have required_aal: highest_available. If I enable One-Time Codes it forces users to login with email code after password authentication, why is that? Are all my users setup to have email 2fa? and how do I check that?
b
A message from kapa.ai
When you set
required_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.
q
@chilly-king-10285 this is what my config looks like
Copy code
clients:
  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
Copy code
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.
b
A message from kapa.ai
Based on your configuration and the information from the knowledge sources, here’s what’s happening: - You have
required_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.
q
@chilly-king-10285 I enabled totp and now my nodes look like this when calling
ory.createBrowserSettingsFlow()
Copy code
[
  {
    "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 user
b
A message from kapa.ai
To configure SMS OTP (one-time code) for a user, you need to ensure two things: 1. Your identity schema must support SMS as a credential for the code method. 2. The user must have a phone number trait in their identity. ### 1. Identity Schema Configuration Your identity schema should include a phone number trait with the correct configuration for SMS OTP. Here’s an example from the knowledge sources:
Copy code
json {   "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.
q
@chilly-king-10285 given the schema I shared with you, what kind of 2fa can I setup for the user?
b
A message from kapa.ai
Based on the configuration you shared, the following 2FA (two-factor authentication) methods are available for your users: - TOTP (Time-based One-Time Passwords): Your config has TOTP enabled (
totp.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.