Identity Schema
The Identity Schema is a JSON schema that describes the data model that makes up the identity. In Ory, every identity can have its own model (a unique set of data fields).
Available presets
The Ory Network provides three basic Identity Schema presets.
Username and password
This preset is useful for applications that don't need the user's email address and don't prioritize a high degree of user anonymity.
This preset disables account verification, account recovery, and account enumeration defenses. Don't use it in security-sensitive environments.
With this preset, every identity has a single trait - the username
. The username
is the login identifier:
// Identity example
{
id: "6e9d3d30-f93e-4630-901f-c2096953723d",
traits: {
username: "some-username",
},
}
Email and password
With this preset, identities have a single trait, the email
. The email
is the login identifier and is used for email
verification and for account recovery:
// Identity example
{
id: "6e9d3d30-f93e-4630-901f-c2096953723d",
traits: {
email: "foo@bar.com",
},
}
Example with name and newsletter opt-in
This preset has an email field, a first name, last name, and a "newsletter" checkbox.
// Identity example
{
id: "6e9d3d30-f93e-4630-901f-c2096953723d",
traits: {
email: "foo@bar.com",
name: {
first: "Foo",
last: "Bar",
},
newsletter: true,
},
}
Identity Schema vocabulary extensions
Because the system doesn't know that a particular field has a system-relevant meaning, you have to specify that in the schema. For example:
- This email address should be used for recovering a lost password.
- This identifier (username or email) should be used for logging in with a password.
- This is the phone number used for SMS 2FA.
A vocabulary extension can be used within a property:
{
$id: "http://mydomain.com/schemas/v2/customer.schema.json",
$schema: "http://json-schema.org/draft-07/schema#",
title: "A customer (v2)",
type: "object",
properties: {
traits: {
type: "object",
properties: {
email: {
title: "E-Mail",
type: "string",
format: "email",
// This tells Ory Identities that the field should be used as the "username" for the username+password flow.
// It's an extension to the regular JSON Schema vocabulary.
"ory.sh/kratos": {
credentials: {
password: {
identifier: true,
},
},
},
},
},
},
},
}
Identifier for username and password flows
You can configure Ory Identities (Ory Kratos) to use specific fields as the identity's identifier. In this example, the password
is set as the identifier:
{
"ory.sh/kratos": {
"credentials": {
"password": {
"identifier": true
}
}
}
}
Email
Looking at the traits from above
traits:
# These are just examples
email: office@ory.sh
name:
first: Aeneas
last: Rekkas
favorite_animal: Dog
accepted_tos: true
and using a JSON Schema that uses the email
field as the identifier for the password flow:
{
$id: "http://mydomain.com/schemas/v2/customer.schema.json",
$schema: "http://json-schema.org/draft-07/schema#",
title: "A customer (v2)",
type: "object",
properties: {
traits: {
type: "object",
properties: {
email: {
title: "E-Mail",
type: "string",
format: "email",
// This tells Ory Identities that the field should be used as the "username" for the username and password flow.
"ory.sh/kratos": {
credentials: {
password: {
identifier: true,
},
},
},
},
name: {
type: "object",
properties: {
first: {
type: "string",
},
last: {
type: "string",
},
},
},
favorite_animal: {
type: "string",
},
accepted_tos: {
type: "string",
},
},
required: ["email"],
additionalProperties: false,
},
},
}
In this example, Ory understands that traits.email='office@ory.sh'
is the identifier for this identity. The system must get
office@ory.sh
and a password to sign in an user.
Username and Password Credentials contains more information and examples about credentials usage.
Note that the format
field of the Identity Schema will perform validation of the given trait. In this example, the email address
is validated using the JSONSchema rule
set.
Phone number
Let's extend the Identity Schema from the previous chapter with a phone number:
{
$id: "http://mydomain.com/schemas/v2/customer.schema.json",
$schema: "http://json-schema.org/draft-07/schema#",
title: "A customer (v2)",
type: "object",
properties: {
traits: {
type: "object",
properties: {
email: {
title: "E-Mail",
type: "string",
format: "email",
// This tells Ory Identities that the field should be used as the "username" for the Username and Password Flow.
"ory.sh/kratos": {
credentials: {
password: {
identifier: true,
},
},
},
},
phone: {
title: "Phone",
type: "string",
format: "tel",
// The phone number is marked as an identifier. This allows the user to log in with both email and phone number.
"ory.sh/kratos": {
credentials: {
password: {
identifier: true,
},
},
},
},
name: {
type: "object",
properties: {
first: {
type: "string",
},
last: {
type: "string",
},
},
},
favorite_animal: {
type: "string",
},
accepted_tos: {
type: "string",
},
},
required: ["email"],
additionalProperties: false,
},
},
}
By using the "format": "tel"
field we enable validation of phone numbers using the Golang
port of Google's libphonenumber.
Examples
Choosing between username, email, and phone Number
Before you start, you need to decide what data you want to collect from your users and why! It's hard to change this decision afterwards, so make sure you've taken everything into account!
When logging in, the user will use a login identifier and a password to sign up and in. The identifier can be
- a username - "john.doe" or "johndoe123" or "oryuser",
- an email address -
john.doe@gmail.com
, - a phone number -
+49-1234-4321-1234-4321
.
All of these approaches have up- and downsides.
Using the email address as the login identifier is easy to remember, doesn't require additional fields (because the email address is already being collected), and is usually unique. It's usually unique because sometimes companies use a "shared" email account (for example office@acme.org) to access services. In that case, multiple real identities are using the same email identifier to log in.
The email address however represents a unique identifier and personally identifiable information (PII). An attacker could for
example check if the email address john.doe@gmail.com
is registered at for example an adult website and use that information for
blackmail (see Account Enumeration Attacks).
The same considerations apply to using a phone number as the primary registration & login identifier.
Using a free text username reduces the privacy risk because it's much harder to make a connection between the username and a real world identity. It's still possible in cases where users choose a username such as "john.doe.from.newyork.1970", but finding the right username identifier is still difficult and there is plausible deniability because anyone could use that username.
A free text username however requires capturing additional fields (for example an email address for password resets / account
recovery) and is hard to remember. It's often very difficult to find unique usernames as people tend to use a combination of their
names and initials such as john.doe
which has a high chance of collision. Therefore, one ends up with usernames such as
john.doe1234432
.
It's important to understand that Ory Identities lowercases all password
identifiers and therefore E-Mail addresses. Characters
+
or .
which have special meaning for some E-Mail Providers such as Gmail aren't normalized:
userNAME
is equal tousername
foo@BaR.com
is equal tofoo@bar.com
foo+baz@bar.com
is NOT equal tofoo@bar.com
foo.baz@bar.com
is NOT equal tofoobaz@bar.com
You need to decide which route you want to take.
Pick the right identity schema
When processing an identity and its traits, the method will use JSON Schema to extract one or more identifiers.
Use Case: Email and Password
To use the email address as the login identifier, define the following Identity JSON Schema:
{
"$id": "https://example.com/registration.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"ory.sh/kratos": {
"credentials": {
"password": {
"identifier": true
}
}
}
}
}
}
}
}
Use case: multiple emails and password
You can allow users to sign up with multiple E-Mail Addresses and use any of those for log in:
{
"$id": "https://example.com/registration.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"emails": {
"type": "array",
"items": {
"type": "string",
"format": "email",
"ory.sh/kratos": {
"credentials": {
"password": {
"identifier": true
}
}
}
}
}
}
}
}
}
Use case: username and password
To use a username as the login identifier, define the following Identity JSON Schema:
{
"$id": "https://example.com/registration.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"username": {
"type": "string",
"ory.sh/kratos": {
"credentials": {
"password": {
"identifier": true
}
}
}
}
}
}
}
}
Use Case: username and email and password
You may also mix usernames and passwords:
{
"$id": "https://example.com/registration.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"ory.sh/kratos": {
"credentials": {
"password": {
"identifier": true
}
}
}
},
"username": {
"type": "string",
"ory.sh/kratos": {
"credentials": {
"password": {
"identifier": true
}
}
}
}
}
}
}
}
Use case: phone number and password
{
"$id": "https://example.com/registration.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"phone": {
"type": "string",
"format": "tel",
"ory.sh/kratos": {
"credentials": {
"password": {
"identifier": true
}
}
}
}
}
}
}
}