quick-addition-90714
04/26/2025, 10:17 AMbrief-egg-61061
04/26/2025, 10:17 AMbrief-egg-61061
04/26/2025, 10:17 AMstate: "active"
property in an identity object indicates that the identity (user account) is active and can be used. This is part of the identity's state property which can have two possible values: - `active`: The identity is active and can be used - `inactive`: The identity is inactive According to the API documentation, this value currently has no effect on the system's behavior, meaning it's primarily an informational field rather than one that controls functionality. When you see this in a session response or identity object, it's telling you that the user account associated with that session or identity is in an active state. For example, in session responses, you'll typically see something like: json { "identity": { "id": "c4d24c01-feba-4213-858b-262c8d427f8c", "state": "active", "state_changed_at": "2023-02-03T15:41:22.835918Z", // other identity properties... }, // other session properties... }
If an account is not active yet, users might see an error message with code 4000010: "Account not active yet. Did you forget to verify your email address?" This suggests that account activation might be related to email verification in some implementations, though the state field itself doesn't directly control this behavior. [Kratos API Reference](https://www.ory.sh/docs/kratos/reference/api) shows this state property in various API responses related to identity management.quick-addition-90714
04/26/2025, 11:11 AMbrief-egg-61061
04/26/2025, 11:11 AMbrief-egg-61061
04/26/2025, 11:12 AMtemplate_data
variable. This allows you to access user details in your message templates: jsonnet function(ctx) { recipient: ctx.recipient, template_type: ctx.template_type, name: if "template_data" in ctx && "identity" in ctx.template_data && "name" in ctx.template_data.identity then ctx.template_data.identity.name else null, // other fields... }
As shown in this example, you can access the user's name through ctx.template_data.identity.name
[HTTP based email delivery](https://www.ory.sh/docs/kratos/self-hosted/email-http#configuration). The identity object is available in various message templates, particularly for: - Recovery emails (valid) - Recovery code emails (valid) - Verification emails (valid) - Verification code emails (valid) When creating custom email templates, you can access the identity information directly. For example, in a recovery email template, you could personalize the greeting with the user's first name: Hello {{ upper .Identity.traits.firstName }}, to recover your account, please enter the following code:
This example shows how to access the user's first name from the identity traits [Email templates in self-hosted Ory Kratos](https://www.ory.sh/docs/kratos/self-hosted/mail-courier-templates). The available identity information depends on your identity schema configuration, but typically includes all the traits you've defined in your schema, such as email, name, phone number, etc.quick-addition-90714
04/26/2025, 11:16 AMbrief-egg-61061
04/26/2025, 11:16 AMbrief-egg-61061
04/26/2025, 11:17 AMjsonnet function(ctx) { recipient: ctx.recipient, template_type: ctx.template_type, name: if "template_data" in ctx && "identity" in ctx.template_data && "traits" in ctx.template_data.identity && "name" in ctx.template_data.identity.traits then ctx.template_data.identity.traits.name.first + " " + ctx.template_data.identity.traits.name.last else null, to: if "template_data" in ctx && "to" in ctx.template_data then <http://ctx.template_data.to|ctx.template_data.to> else null, recovery_code: if "template_data" in ctx && "recovery_code" in ctx.template_data then ctx.template_data.recovery_code else null, recovery_url: if "template_data" in ctx && "recovery_url" in ctx.template_data then ctx.template_data.recovery_url else null, verification_url: if "template_data" in ctx && "verification_url" in ctx.template_data then ctx.template_data.verification_url else null, verification_code: if "template_data" in ctx && "verification_code" in ctx.template_data then ctx.template_data.verification_code else null, }
Then, in your configuration file, reference this template: yaml courier: delivery_strategy: http http: request_config: url: <https://your-api-endpoint.com> method: POST body: <file://path/to/your/template.jsonnet> headers: Content-Type: application/json
This template specifically combines the first and last name from your identity traits structure to create a single "name" field with the value "Luka Boulpaep" in the HTTP request payload [HTTP based email delivery](https://www.ory.sh/docs/kratos/self-hosted/email-http#configuration). Note that the template checks for the existence of each property in the path to avoid errors if any part of the structure is missing. If you're using email templates directly rather than HTTP delivery, you can also access the combined name in your email templates using: Hello {{ .Identity.traits.name.first }} {{ .Identity.traits.name.last }},
This would render as "Hello Luka Boulpaep," in your email [Email templates in self-hosted Ory Kratos](https://www.ory.sh/docs/kratos/self-hosted/mail-courier-templates).quick-addition-90714
04/26/2025, 11:19 AMjson:"recipient"
TemplateType string json:"template_type"
Name *string json:"name,omitempty"
To *string json:"to,omitempty"
RecoveryCode *string json:"recovery_code,omitempty"
RecoveryURL *string json:"recovery_url,omitempty"
VerificationURL *string json:"verification_url,omitempty"
VerificationCode *string json:"verification_code,omitempty"
}brief-egg-61061
04/26/2025, 11:19 AMbrief-egg-61061
04/26/2025, 11:19 AMRecipient
- The email address of the recipient - TemplateType
- The type of template being used - Name
- The name of the user (which you've customized in previous examples) - To
- The recipient email address (duplicated in template_data) - RecoveryCode
, RecoveryURL
- For recovery flows - VerificationURL
, VerificationCode
- For verification flows However, based on the default Jsonnet template used by Ory Kratos for HTTP delivery, you should also include: go type EmailTemplate struct { Recipient string `json:"recipient"` TemplateType string `json:"template_type"` Name *string `json:"name,omitempty"` To *string `json:"to,omitempty"` RecoveryCode *string `json:"recovery_code,omitempty"` RecoveryURL *string `json:"recovery_url,omitempty"` VerificationURL *string `json:"verification_url,omitempty"` VerificationCode *string `json:"verification_code,omitempty"` LoginCode *string `json:"login_code,omitempty"` RegistrationCode *string `json:"registration_code,omitempty"` Subject *string `json:"subject,omitempty"` Body *string `json:"body,omitempty"` }
The additional fields are: - LoginCode
- For one-time code login flows - RegistrationCode
- For one-time code registration flows - Subject
- The email subject - Body
- The email body These fields are included in the default Jsonnet template as shown in the [Use a custom server to send Ory Identity messages to users](https://www.ory.sh/docs/kratos/emails-sms/sending-emails-smtp#send-emails-using-an-http-server) documentation. If you're only handling recovery and verification flows, your current struct might be sufficient, but including all possible fields will make your code more robust for future use cases. Kapa.ai: I've analyzed your Go struct for parsing Ory Kratos courier email templates. It covers most fields but is missing a few that are in the default template. I've provided the complete struct with all fields you should include for full compatibility.quick-addition-90714
04/26/2025, 11:20 AMquick-addition-90714
04/26/2025, 11:21 AMbrief-egg-61061
04/26/2025, 11:21 AMbrief-egg-61061
04/26/2025, 11:21 AM&
) and URL formatting (<http://template_data.to|template_data.to>
) appear to be incorrectly encoded. Here's the corrected version: jsonnet function(ctx) { recipient: ctx.recipient, template_type: ctx.template_type, name: if "template_data" in ctx && "identity" in ctx.template_data && "traits" in ctx.template_data.identity && "name" in ctx.template_data.identity.traits then ctx.template_data.identity.traits.name.first + " " + ctx.template_data.identity.traits.name.last else null, to: if "template_data" in ctx && "to" in ctx.template_data then <http://ctx.template_data.to|ctx.template_data.to> else null, recovery_code: if "template_data" in ctx && "recovery_code" in ctx.template_data then ctx.template_data.recovery_code else null, recovery_url: if "template_data" in ctx && "recovery_url" in ctx.template_data then ctx.template_data.recovery_url else null, verification_url: if "template_data" in ctx && "verification_url" in ctx.template_data then ctx.template_data.verification_url else null, verification_code: if "template_data" in ctx && "verification_code" in ctx.template_data then ctx.template_data.verification_code else null, }
This template will correctly extract the user's first and last name from the identity traits and combine them into a single "name" field. However, as mentioned in my previous response, you might want to consider adding the additional fields that are available in the default template: - login_code
- registration_code
- subject
- body
These fields are included in the default Jsonnet template as shown in the [Use a custom server to send Ory Identity messages to users](https://www.ory.sh/docs/kratos/emails-sms/sending-emails-smtp#send-emails-using-an-http-server) documentation. If you only need to handle the fields you've included, then your template (with the formatting fixes) should work correctly for your use case.quick-addition-90714
04/26/2025, 12:41 PMbrief-egg-61061
04/26/2025, 12:41 PMbrief-egg-61061
04/26/2025, 12:41 PMcsrf_token
parameter - The HTTP header as X-CSRF-Token
2. Or, if you did include a CSRF token, it doesn't match the one in the cookie. ## How to fix this issue: For API-based flows (which you're trying to use), you have a few options: 1. Include the CSRF token in your request body: When submitting the recovery flow, you need to include the csrf_token
field in your request body. This token can be found in the UI nodes of the flow response as a hidden input field named csrf_token
[CSRF troubleshooting](https://www.ory.sh/docs/kratos/debug/csrf). 2. Include the CSRF token in the X-CSRF-Token header: Alternatively, you can include the same token in the X-CSRF-Token
HTTP header. 3. Make sure cookies are properly passed: Ensure that you're passing all cookies from previous requests in your current request. Here's an example of how to properly submit a recovery flow with the CSRF token: curl -X POST \ -H 'Content-Type: application/json' \ -H 'Accept: application/json' \ -b cookies.txt \ -d '{"method":"code","email":"<mailto:example@example.com|example@example.com>","csrf_token":"your-csrf-token"}' \ "<https://your-kratos-domain/self-service/recovery?flow=<your-flow-id>>"
If you're using the admin API, note that even though you're creating a recovery with type "api", the browser-based CSRF protection might still be active if cookies are present in your request [Submit recovery flow](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#submit-recovery-flow). The maintainer's comment in a similar issue confirms this: "The error in your log clearly indicates that you are not sending the CSRF Token in your request body csrf_token=…
which is needed to verify the token" [CSRF Error during Login Flow](https://github.com/ory/kratos/discussions/4307). To completely avoid CSRF issues with API flows, you might want to ensure you're not sending any cookies with your request, or use a fresh HTTP client for each request that doesn't store cookies between requests.quick-addition-90714
04/26/2025, 12:49 PMbrief-egg-61061
04/26/2025, 12:49 PMbrief-egg-61061
04/26/2025, 12:50 PM/admin/recovery/code
endpoint was successful. The logs show: 1. The request was received at the admin endpoint 2. A recovery code was successfully created for identity ID 9e603962-98a1-47e8-8605-9d1e8f073160
3. The request completed with a 201 Created status This is working correctly because you're using the admin API endpoint, which doesn't require CSRF protection. The admin API is designed for server-to-server communication and bypasses the CSRF checks that were causing your previous error. When you mentioned in your previous message that you were getting CSRF errors, it was likely because you were either: 1. Using a public-facing endpoint instead of the admin endpoint 2. Or mixing cookie-based authentication with API requests The admin endpoints (like /admin/recovery/code
that you're using now) don't require CSRF tokens because they're protected by different authentication mechanisms (typically API keys or other admin credentials). I'll wait for your next message with the logs from your service running in docker-compose to help diagnose any issues you might be having there.quick-addition-90714
04/26/2025, 12:50 PMbrief-egg-61061
04/26/2025, 12:50 PMbrief-egg-61061
04/26/2025, 12:50 PM/admin/recovery/code
endpoint from your service, you're encountering a CSRF error, unlike when you call it from Postman. The key difference appears to be related to cookies. The error message states: > 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). This suggests that your Go HTTP client is sending cookies with your request, but not including the corresponding CSRF token. Here are a few ways to fix this: 1. Include the CSRF token from the cookie in your request: - Add it to the request body as csrf_token
- Or add it as an X-CSRF-Token
header 2. Don't send cookies with your request: - Create a new HTTP client for each request that doesn't store cookies - Or disable cookie jar in your HTTP client Here's how you might modify your Go code to create a client that doesn't use cookies: go client := &http.Client{ Jar: nil, // Explicitly set to nil to disable cookie storage }
According to the Ory maintainer's response to a similar issue: "The token value you send in the body comes from the API response of /self-service/login/flows and is a different value than the one from the cookie" [CSRF Error during Login Flow](https://github.com/ory/kratos/discussions/4307). This suggests that there might be a mismatch between the CSRF token in your cookies and what you're sending (if you're sending one at all). Since the admin API endpoint should work without CSRF protection (as it did in Postman), the most likely issue is that your service is somehow maintaining cookies from previous interactions with Kratos, and those cookies contain CSRF tokens that are triggering the protection mechanism. Try implementing one of the solutions above, particularly disabling cookie storage in your HTTP client, and see if that resolves the issue.quick-addition-90714
04/26/2025, 12:54 PMbrief-egg-61061
04/26/2025, 12:54 PMbrief-egg-61061
04/26/2025, 12:54 PMCookie
header from the client when fetching the self-service flows... Without forwarding the Cookie
HTTP header you can't fetch the flow due to a security error. [CSRF troubleshooting](https://www.ory.sh/docs/troubleshooting/csrf) For your admin API calls, you should either: 1. Create a fresh HTTP client for each request that doesn't use cookies: go client := &http.Client{ Jar: nil, // Explicitly disable cookie storage }
2. Or clear any existing cookies before making the admin API call This should resolve the CSRF error you're encountering when calling the admin API from your service. Would you like me to provide more specific guidance on how to modify your Go HTTP client implementation?quick-addition-90714
04/27/2025, 8:20 PMbrief-egg-61061
04/27/2025, 8:20 PMbrief-egg-61061
04/27/2025, 8:20 PMyaml selfservice: flows: settings: after: default_browser_return_url: <https://your-desired-redirect-url/>
This will redirect users after they complete the settings flow (which includes updating their password after recovery) [Browser redirects and flow completion](https://www.ory.sh/docs/concepts/redirects#redirect-flows). ## Option 2: Use the return_to
parameter You can include a return_to
parameter when initializing the recovery flow: sdk.createBrowserRecoveryFlow({ return_to: "<https://your-desired-redirect-url/>" })
However, there's a known issue with this approach. According to a GitHub issue: "When creating a recovery flow with the return_to
query parameter the url is not respected when the account has two factor authentication" [Recovery flow with Two-Factor does not respect return_to
](https://github.com/ory/network/issues/222). ## Option 3: Manual redirect in your UI Since the recovery flow redirects to the settings page, you can implement a custom redirect in your settings UI by checking if the user came from a recovery flow and then redirecting them after they submit the password change. ## Important note There's an open issue in the Ory Kratos GitHub repository about this exact problem: "Recovery flow redirect after success not working as expected" [GitHub Issue #2371](https://github.com/ory/kratos/issues/2371). According to this issue, the default_browser_return_url
configuration for the recovery flow doesn't work as expected because the recovery flow redirects to the settings flow. A user in the issue comments mentioned: "The only way to reliably achieve this behaviour is to cause a manual overriding redirect INSIDE the settings page - By checking the source redirect that brought you to that page, and checking if that value contains the recovery flow identifier." Would you like me to provide more details on any of these approaches?quick-addition-90714
05/04/2025, 5:08 PMbrief-egg-61061
05/04/2025, 5:08 PMbrief-egg-61061
05/04/2025, 5:08 PMquick-addition-90714
05/09/2025, 10:48 PMbrief-egg-61061
05/09/2025, 10:48 PMbrief-egg-61061
05/09/2025, 10:48 PMquick-addition-90714
05/18/2025, 9:39 AMbrief-egg-61061
05/18/2025, 9:39 AMbrief-egg-61061
05/18/2025, 9:39 AMquick-addition-90714
05/20/2025, 6:07 PMbrief-egg-61061
05/20/2025, 6:07 PMbrief-egg-61061
05/20/2025, 6:07 PMcreateNativeRecoveryFlow
endpoint) while a valid session cookie or session token is present, you will receive a 400 Bad Request
error. The documentation states: > On an existing recovery flow, use the getRecoveryFlow
API endpoint. This means you should retrieve and continue the existing flow using its ID, rather than starting a new one. This approach helps prevent issues such as CSRF attacks and ensures the integrity of the recovery process [Ory API Reference – createNativeRecoveryFlow](https://www.ory.sh/docs/reference/api). If the existing flow has expired, you will need to initiate a new one. Otherwise, always use the current flow until it is completed or expires.quick-addition-90714
05/20/2025, 6:08 PMbrief-egg-61061
05/20/2025, 6:08 PMbrief-egg-61061
05/20/2025, 6:08 PMquick-addition-90714
06/14/2025, 12:27 PMbrief-egg-61061
06/14/2025, 12:27 PMbrief-egg-61061
06/14/2025, 12:28 PMfirst
name in the traits.name
object, your request body would look like this: json [ { "op": "replace", "path": "/traits/name/first", "value": "NewFirstName" } ]
You can also add or remove traits using the "op": "add"
or "op": "remove"
operations, respectively. The path
uses [JSON Pointer](https://datatracker.ietf.org/doc/html/rfc6901#section-5) notation to specify the field you want to modify. Example cURL request: bash curl --location --request PATCH "https://<kratos-admin-url>/admin/identities/<identity-id>" \ --header "Content-Type: application/json" \ --header "Authorization: Bearer <your-admin-api-key>" \ --data-raw '[ { "op": "replace", "path": "/traits/name/first", "value": "NewFirstName" } ]'
The fields id
, stateChangedAt
, and credentials
cannot be updated using this method [Kratos API Reference – patchIdentity](https://www.ory.sh/docs/kratos/reference/api#patchidentity). For more complex updates, you can include multiple operations in the array: json [ { "op": "replace", "path": "/traits/name/first", "value": "NewFirstName" }, { "op": "replace", "path": "/traits/name/last", "value": "NewLastName" } ]
This approach allows you to precisely adjust the traits of a user using the PATCH method.quick-addition-90714
06/22/2025, 7:40 AMbrief-egg-61061
06/22/2025, 7:40 AMbrief-egg-61061
06/22/2025, 7:40 AM/admin/identities
endpoint does not support search at the moment, and filtering is only possible by credential type (like username or email), not by arbitrary traits like names [REST API Guidelines](https://www.ory.sh/docs/open-source/guidelines/rest-api-guidelines#the-design-for-openapi) [Kratos API Reference](https://raw.githubusercontent.com/ory/kratos/master/spec/api.json). Additionally, a user reported that searching for identities by domain (e.g., part of an email address) is not supported in the Ory Console, and a team member confirmed that while you can filter by organization ID in the API, there is no mention of filtering by name or similar traits [Identities cannot be search by domain](https://github.com/ory/network/issues/405). Therefore, at this time, filtering on names in Kratos is not supported.quick-addition-90714
06/22/2025, 3:37 PMbrief-egg-61061
06/22/2025, 3:37 PMbrief-egg-61061
06/22/2025, 3:37 PMerror=map[message:I[#/email] S[] missing properties: email stack_trace: ...
This means that the email property was missing from your POST request to the /self-service/recovery
endpoint. In the recovery flow, the email field is required in the request body when you are in the choose_method
state. What happened: - You submitted a form (likely from a browser) to the recovery endpoint. - The request did not include the required email
field. - Kratos responded with a validation error indicating that the email
property is missing. How to fix: Make sure your form or request body includes the email
field. For example, if you are submitting a form, it should look like this: http POST /self-service/recovery?flow=<flow-id> Content-Type: application/x-www-form-urlencoded email=your@email.com&method=code&csrf_token=...
Or, if you are using JSON: json { "email": "<mailto:your@email.com|your@email.com>", "method": "code", "csrf_token": "..." }
The email
field is required to initiate the recovery process, as documented in the [Ory Kratos recovery flow](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#submit-recovery-flow). Summary: The error is because the email
property was missing in your request. Add the email
field to your request body to resolve this issue.quick-addition-90714
06/22/2025, 3:39 PM<form class="form" action="<http://localhost:4433/self-service/recovery?flow=0136af13-4a2d-4225-8b6f-0ce7a3f8c1d6>" method="POST">
<input type="hidden" name="csrf_token" value="4b1nKDjHZJY9UDS7YzUIbCUZxgbdkPxjgh2nZIHAoTlQ+KCWMD6m4MThbBuJ/WOB3eWhCy9qZls50/r3SmSYvg==" required="">
<input data-slot="input" class="file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive" placeholder="Enter your email address:" required="" type="email">
<button type="submit" name="method" value="code" class="submit">Continue</button>
</form>
brief-egg-61061
06/22/2025, 3:39 PMbrief-egg-61061
06/22/2025, 3:39 PMname="email"
attribute on the email input field. Without this attribute, the email value will not be included in the POST request body, which is why Ory Kratos reports the error "missing properties: email". How to fix: Update your email input field to include the name="email"
attribute: html <input data-slot="input" class="file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive" placeholder="Enter your email address:" required="" type="email" name="email" />
With this change, the form will submit the email
field as required by the Ory Kratos recovery flow [Submit recovery flow](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#submit-recovery-flow). Summary: Add name="email"
to your email input field to resolve the "missing properties: email" error.quick-addition-90714
06/22/2025, 3:46 PMbrief-egg-61061
06/22/2025, 3:46 PMbrief-egg-61061
06/22/2025, 3:46 PMreason:The flow query parameter is missing or malformed.
means that your GET request to /self-service/recovery
is missing the required flow
query parameter, or the parameter is not correctly formatted. According to the Ory Kratos documentation, when interacting with a self-service flow (such as recovery), you must always include the flow
parameter in the query string when fetching or submitting a flow. The correct usage is: /self-service/recovery?flow=<flow-id>
What went wrong in your request: Your request was: GET /self-service/recovery?csrf_token=...&email=...&method=code
This is incorrect because: - The endpoint expects a flow
parameter, not csrf_token
, email
, or method
in the query string for a GET request. - The csrf_token
, email
, and method
should be sent in the POST body when submitting the flow, not as query parameters in a GET request. How to fix: - When fetching the recovery flow (GET), use only the flow
parameter: GET /self-service/recovery?flow=<flow-id>
- When submitting the recovery flow (POST), send the csrf_token
, email
, and method
in the request body (form or JSON), not in the query string. This is confirmed in the documentation and API reference for Ory Kratos recovery flows [Get recovery flow](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#get-recovery-flow). Summary: Always include the flow
parameter in the query string when fetching a recovery flow. Do not include csrf_token
, email
, or method
in the query string for GET requests; these belong in the POST body when submitting the flow.quick-addition-90714
06/22/2025, 3:50 PM<form class="form" action="<http://localhost:4433/self-service/recovery?flow=a6f18415-6bc0-4b9f-a1ad-abe5c8f18d2b>" method="POST ">
<input type="hidden" name="csrf_token" value="61uryThAscWC6TGg8JftQ9PwKRnIwVwkjy/3yzn9MJBaHmx3MLlzs3tYaQAaX4auKwxOFDo7xhw04apY8lkJFw==" required="">
<input data-slot="input" class="file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive" placeholder="Your email address here" name="email" value="" required="" type="email">
<button type="submit" name="method" value="code" class="submit">Continue</button>
</form>
is this still correct?quick-addition-90714
06/22/2025, 3:50 PM<form class="form" action="<http://localhost:4433/self-service/recovery?flow=a6f18415-6bc0-4b9f-a1ad-abe5c8f18d2b>" method="POST ">
<input type="hidden" name="csrf_token" value="61uryThAscWC6TGg8JftQ9PwKRnIwVwkjy/3yzn9MJBaHmx3MLlzs3tYaQAaX4auKwxOFDo7xhw04apY8lkJFw==" required="">
<input data-slot="input" class="file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive" placeholder="Your email address here" name="email" value="" required="" type="email">
<button type="submit" name="method" value="code" class="submit">Continue</button>
</form>
is this still correct?brief-egg-61061
06/22/2025, 3:51 PMbrief-egg-61061
06/22/2025, 3:51 PMaction
attribute points to the correct endpoint with the required flow
parameter: /self-service/recovery?flow=a6f18415-6bc0-4b9f-a1ad-abe5c8f18d2b
- The method
is POST
(note: remove any trailing spaces in "POST "
to avoid issues). - The form includes a hidden input for csrf_token
with the correct name and value. - The email input has name="email"
, which is required for Kratos to process the recovery request. - The submit button has name="method"
and value="code"
, which is correct for the code-based recovery method. This structure matches the requirements and examples in the Ory Kratos documentation for submitting a recovery flow [Submit recovery flow](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-basic-integration#submit-recovery-flow). Summary of what to check: - Ensure the method
attribute is exactly "POST"
(no trailing spaces). - All required fields (csrf_token
, email
, method
) are present and named correctly. With these in place, your form should work as expected with Ory Kratos.quick-addition-90714
06/22/2025, 3:53 PMbrief-egg-61061
06/22/2025, 3:53 PMbrief-egg-61061
06/22/2025, 3:53 PMemail
property or a malformed or missing flow
query parameter. When such errors occur, Kratos is configured to redirect the user to the error UI you have set in your configuration: yaml selfservice: flows: error: ui_url: <http://my-app.com/error>
If you see a redirect to your error page, it means Kratos encountered an error it could not recover from in the flow, and is forwarding the user to the error UI as specified in your configuration. You can check the error details by inspecting the error UI or by reviewing the Kratos logs for the specific error message. For more information, see the [Kratos Fallback/Error UI documentation](https://www.ory.sh/docs/kratos/fallback/error).quick-addition-90714
06/23/2025, 7:19 PMtime=2025-06-23T19:18:34Z level=info msg=Encountered self-service settings error. audience=audit error=map[debug: message:The request was malformed or contained invalid parameters reason:Requested return_to URL "<https://api.stag.thommie.be/login>" is not allowed. status:Bad Request status_code:400] http_request=map[headers:map[accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 accept-encoding:gzip, deflate, br, zstd accept-language:en-US,en;q=0.9,fr;q=0.8,nl;q=0.7 cache-control:max-age=0 content-length:151 content-type:application/x-www-form-urlencoded cookie:Value is sensitive and has been redacted. To see the value set config key "log.leak_sensitive_values = true" or environment variable "LOG_LEAK_SENSITIVE_VALUES=true". dnt:1 origin:<https://identity.stag.thommie.be> priority:u=0, i referer:<https://identity.stag.thommie.be/settings?flow=e20adfc6-ddef-428b-b38a-cedd3dc274ab> sec-ch-ua:"Chromium";v="137", "Not/A)Brand";v="24" sec-ch-ua-mobile:?0 sec-ch-ua-platform:"macOS" sec-fetch-dest:document sec-fetch-mode:navigate sec-fetch-site:same-origin sec-fetch-user:?1 upgrade-insecure-requests:1 user-agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 x-amzn-trace-id:Root=1-6859a889-4016f63216b0d1ca22a9f604 x-forwarded-for:213.49.51.23 x-forwarded-port:443 x-forwarded-proto:https] host:<http://identity.stag.thommie.be|identity.stag.thommie.be> method:POST path:/self-service/settings query:Value is sensitive and has been redacted. To see the value set config key "log.leak_sensitive_values = true" or environment variable "LOG_LEAK_SENSITIVE_VALUES=true". remote:10.0.90.73:50630 scheme:http] service_name=Ory Kratos service_version=v1.3.1 settings_flow=map[Type:browser active: id:e20adfc6-ddef-428b-b38a-cedd3dc274ab nid:84521698-4582-4a5e-9268-446986f59c32 request_url:<https://identity.stag.thommie.be/self-service/recovery?flow=369f3c2b-eb5e-4e0f-9fd4-60524a5a030f&return_to=%2Flogin> return_to:/login state:show_form]
brief-egg-61061
06/23/2025, 7:19 PMbrief-egg-61061
06/23/2025, 7:20 PMreason:Requested return_to URL "<https://api.stag.thommie.be/login>" is not allowed. status:Bad Request status_code:400
means that the return_to
URL you provided (`https://api.stag.thommie.be/login`) is not included in the list of allowed return URLs in your Ory Kratos configuration. Ory Kratos enforces an allow list for dynamic redirects using the return_to
parameter to prevent open redirect attacks. If a return_to
URL is not explicitly allowed in your configuration, Kratos will reject the request with this error [Browser redirects and flow completion](https://www.ory.sh/docs/concepts/redirects). How to fix: You need to add the domain and path of your desired return URL to the selfservice.allowed_return_urls
configuration in your Kratos config file. For example: yaml selfservice: allowed_return_urls: - <https://api.stag.thommie.be/login>
After updating your configuration, restart Kratos for the changes to take effect. Summary: The error occurs because the return_to
URL is not on the allow list. Add it to selfservice.allowed_return_urls
in your Kratos configuration to resolve the issue.quick-addition-90714
06/23/2025, 7:22 PMbrief-egg-61061
06/23/2025, 7:22 PMbrief-egg-61061
06/23/2025, 7:22 PMreturn_to
parameter is set when you initialize a self-service flow (such as login, registration, recovery, verification, or settings) by including it as a query parameter in the URL. For example, when starting a login flow, you can set it like this: /self-service/login/browser?return_to=<https://myapp.com/posts>
This tells Ory Kratos where to redirect the user after the flow is successfully completed. The same applies to other flows, such as registration, recovery, and settings. The return_to
parameter is not automatically persisted across different flows; if you want to keep it, you must explicitly pass it again when initializing a new flow [Advanced integration](https://www.ory.sh/docs/kratos/bring-your-own-ui/custom-ui-advanced-integration) [Browser redirects and flow completion](https://www.ory.sh/docs/concepts/redirects). Additionally, for security, the value of return_to
must match one of the URLs in your selfservice.allowed_return_urls
configuration, otherwise Kratos will reject the request [HTTP redirection configuration](https://www.ory.sh/docs/kratos/concepts/browser-redirect-flow-completion#redirection). In summary: You set return_to
as a query parameter when initializing a self-service flow.quick-addition-90714
06/23/2025, 7:22 PMbrief-egg-61061
06/23/2025, 7:22 PMbrief-egg-61061
06/23/2025, 7:23 PMredirect_uri
parameter, not directly by Kratos’ return_to
parameter. Here’s how it works according to the knowledge sources: - When an OAuth2 flow is initiated, the client application sends a request to Hydra’s /oauth2/auth
endpoint with a redirect_uri
. - Hydra then redirects the user to the Kratos login (or registration) UI, typically passing a login_challenge
(not a return_to
) as a query parameter. - The Kratos UI or Account Experience should use the login_challenge
to interact with Hydra and, after successful authentication, accept the login request and redirect the user back to Hydra, which then redirects to the original redirect_uri
provided by the client. There is discussion in the knowledge sources about the relationship between redirect_uri
, login_challenge
, and return_to
. Specifically, the Account Experience UI does not automatically transfer the redirect_uri
from Hydra to the Kratos return_to
parameter. Instead, the flow is designed so that after login/registration, the user is returned to Hydra, which then handles the final redirect to the client’s redirect_uri
[Account Experience should transfer the redirect_uri
query parameter from Hydra to the login return_to
query parameter](https://github.com/ory/network/issues/264). If you want to persist the OAuth2 login flow through more complex identity flows (like account recovery), Ory provides an "Always Return to OAuth2 provider" setting, which ensures the user is always redirected back to Hydra after the identity flow, regardless of the return_to
parameter [Identity Provider Integration Settings](https://www.ory.sh/docs/oauth2-oidc/identity-provider-integration-settings#overview). Summary: When using Kratos with Hydra, the return_to
parameter is not the main mechanism for post-authentication redirection. Instead, Hydra’s redirect_uri
and the OAuth2 flow manage where the user ends up after authentication. Kratos’ return_to
is only relevant for direct self-service flows, not for OAuth2/OIDC flows via Hydra. If you need to ensure the user is always returned to the OAuth2 provider, enable the "Always Return to OAuth2 provider" setting in your Ory project [Identity Provider Integration Settings](https://www.ory.sh/docs/oauth2-oidc/identity-provider-integration-settings#overview).quick-addition-90714
06/23/2025, 7:25 PMquick-addition-90714
06/23/2025, 7:25 PMbrief-egg-61061
06/23/2025, 7:25 PMbrief-egg-61061
06/23/2025, 7:25 PMredirect_uri
. This is done when the client initiates the OAuth2 flow by making a request to Hydra’s /oauth2/auth
endpoint and includes the redirect_uri
as a query parameter. This URI tells Hydra where to send the user after the authentication and consent process is complete. For example: /oauth2/auth?client_id=...&redirect_uri=<https://my-app.com/callback&>...
Hydra then manages the flow, and after successful authentication (and consent), redirects the user to the redirect_uri
that was set by the client application, provided it matches one of the URIs registered for that client [OAuth2 authorization code flow](https://www.ory.sh/docs/oauth2-oidc/authorization-code-flow). So, the redirect URI is set by the OAuth2 client application when starting the authentication flow.quick-addition-90714
06/23/2025, 7:29 PM/**
* AuthClient handles OAuth2 authentication for an application, including generating authorization URIs,
* obtaining and refreshing tokens, and managing token data.
*/
import { generateCodeVerifier, OAuth2Client } from "@badgateway/oauth2-client";
// Interfaces
export interface TokenResponse {
access_token: string;
token_type: string;
expires_in: number;
refresh_token: string;
scope: string;
}
export interface OAuthToken {
accessToken: string;
tokenType: string;
expiresAt: number;
refreshToken: string;
scope: string;
}
class AuthClient {
/**
* A verifier string for PKCE (Proof Key for Code Exchange).
* @type {string | undefined}
*/
private codeVerifier: string | undefined;
/**
* A random string used as a state parameter in OAuth2 flows.
* @type {string | undefined}
*/
private randomState: string | undefined;
/**
* Object containing OAuth token information.
* @type {OAuthToken}
*/
private clientData: OAuthToken;
/**
* The redirect URI used for OAuth2 callbacks.
* @type {string}
*/
private redirectUri: string;
/**
* Instance of the OAuth2 client.
* @type {OAuth2Client}
*/
client: OAuth2Client;
/**
* Initializes an instance of AuthClient with predefined OAuth2 client settings.
*/
constructor() {
this.redirectUri = "<http://localhost:5173/callback>";
this.client = new OAuth2Client({
server: "<https://identity.stag.thommie.be>",
clientId: "2a5e075c-fa14-4fbb-8b14-ce04f39358de",
tokenEndpoint: "/oauth2/token",
authorizationEndpoint: "/oauth2/auth",
});
this.clientData = {
accessToken: "",
tokenType: "",
expiresAt: 0,
refreshToken: "",
scope: "",
};
}
/**
* Initializes the client by generating and storing the code verifier and state.
* @async
* @returns {Promise<void>}
*/
initializeClient = async (): Promise<void> => {
this.randomState = this.ensureRandomState();
this.codeVerifier = await this.ensureCodeVerifier();
};
/**
* Generates a random string for OAuth2 state or PKCE code verification.
* @private
* @returns {string} A random alphanumeric string.
*/
private getRandomString = (): string =>
Math.random().toString(36).substring(2, 15);
/**
* Ensures that a random state is available, generating and storing one if not found.
* @private
* @returns {string} The OAuth2 state.
*/
private ensureRandomState = (): string => {
const storedState = sessionStorage.getItem("oauth_random_state");
if (storedState) return storedState;
const newState = this.getRandomString() + this.getRandomString();
sessionStorage.setItem("oauth_random_state", newState);
return newState;
};
/**
* Ensures that a code verifier is available, generating and storing one if not found.
* @private
* @async
* @returns {Promise<string>} The PKCE code verifier.
*/
private ensureCodeVerifier = async (): Promise<string> => {
const storedCodeVerifier = sessionStorage.getItem("oauth_code_verifier");
if (storedCodeVerifier) return storedCodeVerifier;
const newCodeVerifier = await generateCodeVerifier();
sessionStorage.setItem("oauth_code_verifier", newCodeVerifier);
return newCodeVerifier;
};
/**
* Extracts the authorization code from a URL.
* @private
* @param {string} url - The URL to extract the code from.
* @returns {string} The authorization code.
*/
private extractCodeFromUrl = (url: string): string => {
try {
return new URL(url).searchParams.get("code") ?? "";
} catch (error) {
console.error("Invalid URL:", error);
return "";
}
};
/**
* Maps a token response to the OAuthToken interface.
* @private
* @param {TokenResponse} response - The token response from the server.
* @returns {OAuthToken} The mapped token object.
*/
private mapResponse = (response: TokenResponse): OAuthToken => ({
accessToken: response?.access_token,
tokenType: response.token_type,
expiresAt: new Date(Date.now() + response.expires_in * 1000).getTime(),
refreshToken: response.refresh_token,
scope: response.scope,
});
/**
* Retrieves the authorization URI for the OAuth2 flow.
* @async
* @returns {Promise<string>} The authorization URI.
* @throws {Error} If the state is not initialized.
*/
getAuthorizeUri = async (): Promise<string> => {
if (!this.randomState)
throw new Error("OAuth2 authentication state not found");
return await this.client.authorizationCode.getAuthorizeUri({
redirectUri: this.redirectUri,
state: this.randomState,
codeVerifier: this.codeVerifier,
scope: ["openid offline_access"],
});
};
/**
* Exchanges the authorization code for an OAuth2 token.
* @async
* @returns {Promise<void>}
* @throws {Error} If the authorization code or token response is invalid.
*/
getOAuth2Token = async (): Promise<string> => {
try {
const { server, clientId, tokenEndpoint } = this.client.settings;
const code = this.extractCodeFromUrl(document.URL);
if (!code) throw new Error("Authorization code not found in the URL");
const postData = new URLSearchParams({
code,
client_id: clientId,
redirect_uri: this.redirectUri,
grant_type: "authorization_code",
code_verifier: String(this.codeVerifier),
});
const response = await fetch(`${server}${tokenEndpoint}`, {
method: "POST",
body: postData,
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
if (!response.ok) this.initiateLogin();
const responseData = this.mapResponse(await response.json());
this.clientData = responseData;
return responseData.accessToken;
} catch (error) {
console.error("Error fetching OAuth2 token:", error);
throw error;
}
};
/**
* Refreshes the access token using the refresh token.
* @async
* @returns {Promise<void>}
* @throws {Error} If the refresh token is missing or the refresh fails.
*/
refreshAccessToken = async (): Promise<string> => {
try {
const { server, clientId, tokenEndpoint } = this.client.settings;
const { refreshToken } = this.clientData;
if (!refreshToken) throw new Error("No refreshToken found");
const postData = new URLSearchParams({
grant_type: "refresh_token",
refresh_token: refreshToken,
client_id: clientId,
});
const response = await fetch(`${server}${tokenEndpoint}`, {
method: "POST",
body: postData,
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
if (!response.ok) this.initiateLogin();
const responseData = this.mapResponse(await response.json());
this.clientData = responseData;
return responseData.accessToken;
} catch (error) {
console.error("Error refreshing token:", error);
throw error;
}
};
/**
* Redirects the user to the login page to initiate the OAuth2 flow.
* @async
* @returns {Promise<void>}
*/
initiateLogin = async (): Promise<void> => {
await client.initializeClient();
const authorizeUri = await client.getAuthorizeUri();
if (!authorizeUri)
throw new Error("Failed to retrieve the authorization URI.");
document.location.assign(authorizeUri);
};
/**
* Checks if the user is currently logged in based on token expiration.
* @returns {boolean} True if the user is logged in, false otherwise.
*/
isLoggedIn = (): boolean => {
try {
const { expiresAt } = this.clientData;
const now = Date.now();
if (expiresAt <= now) return false;
return true;
} catch (error) {
console.error("Error while checking token validity:", error);
return false;
}
};
}
export const client = new AuthClient();
how come I see that the redirect uri is not the same as in the log here:
time=2025-06-23T19:18:33Z level=info msg=started handling request http_request=map[headers:map[accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7 accept-encoding:gzip, deflate, br, zstd accept-language:en-US,en;q=0.9,fr;q=0.8,nl;q=0.7 cache-control:max-age=0 content-length:151 content-type:application/x-www-form-urlencoded cookie:Value is sensitive and has been redacted. To see the value set config key "log.leak_sensitive_values = true" or environment variable "LOG_LEAK_SENSITIVE_VALUES=true". dnt:1 origin:<https://identity.stag.thommie.be> priority:u=0, i referer:<https://identity.stag.thommie.be/settings?flow=e20adfc6-ddef-428b-b38a-cedd3dc274ab> sec-ch-ua:"Chromium";v="137", "Not/A)Brand";v="24" sec-ch-ua-mobile:?0 sec-ch-ua-platform:"macOS" sec-fetch-dest:document sec-fetch-mode:navigate sec-fetch-site:same-origin sec-fetch-user:?1 upgrade-insecure-requests:1 user-agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 x-amzn-trace-id:Root=1-6859a889-4016f63216b0d1ca22a9f604 x-forwarded-for:213.49.51.23 x-forwarded-port:443 x-forwarded-proto:https] host:<http://identity.stag.thommie.be|identity.stag.thommie.be> method:POST path:/self-service/settings query:Value is sensitive and has been redacted. To see the value set config key "log.leak_sensitive_values = true" or environment variable "LOG_LEAK_SENSITIVE_VALUES=true". remote:10.0.90.73:50630 scheme:http]
brief-egg-61061
06/23/2025, 7:29 PMbrief-egg-61061
06/23/2025, 7:29 PMredirect_uri
you set in your OAuth2 client (`http://localhost:5173/callback`) is not the same as the URL seen in the Kratos log (`https://api.stag.thommie.be/login`). Here’s what’s happening, according to the knowledge sources: - In OAuth2/OIDC flows with Ory Hydra, the redirect_uri
is set by the OAuth2 client application when it initiates the flow with Hydra’s /oauth2/auth
endpoint. This is the URI to which Hydra will redirect the user after authentication and consent [OAuth2 authorization code flow](https://www.ory.sh/docs/oauth2-oidc/authorization-code-flow). - The log you posted is from a self-service settings flow in Kratos, not from the OAuth2 callback. The return_to
parameter in Kratos flows is separate from the OAuth2 redirect_uri
. The return_to
parameter is used for browser-based self-service flows (like settings, recovery, etc.) and must be whitelisted in Kratos’ configuration [Browser redirects and flow completion](https://www.ory.sh/docs/concepts/redirects). - If you see a return_to
or redirect URL in the Kratos logs that does not match your OAuth2 client’s redirect_uri
, it is likely because a self-service flow (such as settings or recovery) is being completed, and the redirect is being handled by Kratos, not by Hydra. The two mechanisms are independent, and the URLs do not have to match. Why the mismatch? - The redirect_uri
in your OAuth2 client is only relevant to the OAuth2 flow managed by Hydra. - The return_to
or redirect URL in the Kratos log is relevant to Kratos self-service flows and is controlled by the frontend or by the allowed_return_urls
configuration in Kratos. - If you are chaining flows (for example, a user is sent to a settings or recovery flow during an OAuth2 login), the final redirect may be determined by the Kratos flow, not by the original OAuth2 redirect_uri
, unless you have configured Kratos to always return to the OAuth2 provider after such flows [Identity Provider Integration Settings](https://www.ory.sh/docs/oauth2-oidc/identity-provider-integration-settings#overview). Summary: The redirect_uri
in your OAuth2 client and the return_to
or redirect URL in Kratos logs are set and used in different contexts. If you want to ensure users always return to the OAuth2 provider after a Kratos flow, you need to enable the "Always Return to OAuth2 provider" setting in your Ory project. Otherwise, Kratos will use its own redirect logic, which may not match your OAuth2 client’s redirect_uri
[Account Experience should transfer the redirect_uri
query parameter from Hydra to the login return_to
query parameter](https://github.com/ory/network/issues/264). If you need more precise control, ensure your frontend and Kratos configuration are aligned regarding allowed return URLs and how flows are chained. If you need to debug further, check the actual requests and parameters being sent to both Hydra and Kratos during the flow.quick-addition-90714
06/23/2025, 7:30 PMbrief-egg-61061
06/23/2025, 7:30 PMbrief-egg-61061
06/23/2025, 7:31 PMafter
configuration in the settings
flow, specifically: yaml flows: settings: ui_url: ${base_url}/settings after: default_browser_return_url: ${thommie_api_url}/
What does this do? According to the Ory Kratos documentation, the after.default_browser_return_url
under the settings
flow determines where the user is redirected after successfully completing the settings flow (for example, after updating their profile or password) [Browser redirects and flow completion](https://www.ory.sh/docs/concepts/redirects#redirect-flows). So, in your configuration, after a successful settings update, the user will be redirected to ${thommie_api_url}/
. Important notes: - The redirect will only work if ${thommie_api_url}/
is included in your selfservice.allowed_return_urls
. In your config, only ${base_url}
is allowed, so you must add ${thommie_api_url}/
to allowed_return_urls
for this redirect to work. Otherwise, Kratos will reject the redirect and may send the user to the error UI with a message like "Requested return_to URL is not allowed" [Browser redirects and flow completion](https://www.ory.sh/docs/kratos/concepts/browser-redirect-flow-completion#redirection). - The after
configuration for the settings flow only affects the redirect after a successful settings update, not after other flows. Summary: The after.default_browser_return_url
in the settings flow controls where users are sent after a successful settings update. Make sure the target URL is whitelisted in allowed_return_urls
to avoid redirect errors.quick-addition-90714
06/23/2025, 7:31 PMbrief-egg-61061
06/23/2025, 7:32 PMbrief-egg-61061
06/23/2025, 7:32 PMreturn_to
URL in Ory Kratos is controlled by the Kratos server itself, based on the selfservice.allowed_return_urls
configuration. When a return_to
parameter is provided (for example, when initializing a self-service flow), Kratos checks if the URL matches one of the entries in the allowed_return_urls
list. If the URL is not on this allow list, Kratos will reject the request and may redirect the user to the error UI with a message indicating the URL is not allowed [Browser redirects and flow completion](https://www.ory.sh/docs/concepts/redirects) [HTTP redirection configuration](https://www.ory.sh/docs/kratos/concepts/browser-redirect-flow-completion#redirection). So, the Kratos server and its configuration determine whether a return_to
URL is valid.quick-addition-90714
06/23/2025, 7:33 PMbrief-egg-61061
06/23/2025, 7:33 PMbrief-egg-61061
06/23/2025, 7:33 PMreturn_to
URLs in your selfservice.allowed_return_urls
configuration. Ory Kratos matches the full URL or path you specify. For example, if you set: yaml selfservice: allowed_return_urls: - <https://myapp.com/dashboard> - <https://myapp.com/login>
then only those exact URLs are allowed. If you want to allow all paths under a domain, you need to explicitly list them or use the domain root (e.g., `https://myapp.com/`), which allows any path under that domain [Browser redirects and flow completion](https://www.ory.sh/docs/concepts/redirects). Regarding your settings flow: If you did not explicitly set a return_to
parameter when initializing the flow from the client, then the redirect after the settings flow is determined by your Kratos configuration, specifically the after.default_browser_return_url
under the settings
flow. This means the redirect URL is set by your config, not by the client [Browser redirects and flow completion](https://www.ory.sh/docs/concepts/redirects#redirect-flows). Summary: - You do not need /*
for return_to
URLs; use the domain or specific paths as needed. - For the settings flow, if the client did not set return_to
, the redirect is controlled by your Kratos config.