Decoupling User Profiles with Ory Kratos Identity Schemas
Learn how to use Ory Kratos Identity Schemas to decouple user profile management from your database, allowing for flexible, JSON-driven user traits.
11 Jun 2026, 14:23 UTC

The Rigid Schema Problem
Most authentication systems force you into a predefined user model. If you need to add a "company_id" or a "preferred_timezone" to your user profile, you typically have to run a database migration, update your ORM models, and modify your registration API logic. This creates a tight coupling between your identity provider and your application's business requirements.
Ory Kratos solves this by treating user traits as a flexible JSON object governed by a JSON Schema. Instead of modifying a database table, you define your identity requirements in a configuration file. Kratos then handles the validation, storage, and retrieval of these traits automatically, allowing you to evolve your user profile without touching the core infrastructure.
How Identity Schemas Work
In Kratos, an Identity is separated into two parts: the system-managed metadata (like the unique ID and status) and the Traits. Traits are the custom fields you define for your users.
By using a JSON Schema, you can enforce strict rules on these traits. You can specify which fields are required for registration, which are optional, and which must follow a specific format (like an email regex). Because Kratos is "headless," it doesn't provide the HTML forms; it provides the validation logic and the API endpoints. Your frontend sends the form data to Kratos, and Kratos rejects the request if it doesn't match your schema.
Practical Example: Defining a Custom Profile
To implement a custom user profile, you create an identity.schema.json file. In this example, we will require a username and email, but make a "bio" optional.
{
"$id": "https://schemas.ory.sh/kratos/identity/1",
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"title": "E-Mail",
"ory.sh/kratos": {
"credentials": {
"recovery": {
"implemented": true
}
},
"ui": {
"placeholder": "[contact removed]"
}
}
},
"username": {
"type": "string",
"title": "Username",
"minLength": 3
},
"bio": {
"type": "string",
"title": "Short Bio",
"maxLength": 200
}
},
"required": ["email", "username"]
}
Implementing the Schema
To apply this configuration, follow these steps:
- Configuration: Map the path to your schema file in the Kratos configuration file (usually
kratos.yaml) under theidentity.schema.urlor local file path setting. - Deployment: Restart the Kratos service to load the new schema.
- Verification: Attempt to register a user via the Kratos Public API. If you omit the
username, the API should return a400 Bad Requestwith a validation error specifically identifying the missing field.
Trade-offs and Limitations
While JSON Schemas provide immense flexibility, they have limitations regarding complex business logic. For example, JSON Schema cannot verify if a username is already taken in a separate external database, nor can it perform cross-field validation (e.g., "if field A is X, then field B must be Y").
To handle these scenarios, you must use Webhooks. Kratos can trigger a webhook during the registration or update flow. Your application can then intercept this call, perform the complex validation, and tell Kratos whether to allow or reject the identity change.
Checking Your Results
To verify that your traits are being stored correctly, you can use the Kratos Admin API. Run the following command from a terminal with access to the Admin API (typically on port 4434):
# Replace {identity_id} with the actual ID returned during registration
curl -X GET "http://localhost:4434/admin/identities/{identity_id}"
The expected output is a JSON object where the traits key contains the exact values provided during registration, confirming that Kratos is persisting your custom schema fields without requiring a manual database migration.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.