Skip to main content

Customize User Interface

Ory Kratos is an HTTP API server and does not include a traditional HTML user interface (rendering forms such as "Login" or "Registration") in its code base. We provide several reference UI implementations which you can use to get started quickly, or as the basis for your own UI:

Ory Kratos' HTTP APIs make it easy and straight forward to write your own UI (e.g. "login" screen, "update profile settings", ...) in any programming languages and framework! It can be built in any programming language including Java, Node, or Python and can be run both a server or an end-user device for example a browser, or a mobile phone. Implementing your own UI is simple and straight forward. There is no complex authentication mechanism required and no need to worry about possible attack vectors such as CSRF or Session Attacks since Ory Kratos provides the preventive measures built in.

Chapter Self-Service Flows contains further information on APIs and flows related to the SSUI, and build self-service applications.

UI Payloads

To make UI customization easy, Ory Kratos prepares all the necessary data for forms that need to be shown during e.g. login, registration:

{
id: '9b527900-2199-4221-9252-7971b3362282',
type: 'browser',
expires_at: '2021-04-28T13:55:36.046466067Z',
issued_at: '2021-04-28T12:55:36.046466067Z',
ui: {
action: 'https://playground.projects.oryapis.com/api/kratos/public/self-service/settings?flow=9b527900-2199-4221-9252-7971b3362282',
method: 'POST',
nodes: [
{
type: 'input',
group: 'default',
attributes: {
node_type: 'input',
name: 'csrf_token',
type: 'hidden',
value: 'U3r/lgEfT8rA1Lg0Eeo06oGO8mX6T4TKoe/z7rbInhvYeacbRg0IW9zrqnpU1wmQJXKiekNzdLnypx5naHXoPg==',
required: true,
disabled: false
},
messages: null,
meta: {}
},
{
type: 'input',
group: 'profile',
attributes: {
node_type: 'input',
name: 'traits.email',
type: 'email',
value: 'foo@ory.sh',
disabled: false
},
messages: null,
meta: {
label: {
id: 1070002,
text: 'E-Mail',
type: 'info'
}
}
}
// ...
]
}
}

The above example would be rendered in HTML like this:

<form
method="POST"
action="https://playground.projects.oryapis.com/api/kratos/public/self-service/settings?flow=9b527900-2199-4221-9252-7971b3362282"
>
<!-- this is the first node -->
<input
type="hidden"
name="csrf_token"
value="U3r/lgEfT8rA1Lg0Eeo06oGO8mX6T4TKoe/z7rbInhvYeacbRg0IW9zrqnpU1wmQJXKiekNzdLnypx5naHXoPg=="
required
/>

<!-- this is the second node -->
<input type="email" name="traits.email" value="foo@ory.sh" />
<label for="traits.email">E-Mail</label>
</form>

As you can see, the JSON can be mapped almost 1:1 to HTML! This method makes it easy for you to implement complex forms without having to deal with state management, form validation, and more!

UI Node Groups

Nodes are grouped (using the group key) based on the source that generated the node. Sources are the different methods such as "password" ("Sign in/up with ID & password"), "oidc" (Social Sign In), "link" (Password reset and email verification), "profile" ("Update your profile") and the "default" group which typically contains the CSRF token.

You can use the node group to filter out items, re-arrange them, render them differently - up to you!

UI Node Types

UI Nodes can have several types!

UI Script Nodes

The script node type is primarily used to load required scripts for WebAuthn!

{
"type": "script",
"group": "webauthn",
"attributes": {
"src": "http://localhost:4455/.well-known/ory/webauthn.js",
"async": true,
"referrerpolicy": "no-referrer",
"crossorigin": "anonymous",
"integrity": "sha512-E3ctShTQEYTkfWrjztRCbP77lN7L0jJC2IOd6j8vqUKslvqhX/Ho3QxlQJIeTI78krzAWUQlDXd9JQ0PZlKhzQ==",
"type": "text/javascript",
"id": "webauthn_script",
"node_type": "script"
},
"messages": [],
"meta": {}
}

