Ory Kratos: The Auth Server That Refuses to Be Your Login Page
A deep dive into the headless identity engine behind custom UX, schema-driven identities, and flow-based authentication.
- Kratos treats identity as a hardened backend state machine, not as a bundled login product with opinions about your interface.
- Its biggest leap is flow continuity, which lets login, recovery, and verification pause across redirects without collapsing the identity context.
- The repo replaces a rigid users table with schema-driven traits, so identity can evolve without making database migrations the center of the story.
- Kratos feels production-grade because it separates public and admin surfaces, queues delivery work, and isolates risky template execution.
Most auth products try to win by giving you less work. Kratos wins by taking away the wrong work. It removes the login page as a product decision, so your app can own the experience while the backend absorbs the security risk.
That sounds austere until you look at the shape of the repo. Kratos is not a generic CRUD service with a couple of endpoints bolted on. It is a choreography engine for identity, built around flows, resumable state, and delivery that can survive the messy parts of the real world.
We started Ory Kratos because we saw so many developers struggle with OAuth2 and OpenID Connect.
The login box is the product, except Kratos deletes it
Kratos makes a blunt bet: the UI should belong to the application, not the identity layer. That means no bundled login experience to fight, no theme to work around, and no hidden assumptions about how your brand or routing should behave.
The upside is control. The downside is that you have to build the front end yourself. Kratos takes that hit on purpose because the risky parts are not the page chrome, they are the rules around credentials, recovery, verification, and session handling.
This is why the project feels more like infrastructure than a library. It is designed to stay out of your way visually while staying in the path of every dangerous transition.
Identity is a flow, not a form
This is Kratos’s most important idea. Login, registration, recovery, and MFA are not one-off requests. They are flows that can pause, hand work to another system, and resume later without losing their place.
That is what continuity exists to protect. A browser redirect, an email click, or a social sign-in callback should not force the system to forget who started the flow or what state it was in. The continuity layer keeps that context alive while still resisting session fixation and other identity mix-ups.
Why Kratos does not use a classic users table
A traditional identity system wants fixed columns. Email here. Password there. Maybe a few more fields if the data model gets ambitious. Kratos goes the other direction and treats identity traits as schema-driven data, which is far more tolerant of different user shapes over time.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "identity",
"type": "object",
"properties": {
"traits": {
"type": "object",
"properties": {
"email": { "type": "string", "format": "email" },
"name": { "type": "object" },
"company": { "type": "string" }
},
"required": ["email"]
}
}
}
The point is not that JSON Schema is trendy. The point is that it lets identity evolve without turning schema migrations into the bottleneck for every new user property. That matters when one product team wants a consumer profile and another wants B2B attributes, but both still need the same authentication backbone.
| Approach | Identity shape | Change management | Tradeoff |
|---|---|---|---|
| Classic users table | Fixed columns | ALTER TABLE becomes the growth path | Simple at first, brittle later |
| Kratos schema-driven traits | Validated JSON traits | Schema changes are first-class | More flexible, less rigid |
| Ad hoc app-owned auth | Whatever the app invents | Every team reinvents migrations | Maximum freedom, maximum drift |
The same pattern shows up in the codebase. Kratos isolates dynamic mapping instead of letting it leak into the core system. That keeps identity logic predictable while still allowing external profile data to be transformed when it needs to be.
The courier is where auth gets real
Identity is never just a request-response loop. It has to send codes, links, and alerts into systems that may be slow, flaky, or temporarily unavailable. Kratos handles that with a courier subsystem that behaves more like durable infrastructure than a helper function.
Messages are queued, retried, and eventually marked sent or abandoned. The important detail is that delivery is asynchronous, which means auth flows do not block on the mail server feeling cooperative.
We needed a new system to import PBKDF2-hashed passwords and this disqualified AWS Cognito.
Why the repository feels like a production system, not a library
The repo structure tells you a lot. There is a registry pattern for dependency injection, separate public and admin surfaces, a dedicated continuity layer, and a courier that gets its own operational logic. That is not how a small package is organized. It is how a service meant to live inside production traffic is organized.
The split between public and admin APIs is especially revealing. Public endpoints need CSRF protection, browser safety, and user-facing ergonomics. Admin operations need different assumptions entirely. Kratos keeps those worlds apart instead of pretending they are the same thing.
It also pays attention to the boring but important stuff: hermetic builds, database portability, process isolation for Jsonnet execution, and observability hooks. That is what makes the project feel durable rather than merely clever.
Where Kratos wins, and where it deliberately does not
Kratos is not trying to be every identity product at once. It is narrower than Auth0 or Okta, more specialized than an all-in-one IAM suite, and more opinionated than a homegrown auth stack that lives inside your application code.
| System | What you get | What you give up | Best fit |
|---|---|---|---|
| Auth0 / Okta | Managed identity with bundled UX | Less control over the full experience | Teams that want speed over ownership |
| Keycloak | Broad all-in-one IAM | Heavier surface area and more moving parts | Organizations that want a self-hosted suite |
| Homegrown auth | Total flexibility | You own every edge case and every bug | Small systems with unusual constraints |
| Ory Kratos | Headless, schema-driven, flow-based auth | You build the UI and own the integration | Teams that want custom UX with hardened backend primitives |
The tradeoff is especially clear around multi-tenancy. Kratos does not pretend every deployment wants to model multiple organizations inside one identity store. It favors cleaner boundaries, separate instances when needed, and a narrower definition of what the product should own.
That restraint is part of the design. Kratos is compelling precisely because it refuses to become a bloated identity suite when a specialized engine will do.





