Skip to main content
Version: v0.6

Account Recovery and Password Reset


Please read the Self-Service Flows overview before continuing with this document.

Account Recovery must be performed if access to an account needs to be recovered. Common use cases include:

  • "Forgot password" flows
  • "Lost MFA device" flows
  • ...

There are two Recovery Flow types supported in Ory Kratos:

  • Flows where the user sits in front of the Browser (e.g. website, single page app, ...)
  • Flows where API interaction is required (e.g. mobile app, Smart TV, ...)

The Recovery Flow can be summarized as the following state machine:

To enable recovery flows, make the following adjustments to your Ory Kratos configuration:

selfservice:  methods:    link:      enabled: true  flows:    recovery:      enabled: true


Currently, one recovery method is supported:

  • The link method performs account recovery (also known as password reset) by sending an email containing a recovery link to the user.

Recovery link Method#

The link method is dis/enabled in the Ory Kratos config:

selfservice:  methods:    link:      enabled: true      # ...

There are two email types sent by this method:

Recovery email sent to unknown address
If the requested email address is an unknown recovery address, an account recovery attempt email is sent to that email address.

Recovery email sent to a known recovery address
If the requested email address is a known recovery address, a recovery link is sent to that email address.

This prevents account enumeration attacks as explained in this brilliant blog post by Troy Hunt.

The emails are using templates that can be customised as explained in Customizing E-Mail Templates. The template IDs are:

  • Unknown email address: recovery_invalid
  • Known email address: recovery_valid

You should also configure how long a session is privileged. The user will only be able to update his/her password (or any other credential) for the specified amount of time after clicking on the recovery link:

selfservice:  flows:    settings:      privileged_session_max_age: 15m

To specify that an identity's trait is a recovery email, use the following Identity JSON Schema:

 {   "$id": "",   "$schema": "",   "title": "Person",   "type": "object",   "properties": {     "traits": {       "type": "object",       "properties": {         "email": {           "type": "string",           "format": "email",           "": {             "credentials": {               "password": {                 "identifier": true               }             },+            "recovery": {+              "via": "email"+            }           }         }       }       "additionalProperties": false     }   } }

Initialize Recovery Flow#

The first step is to initialize the Recovery Flow. This sets up Anti-CSRF tokens and more. Each recovery flow has a state parameter which follows the state machine:


  • choose_method indicates that the user has not chosen a recovery method yet. This is useful when link is not the only recovery method active.
  • sent_email implies that the recovery email has been sent out.
  • passed_challenge is set when the user has clicked the recovery link and completed the account recovery.

Recovery for Browser Clients#

The Recovery Flow for browser clients relies on HTTP redirects between Ory Kratos, your Recovery UI, and the end-user's browser:

The Flow UI (your application!) is responsible for rendering the actual Login and Registration HTML Forms. You can of course implement one app for rendering all the Login, Registration, ... screens, and another app (think "Service Oriented Architecture", "Micro-Services" or "Service Mesh") is responsible for rendering your Dashboards, Management Screens, and so on.

To initialize the Recovery Flow, point the Browser to the initialization endpoint:

curl -s -v -X GET \  -H "Accept: text/html"  \
> GET /self-service/recovery/browser HTTP/1.1> Host:> User-Agent: curl/7.64.1> Accept: text/html>< HTTP/1.1 302 Found< Cache-Control: 0< Content-Type: text/html; charset=utf-8< Location:< Set-Cookie: csrf_token=y4Ocu6V83BapwJwbPw/pnlRHHw40DZbjq5iuDrxl0Ds=; Path=/; Domain=; Max-Age=31536000; HttpOnly<<a href="">Found</a>.

The server responds with a HTTP 302 redirect to the Recovery UI, appending the ?flow=<flow-id> query parameter (see the curl example) to the URL configured here:

selfservice:  flows:    recovery:      # becomes      ui_url:

Recovery for API Clients#


Never use API flows to implement Browser applications! Using API flows in Single-Page-Apps as well as server-side apps opens up several potential attack vectors, including Login and other CSRF attacks.

The Recovery Flow for API clients does not use HTTP Redirects and can be summarized as follows:

To initialize the API flow, the client calls the API-flow initialization endpoint (REST API Reference) which returns a JSON response:

$ curl -s -X GET \    -H "Accept: application/json" \ | \      jq
{  "id": "c0da5b82-f22c-4609-8467-8c7e2fbc12db",  "type": "api",  "expires_at": "2021-04-28T13:22:01.115431237Z",  "issued_at": "2021-04-28T12:22:01.115431237Z",  "request_url": "",  "ui": {    "action": "",    "method": "POST",    "nodes": [      {        "type": "input",        "group": "default",        "attributes": {          "name": "csrf_token",          "type": "hidden",          "value": "",          "required": true,          "disabled": false        },        "messages": null,        "meta": {}      },      {        "type": "input",        "group": "link",        "attributes": {          "name": "email",          "type": "email",          "required": true,          "disabled": false        },        "messages": null,        "meta": {}      },      {        "type": "input",        "group": "link",        "attributes": {          "name": "method",          "type": "submit",          "value": "link",          "disabled": false        },        "messages": null,        "meta": {          "label": {            "id": 1070005,            "text": "Submit",            "type": "info"          }        }      }    ]  },  "state": "choose_method"}