Rendering it is straight forward if you are on the server-side:

https://github.com/ory/kratos-selfservice-ui-node/blob/master/views/partials/ui_node_script.hbs

For single-page apps, you might need a different set up to load the script asynchronously. In ReactJS you might want to do something like the following:

https://github.com/ory/kratos-react-nextjs-ui/blob/master/pkg/ui/NodeScript.tsx

UI Anchor Nodes

Nodes of type a are currently not used in Ory Kratos, but we have them around in case they are needed at a later stage!

https://github.com/ory/kratos-selfservice-ui-node/blob/master/views/partials/ui_node_anchor.hbs

UI Image Nodes

Nodes of type img are used as QR codes, for example.

{
"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"
}
}
}

QR Code

Here is an example of implementing an img node in handlebars:

https://github.com/ory/kratos-selfservice-ui-node/blob/master/views/partials/ui_node_image.hbs

Here is an example of implementing an img node in ReactJS:

https://github.com/ory/kratos-react-nextjs-ui/blob/master/pkg/ui/NodeImage.tsx

UI Text Nodes

Nodes of type text are commonly used to display secrets (e.g. lookup secrets).

Lookup Secret

TOTP Secret

Their JSON usually contains a bit of contextual information.

{
"type": "text",
"group": "totp",
"attributes": {
"text": {
"id": 1050006,
"text": "GLAS5YHAJ6V5LT3N7AU2R4AWU6SYOCHS",
"type": "info",
"context": {
"secret": "GLAS5YHAJ6V5LT3N7AU2R4AWU6SYOCHS"
}
},
"id": "totp_secret_key",
"node_type": "text"
},
"messages": [],
"meta": {
"label": {
"id": 1050006,
"text": "This is your authenticator app secret. Use it if you can not scan the QR code.",
"type": "info"
}
}
}

Rendering the text component is usually a bit more complex, as you most likely want a bit of custom formatting. By the way, you can find an overview of all message IDs used here in the conditionals at the end of this page! In handlebars, this could look as follows:

https://github.com/ory/kratos-selfservice-ui-node/blob/master/views/partials/ui_node_text.hbs

In ReactJS, you will most likely find similar if statements on the message IDs.

https://github.com/ory/kratos-react-nextjs-ui/blob/master/pkg/ui/NodeText.tsx

UI Input Nodes

The most common node type is the input type. In general, it represents an HTL <input> field:

{
"type": "input",
"group": "default",
"attributes": {
"name": "csrf_token",
"type": "hidden",
"node_type": "input",
"value": "U3r/lgEfT8rA1Lg0Eeo06oGO8mX6T4TKoe/z7rbInhvYeacbRg0IW9zrqnpU1wmQJXKiekNzdLnypx5naHXoPg==",
"required": true,
"disabled": false
},
"messages": null,
"meta": {}
}

An input node itself has a type as well. These types are taken from HTML:

  • text
  • password
  • number
  • checkbox
  • hidden
  • email
  • submit
  • button
  • datetime-local
  • date
  • url

Checkbox Input Node

Checkboxes are primarily used when an Identity Schema field is of "type": "boolean". The following Identity Schema

{
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
properties: {
traits: {
type: 'object',
properties: {
// ...
tos: {
title: 'Accept Terms of Service',
type: 'boolean'
}
// ...
}
// ...
}
}
}

would result in such a checkbox node:

{
"type": "input",
"group": "profile",
"attributes": {
"name": "traits.tos",
"type": "checkbox",
"disabled": false,
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1070002,
"text": "Accept Terms of Service",
"type": "info"
}
}
}

When using plain HTML and not a single page app, the best option you have is to use two input fields - one hidden and one a checkbox - with false and true respectively. Make sure that the checkbox input field is second as it needs to override the hidden field, if set.

