Architecture

Identity

Authentication stays where it is. Your identity provider keeps signing users in exactly as it does today — Keycloak, Auth0, Amazon Cognito, or another compatible provider. The framework consumes verified identity through one deliberately narrow seam.

What your identity provider owns

Credentials, passwords, MFA and SSO federation belong to your identity provider — for human users and machine callers alike. The framework owns none of it: it never holds credential material and never parses tokens itself. Verification belongs to the seam and the provider behind it.

What identity is not

Two things deliberately do not come from identity. The first is business authorisation. Identity answers who is calling; authorisation answers what they may do, and where. The framework requires neither to be its own: authorisation can come from identity-provider claims, from your existing IAM, or from Access Management.

The second is tenant context. The request's tenant is derived from the data being accessed, never trusted from a session claim — a token that asserts a tenant identifier does not get one. How that derivation works is the tenant API page's subject.

One resolution seam

Every request crosses one boundary on the way in: an identity resolver takes the incoming request and returns the authenticated user — or nothing, and nothing is an authentication failure. There is no partially authenticated state, and no route silently skips the step: opting out of authentication is only ever explicit. The seam takes three shapes behind one interface — a local resolver against your own user table, the working default in delivered systems; your identity provider; or your existing IAM. Because they share the seam, moving from standalone to central identity is configuration, not a rewrite.

Resolved identity is cached briefly and safely, with a stated, configurable revocation window — typically under a minute, agreed for each deployment rather than discovered during an incident. An unverifiable token always denies: verification is never cached, so a bad credential cannot ride a warm entry. If the provider itself goes unreachable and nothing usable is held, the request denies. Where something usable is held, the deployment's posture decides, within a bounded window — refuse anyway, or keep serving reads while writes stop. Serving on a stale answer is never silent: it is flagged on the request's log line and recorded against anything it touches, so who acted during an outage, and on what authority, stays answerable. Each posture is agreed in writing.

Migrations keep the audit trail

Users arriving from an external identity provider are mirrored into local user records, so attribution and audit keep pointing at real users in your own database. That is what makes an identity migration genuinely low-cost: move from standalone to central identity and every historical audit record still resolves — the trail survives the transition. More than one identity provider can coexist in a single deployment, so a migration does not demand a hard cut-over.

Using identity provider roles directly

For simple systems, deriving authorisation from identity-provider claims is a legitimate end state, not a stopgap. Where roles are few and apply everywhere a user acts, the claims your provider already issues may be all the authorisation you need — consumed through the same seam, enforced with the same deny-by-default posture. Dedicated authorisation earns its place when access varies by place: many tenants, administration delegated to customers, grants that need review and revocation. That is what Access Management is for.

Integrating an existing IAM

If you already run an IAM, it stays. Your system implements one contract: who is this caller, and what do they hold. The framework enforces the answers it is handed. Users, roles and administrative tooling stay exactly where they are. The call sits behind the identity cache, under the same outage posture as any other resolver. Whether an existing IAM fits the contract is a short conversation — ask us.

Share links and public routes

Not every caller signs in. A route that does not authenticate is an explicit opt-out, declared in code and never a default — and it has to say so twice: a route that skips authentication must also declare that it requires no permission, and declaring only one of the two fails at startup rather than reaching production quietly. Share links work the same way: the route is written out, and what its token may reach is application code a reviewer can read. Across the whole identity surface the rule is the same: omission never opts into risk.