Recovery Flow Payloads#

Fetching the Recovery Flow (REST API Reference) is usually only required for browser clients but also works for Recovery Flows initialized by API clients. All you need is a valid flow ID:

$ curl -H "Accept: application/json" -s \    '' | \      jq
{  "id": "199a2c74-08ca-4f22-b24c-3de3032682f8",  "type": "browser",  "expires_at": "2021-04-28T13:47:17.913580042Z",  "issued_at": "2021-04-28T12:47:17.913580042Z",  "request_url": "",  "ui": {    "action": "",    "method": "POST",    "nodes": [      {        "type": "input",        "group": "default",        "attributes": {          "name": "csrf_token",          "type": "hidden",          "value": "VuOjAAE4olxMvyiCzB7STotMOtQEYrABc2GDo6vIF+e8trBx+zpaCkvYf7+myIDi7duYGY4vwgp8gqxmE6BecA==",          "required": true,          "disabled": false        },        "messages": null,        "meta": {}      },      {        "type": "input",        "group": "link",        "attributes": {          "name": "email",          "type": "email",          "required": true,          "disabled": false        },        "messages": null,        "meta": {}      },      {        "type": "input",        "group": "link",        "attributes": {          "name": "method",          "type": "submit",          "value": "link",          "disabled": false        },        "messages": null,        "meta": {          "label": {            "id": 1070005,            "text": "Submit",            "type": "info"          }        }      }    ]  },  "state": "choose_method"}

Send Recovery Link to Email#


The link recovery mode will always open a link in the browser, even if it was initiated by an API client. This is because the user clicks the link in his/her email client, which opens the browser.

When the link method is enabled, it will be part of the methods payload in the Recovery Flow:

$ curl -H "Accept: application/json" -s \    '' | \      jq -r '.ui.nodes[] | select(.group=="link")'
{  "type": "input",  "group": "link",  "attributes": {    "name": "email",    "type": "email",    "required": true,    "disabled": false  },  "messages": null,  "meta": {}}{  "type": "input",  "group": "link",  "attributes": {    "name": "method",    "type": "submit",    "value": "link",    "disabled": false  },  "messages": null,  "meta": {    "label": {      "id": 1070005,      "text": "Submit",      "type": "info"    }  }}

Recovery Flow Form Rendering#

The Recovery User Interface is a route (page / site) in your application (server, native app, single page app) that should render a recovery form.

In stark contrast to other Identity Systems, Ory Kratos does not render this HTML. Instead, you need to implement the HTML code in your application (e.g. NodeJS + ExpressJS, Java, PHP, ReactJS, ...), which gives you extreme flexibility and customizability in your user interface flows and designs.

You will use the Recovery Flow JSON response to render the recovery form UI, which could looks as follows depending on your programming language and web framework:

Account Recovery HTML Form

Recovery Form Validation#

The form payloads are then submitted to Ory Kratos which follows up with:

  • An HTTP 302 Found redirect pointing to the Recovery UI for Browser Clients.
  • An application/json response for API Clients.

Recovery Link via Email#

To send the recovery email, the end-user fills out the form. There might be validation errors such as a malformed email:

Account Recovery HTML Form with validation errors

When validation errors happen, browser clients receive a HTTP 302 Found redirect to the Recovery Flow UI, containing the Recovery Flow ID which includes the error payloads.

For API Clients, the server typically responds with HTTP 400 Bad Request and the Recovery Flow in the response payload as JSON.

Successful Submission#

On successful submission, an email will be sent to the provided address:

Account Recovery HTML Form with success message

Unsuccessful Recovery#

If the recovery challenge (e.g. the link in the recovery email) is invalid or expired, the user will be HTTP 302 redirected to the Recovery UI.


When an invalid or expired challenge is used, Ory Kratos initializes a new Account Recovery flow automatically. This flow will always be a Browser-based flow because the challenge is completed by clicking a link!

The new Recovery Flow includes an error message such as the following:

Account Recovery HTML Form with an invalid challenge

Please keep in mind that this part of the flow always involves a Browser!

Successful Recovery#

Completing account recovery always results in a HTTP 302 redirect with a Ory Kratos Login Session Cookie to the Settings UI with a Settings Flow prompting the user to update their password or credentials:

Account Recovery HTML Form with an invalid challenge