https://github.com/ory/kratos-selfservice-ui-node/blob/master/views/partials/ui_node_input_checkbox.hbs

In single page apps, such as ReactJS, this trickery is not needed as you have full control over the form's state:

https://github.com/ory/kratos-react-nextjs-ui/blob/master/pkg/ui/NodeInputCheckbox.tsx

Button Input Node

Buttons are primarily used to trigger something, which is why it is important to have the onclick attribute handled. If onclick exists, you will also find a script node:

[
{
"type": "input",
"group": "webauthn",
"attributes": {
"name": "webauthn_register_trigger",
"type": "button",
"value": "",
"disabled": false,
"onclick": "window.__oryWebAuthnRegistration({\"publicKey\":{\"challenge\":\"SOaWrZE4unW3cC57ED52HRnHwd22Fcg8DNf0zf9Jgr0=\",\"rp\":{\"name\":\"Ory\",\"id\":\"localhost\"},\"user\":{\"name\":\"placeholder\",\"icon\":\"https://via.placeholder.com/128\",\"displayName\":\"placeholder\",\"id\":\"q7MFvGN9TzqDTIgEIvkjZw==\"},\"pubKeyCredParams\":[{\"type\":\"public-key\",\"alg\":-7},{\"type\":\"public-key\",\"alg\":-35},{\"type\":\"public-key\",\"alg\":-36},{\"type\":\"public-key\",\"alg\":-257},{\"type\":\"public-key\",\"alg\":-258},{\"type\":\"public-key\",\"alg\":-259},{\"type\":\"public-key\",\"alg\":-37},{\"type\":\"public-key\",\"alg\":-38},{\"type\":\"public-key\",\"alg\":-39},{\"type\":\"public-key\",\"alg\":-8}],\"authenticatorSelection\":{\"requireResidentKey\":false,\"userVerification\":\"preferred\"},\"timeout\":60000}})",
"node_type": "input"
},
"messages": [],
"meta": {
"label": {
"id": 1050012,
"text": "Add security key",
"type": "info"
}
}
},
{
"type": "script",
"group": "webauthn",
"attributes": {
"src": "http://localhost:4455/.well-known/ory/webauthn.js",
"async": true,
"referrerpolicy": "no-referrer",
"crossorigin": "anonymous",
"integrity": "sha512-E3ctShTQEYTkfWrjztRCbP77lN7L0jJC2IOd6j8vqUKslvqhX/Ho3QxlQJIeTI78krzAWUQlDXd9JQ0PZlKhzQ==",
"type": "text/javascript",
"id": "webauthn_script",
"node_type": "script"
},
"messages": [],
"meta": {}
}
]

For example, when an end-user triggers a WebAuthn flow, the button would trigger the required JavaScript to get it working!

https://github.com/ory/kratos-selfservice-ui-node/blob/master/views/partials/ui_node_input_checkbox.hbs

In a single page application, we need a bit of trickery as we are executing JavaScript from a JSON source in JavaScript - which essentially means we need to execute eval. Fortunately, this is only needed in this special case. If you do not use WebAuthn, you will not need eval at all!

https://github.com/ory/kratos-react-nextjs-ui/blob/master/pkg/ui/NodeInputButton.tsx

Submit Input Node

Submit buttons trigger the form submission. In pure HTML, this is just a simple submit button:

https://github.com/ory/kratos-selfservice-ui-node/blob/master/views/partials/ui_node_input_submit.hbs

If you have control over the form's payload, like in ReactJS, you need to ensure that the name of the form (e.g. method) is only submitted once with the correct value (e.g. password). Because in some cases Ory Kratos might generate multiple submit buttons with the same name attribute.

[
{
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: 'method',
type: 'submit',
value: 'password',
disabled: false,
node_type: 'input'
},
messages: [],
meta: {
label: {
id: 1070003,
text: 'Save',
type: 'info'
}
}
}
]

You need to ensure that the correct value for the name is submitted, or the user might end up updating their password instead of their email address!

https://github.com/ory/kratos-react-nextjs-ui/blob/master/pkg/ui/NodeInputSubmit.tsx

Hidden Input Node

Hidden input fields such as the csrf_token

{
type: 'input',
group: 'default',
attributes: {
name: 'csrf_token',
type: 'hidden',
value: 'By8X7TPnn/NMtXeDpK6sbshISK3t1WnezAtlMnFA6ZPsxxNmRsG8ks7WpsHMQtTLbxtqKJOiu4aArJok6/GOSw==',
required: true,
disabled: false,
node_type: 'input'
},
messages: [],
meta: {}
}

are easy to implement in both pure HTML

https://github.com/ory/kratos-selfservice-ui-node/blob/master/views/partials/ui_node_input_hidden.hbs

and single page apps like ReactJS

https://github.com/ory/kratos-react-nextjs-ui/blob/master/pkg/ui/NodeInputHidden.tsx

Default Input Node Rendering

You do not need to have one view for every node type. Instead, you can "fall back" to a default implementation. The most important node types (hidden, submit, button, checkbox) we already covered!

Here are some examples for you to get a feeling for possible payloads!

{
"type": "input",
"group": "profile",
"attributes": {
"name": "traits.email",
"type": "email",
"value": "foo@ory.sh",
"disabled": false
},
"messages": null,
"meta": {
"label": {
"id": 1070002,
"text": "E-Mail",
"type": "info"
}
}
}

You should render these components using a generic node input render logic like the one below:

https://github.com/ory/kratos-selfservice-ui-node/blob/master/views/partials/ui_node_input_default.hbs

Node Order and Labels

For all traits, the labels and orders are taken from the Identity Schema. An Identity Schema such as

{
"$id": "https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"title": "E-Mail",
"minLength": 3,
"ory.sh/kratos": {
"credentials": {
"password": {
"identifier": true
}
}
}
},
"website": {
"title": "Website",
"type": "string",
"format": "uri",
"minLength": 10
},
"consent": {
"title": "Consent",
"const": true
},
"newsletter": {
"title": "Newsletter",
"type": "boolean"
}
},
"required": ["email", "website"],
"additionalProperties": false
}
}
}

will generate the following fields - take note that the order of the JSON Schema affects the order of the nodes:

[
{
"type": "input",
"group": "profile",
"attributes": {
"name": "traits.email",
"type": "email",
"value": "foo@ory.sh",
"disabled": false
},
"messages": null,
"meta": {
"label": {
"id": 1070002,
"text": "E-Mail",
"type": "info"
}
}
},
{
"type": "input",
"group": "profile",
"attributes": {
"name": "traits.name.first",
"type": "text",
"value": "Foo",
"disabled": false
},
"messages": null,
"meta": {
"label": {
"id": 1070002,
"text": "First Name",
"type": "info"
}
}
},
{
"type": "input",
"group": "profile",
"attributes": {
"name": "traits.name.last",
"type": "text",
"value": "Bar",
"disabled": false
},
"messages": null,
"meta": {
"label": {
"id": 1070002,
"text": "Last Name",
"type": "info"
}
}
},
{
"type": "input",
"group": "profile",
"attributes": {
"name": "method",
"type": "submit",
"value": "profile",
"disabled": false
},
"messages": null,
"meta": {
"label": {
"id": 1070003,
"text": "Save",
"type": "info"
}
}
}
]

Generally, submit buttons are the last node in a group. If you wish to have more flexibility with regards to order or labeling the best option is to implement this in your UI using map, filter, and other JSON transformation functions.

Titles, Labels & Validation Messages

Ory Kratos helps users understand what is happening by providing messages that explain what went wrong or what needs to be done. Examples are "The provided credentials are invalid", "Missing property email" and similar.

Typically login, registration, settings, ... flows include such messages on different levels:

  1. At the root level, indicating that the message affects the whole request (e.g. request expired)
  2. At the method (password, oidc, profile) level, indicating that the message affects a specific method / form.
  3. At the field level, indicating that the message affects a form field (e.g. validation errors).

Each message has a layout of:

{
id: 1234,
// This ID will not change and can be used to translate the message or use your own message content.
text: 'Some default text',
// A default text in english that you can display if you do not want to customize the message.
context: {}
// A JSON object which may contain additional fields such as `expires_at`. This is helpful if you want to craft your own messages.
}

The message ID is a 7-digit number (xyyzzzz) where

  • x is the message type which is either 1 for an info message (e.g. 1020000), 4 (e.g. 4020000) for an input validation error message, and 5 (e.g. 5020000) for a generic error message.
  • yy is the module or flow this error references and can be:
  • 01 for login messages (e.g. 1010000)
  • 02 for logout messages (e.g. 1020000)
  • 03 for multi-factor authentication messages (e.g. 1030000)
  • 04 for registration messages (e.g. 1040000)
  • 05 for settings messages (e.g. 1050000)
  • 06 for account recovery messages (e.g. 1060000)
  • 07 for email/phone verification messages (e.g. 1070000)
  • zzzz is the message ID and typically starts at 0001. For example, message ID 4070001 (4 for input validation error, 07 for verification, 0001 for the concrete message) is: The verification code has expired or was otherwise invalid. Please request another code..

UI Error Codes

When building a single page app, or a native app, Ory Kratos will respond with errors in some cases. You can find an exemplary error handling logic in our ReactJS reference implementation:

https://github.com/ory/kratos-react-nextjs-ui/blob/master/pkg/errors.tsx

UI Message Codes

UI Messages are always presented as the following JSON

{
id: 1234, // A unique, fixed number
text: '...',
type: '...', // Usually "error" or "info"
context: {
// Additional, contextual information
}
}

for the values of these fields, please check the list below.

note

The section below is auto-generated. Changing it has no effect as any changes will be overwritten!

Sign in (1010001)
{
"id": 1010001,
"text": "Sign in",
"type": "info",
"context": {}
}
Sign in with security key (1010001)
{
"id": 1010001,
"text": "Sign in with security key",
"type": "info",
"context": {}
}
Sign in with {provider} (1010002)
{
"id": 1010002,
"text": "Sign in with {provider}",
"type": "info",
"context": {
"provider": "{provider}"
}
}
Please confirm this action by verifying that it is you. (1010003)
{
"id": 1010003,
"text": "Please confirm this action by verifying that it is you.",
"type": "info",
"context": {}
}
Please complete the second authentication challenge. (1010004)
{
"id": 1010004,
"text": "Please complete the second authentication challenge.",
"type": "info",
"context": {}
}
Verify (1010005)
{
"id": 1010005,
"text": "Verify",
"type": "info",
"context": {}
}
Authentication code (1010006)
{
"id": 1010006,
"text": "Authentication code",
"type": "info",
"context": {}
}
Backup recovery code (1010007)
{
"id": 1010007,
"text": "Backup recovery code",
"type": "info",
"context": {}
}
Use security key (1010008)
{
"id": 1010008,
"text": "Use security key",
"type": "info"
}
Use Authenticator (1010009)
{
"id": 1010009,
"text": "Use Authenticator",
"type": "info",
"context": {}
}
Use backup recovery code (1010010)
{
"id": 1010010,
"text": "Use backup recovery code",
"type": "info",
"context": {}
}
Continue with security key (1010011)
{
"id": 1010011,
"text": "Continue with security key",
"type": "info"
}
Prepare your WebAuthn device (e.g. security key, biometrics scanner, ...) and press continue. (1010012)
{
"id": 1010012,
"text": "Prepare your WebAuthn device (e.g. security key, biometrics scanner, ...) and press continue.",
"type": "info",
"context": {}
}
Continue (1010013)
{
"id": 1010013,
"text": "Continue",
"type": "info"
}
Sign up (1040001)
{
"id": 1040001,
"text": "Sign up",
"type": "info",
"context": {}
}
Sign up with {provider} (1040002)
{
"id": 1040002,
"text": "Sign up with {provider}",
"type": "info",
"context": {
"provider": "{provider}"
}
}
Continue (1040003)
{
"id": 1040003,
"text": "Continue",
"type": "info"
}
Sign up with security key (1040004)
{
"id": 1040004,
"text": "Sign up with security key",
"type": "info"
}
Your changes have been saved! (1050001)
{
"id": 1050001,
"text": "Your changes have been saved!",
"type": "info"
}
{
"id": 1050002,
"text": "Link {provider}",
"type": "info",
"context": {
"provider": "{provider}"
}
}
{
"id": 1050003,
"text": "Unlink {provider}",
"type": "info",
"context": {
"provider": "{provider}"
}
}
{
"id": 1050004,
"text": "Unlink TOTP Authenticator App",
"type": "info"
}
Authenticator app QR code (1050005)
{
"id": 1050005,
"text": "Authenticator app QR code",
"type": "info"
}
{secret} (1050006)
{
"id": 1050006,
"text": "{secret}",
"type": "info",
"context": {
"secret": "{secret}"
}
}
Reveal backup recovery codes (1050007)
{
"id": 1050007,
"text": "Reveal backup recovery codes",
"type": "info"
}
Generate new backup recovery codes (1050008)
{
"id": 1050008,
"text": "Generate new backup recovery codes",
"type": "info"
}
{secret} (1050009)
{
"id": 1050009,
"text": "{secret}",
"type": "info",
"context": {
"secret": "{secret}"
}
}
These are your back up recovery codes. Please keep them in a safe place! (1050010)
{
"id": 1050010,
"text": "These are your back up recovery codes. Please keep them in a safe place!",
"type": "info"
}
Confirm backup recovery codes (1050011)
{
"id": 1050011,
"text": "Confirm backup recovery codes",
"type": "info"
}
Add security key (1050012)
{
"id": 1050012,
"text": "Add security key",
"type": "info"
}
Add security key (1050012)
{
"id": 1050012,
"text": "Add security key",
"type": "info"
}
Name of the security key (1050013)
{
"id": 1050013,
"text": "Name of the security key",
"type": "info"
}
Secret was used at 2020-01-01 00:59:59 +0000 UTC (1050014)
{
"id": 1050014,
"text": "Secret was used at 2020-01-01 00:59:59 +0000 UTC",
"type": "info",
"context": {
"used_at": "2020-01-01T00:59:59Z"
}
}
{code-1}, {code-2} (1050015)
{
"id": 1050015,
"text": "{code-1}, {code-2}",
"type": "info",
"context": {
"secrets": [
{
"id": 1050009,
"text": "{code}",
"type": "info",
"context": {
"secret": "{code}"
}
},
{
"id": 1050014,
"text": "Secret was used at 2020-01-01 00:59:59 +0000 UTC",
"type": "info",
"context": {
"used_at": "2020-01-01T00:59:59Z"
}
}
]
}
}
Disable this method (1050016)
{
"id": 1050016,
"text": "Disable this method",
"type": "info"
}
This is your authenticator app secret. Use it if you can not scan the QR code. (1050017)
{
"id": 1050017,
"text": "This is your authenticator app secret. Use it if you can not scan the QR code.",
"type": "info"
}
Remove security key "{name}" (1050018)
{
"id": 1050018,
"text": "Remove security key \"{name}\"",
"type": "info",
"context": {
"added_at": "2020-01-01T00:59:59Z",
"display_name": "{name}"
}
}
You successfully recovered your account. Please change your password or set up an alternative login method (e.g. social sign in) within the next 0.02 minutes. (1060001)
{
"id": 1060001,
"text": "You successfully recovered your account. Please change your password or set up an alternative login method (e.g. social sign in) within the next 0.02 minutes.",
"type": "info",
"context": {
"privilegedSessionExpiresAt": "2020-01-01T01:01:00Z"
}
}
{
"id": 1060002,
"text": "An email containing a recovery link has been sent to the email address you provided.",
"type": "info",
"context": {}
}
Password (1070001)
{
"id": 1070001,
"text": "Password",
"type": "info"
}
{title} (1070002)
{
"id": 1070002,
"text": "{title}",
"type": "info"
}
Save (1070003)
{
"id": 1070003,
"text": "Save",
"type": "info"
}
ID (1070004)
{
"id": 1070004,
"text": "ID",
"type": "info"
}
Submit (1070005)
{
"id": 1070005,
"text": "Submit",
"type": "info"
}
Verify code (1070006)
{
"id": 1070006,
"text": "Verify code",
"type": "info"
}
Email (1070007)
{
"id": 1070007,
"text": "Email",
"type": "info"
}
{
"id": 1080001,
"text": "An email containing a verification link has been sent to the email address you provided.",
"type": "info",
"context": {}
}
You successfully verified your email address. (1080002)
{
"id": 1080002,
"text": "You successfully verified your email address.",
"type": "info"
}
{reason} (4000001)
{
"id": 4000001,
"text": "{reason}",
"type": "error"
}
Property {field} is missing. (4000002)
{
"id": 4000002,
"text": "Property {field} is missing.",
"type": "error",
"context": {
"property": "{field}"
}
}
Length must be >= 1, but got 2. (4000003)
{
"id": 4000003,
"text": "Length must be \u003e= 1, but got 2.",
"type": "error",
"context": {
"actual_length": 2,
"expected_length": 1
}
}
"{value}" is not valid "{format}" (4000004)
{
"id": 4000004,
"text": "\"{value}\" is not valid \"{format}\"",
"type": "error",
"context": {
"actual_value": "{value}",
"expected_format": "{format}"
}
}
The password can not be used because {reason}. (4000005)
{
"id": 4000005,
"text": "The password can not be used because {reason}.",
"type": "error",
"context": {
"reason": "{reason}"
}
}
The provided credentials are invalid, check for spelling mistakes in your password or username, email address, or phone number. (4000006)
{
"id": 4000006,
"text": "The provided credentials are invalid, check for spelling mistakes in your password or username, email address, or phone number.",
"type": "error",
"context": {}
}
An account with the same identifier (email, phone, username, ...) exists already. (4000007)
{
"id": 4000007,
"text": "An account with the same identifier (email, phone, username, ...) exists already.",
"type": "error",
"context": {}
}
The provided authentication code is invalid, please try again. (4000008)
{
"id": 4000008,
"text": "The provided authentication code is invalid, please try again.",
"type": "error",
"context": {}
}
Could not find any login identifiers. Did you forget to set them? This could also be caused by a server misconfiguration. (4000009)
{
"id": 4000009,
"text": "Could not find any login identifiers. Did you forget to set them? This could also be caused by a server misconfiguration.",
"type": "error"
}
Account not active yet. Did you forget to verify your email address? (4000010)
{
"id": 4000010,
"text": "Account not active yet. Did you forget to verify your email address?",
"type": "error"
}
You have no TOTP device set up. (4000011)
{
"id": 4000011,
"text": "You have no TOTP device set up.",
"type": "error",
"context": {}
}
This backup recovery code has already been used. (4000012)
{
"id": 4000012,
"text": "This backup recovery code has already been used.",
"type": "error",
"context": {}
}
You have no WebAuthn device set up. (4000013)
{
"id": 4000013,
"text": "You have no WebAuthn device set up.",
"type": "error",
"context": {}
}
You have no backup recovery codes set up. (4000014)
{
"id": 4000014,
"text": "You have no backup recovery codes set up.",
"type": "error",
"context": {}
}
This account does not exist or has no security key set up. (4000015)
{
"id": 4000015,
"text": "This account does not exist or has no security key set up.",
"type": "error",
"context": {}
}
The backup recovery code is not valid. (4000016)
{
"id": 4000016,
"text": "The backup recovery code is not valid.",
"type": "error",
"context": {}
}
The login flow expired 0.02 minutes ago, please try again. (4010001)
{
"id": 4010001,
"text": "The login flow expired 0.02 minutes ago, please try again.",
"type": "error",
"context": {
"expired_at": "2020-01-01T01:01:01Z"
}
}
Could not find a strategy to log you in with. Did you fill out the form correctly? (4010002)
{
"id": 4010002,
"text": "Could not find a strategy to log you in with. Did you fill out the form correctly?",
"type": "error"
}
Could not find a strategy to sign you up with. Did you fill out the form correctly? (4010003)
{
"id": 4010003,
"text": "Could not find a strategy to sign you up with. Did you fill out the form correctly?",
"type": "error"
}
Could not find a strategy to update your settings. Did you fill out the form correctly? (4010004)
{
"id": 4010004,
"text": "Could not find a strategy to update your settings. Did you fill out the form correctly?",
"type": "error"
}
Could not find a strategy to recover your account with. Did you fill out the form correctly? (4010005)
{
"id": 4010005,
"text": "Could not find a strategy to recover your account with. Did you fill out the form correctly?",
"type": "error"
}
Could not find a strategy to verify your account with. Did you fill out the form correctly? (4010006)
{
"id": 4010006,
"text": "Could not find a strategy to verify your account with. Did you fill out the form correctly?",
"type": "error"
}
The registration flow expired 0.02 minutes ago, please try again. (4040001)
{
"id": 4040001,
"text": "The registration flow expired 0.02 minutes ago, please try again.",
"type": "error",
"context": {
"expired_at": "2020-01-01T01:01:01Z"
}
}
The settings flow expired 0.02 minutes ago, please try again. (4050001)
{
"id": 4050001,
"text": "The settings flow expired 0.02 minutes ago, please try again.",
"type": "error",
"context": {
"expired_at": "2020-01-01T01:01:01Z"
}
}
The request was already completed successfully and can not be retried. (4060001)
{
"id": 4060001,
"text": "The request was already completed successfully and can not be retried.",
"type": "error",
"context": {}
}
The recovery flow reached a failure state and must be retried. (4060002)
{
"id": 4060002,
"text": "The recovery flow reached a failure state and must be retried.",
"type": "error",
"context": {}
}
The recovery token is invalid or has already been used. Please retry the flow. (4060004)
{
"id": 4060004,
"text": "The recovery token is invalid or has already been used. Please retry the flow.",
"type": "error",
"context": {}
}
The recovery flow expired 0.02 minutes ago, please try again. (4060005)
{
"id": 4060005,
"text": "The recovery flow expired 0.02 minutes ago, please try again.",
"type": "error",
"context": {
"expired_at": "2020-01-01T01:01:01Z"
}
}
The verification token is invalid or has already been used. Please retry the flow. (4070001)
{
"id": 4070001,
"text": "The verification token is invalid or has already been used. Please retry the flow.",
"type": "error",
"context": {}
}
The request was already completed successfully and can not be retried. (4070002)
{
"id": 4070002,
"text": "The request was already completed successfully and can not be retried.",
"type": "error",
"context": {}
}
The verification flow reached a failure state and must be retried. (4070003)
{
"id": 4070003,
"text": "The verification flow reached a failure state and must be retried.",
"type": "error",
"context": {}
}
The verification flow expired -0.02 minutes ago, please try again. (4070005)
{
"id": 4070005,
"text": "The verification flow expired -0.02 minutes ago, please try again.",
"type": "error",
"context": {
"expired_at": "2020-01-01T01:00:59Z"
}
}
{reason} (5000001)
{
"id": 5000001,
"text": "{reason}",
"type": "error",
"context": {}
}