Authentication¶
Authentication answers "who is calling?". An AuthenticationStrategy inspects the request
and produces an Authentication (identity + roles) that authorization then
uses.
You usually do not implement this yourself — assign a strategy from Basic Auth, JWT, or a custom authenticator.
Assigning a strategy¶
Any AuthenticationStrategy can be assigned to http.authentication or ws.authentication:
Leaving authentication unset is valid: every caller is anonymous, and the rule table alone
decides access.
Anatomy of a strategy¶
AuthenticationStrategy is a sealed type with two variants. Both carry the same optional handlers
for rendering failures; they differ only in how authentication runs:
| Variant | Authenticator | When to use |
|---|---|---|
AuthenticationStrategy.Sync |
Authenticator |
Blocking work on the request thread (typical). |
AuthenticationStrategy.Async |
AsyncAuthenticator |
Remote I/O (DB lookups, IdP calls) that should not block. |
The authenticator is the piece that looks at the request and returns an AuthenticationResult
(see Three outcomes). Sync authenticators return the result directly; async
ones return a CompletableFuture of the same type.
A strategy also exposes:
| Member | Role |
|---|---|
authenticator() |
Resolves who is calling. |
unauthorizedHandler |
Renders failed or absent authentication (default: bare HTTP 401). |
forbiddenHandler |
Renders access denied for an authenticated caller (default: 403). |
Prefer AuthenticationStrategy.Async when authentication performs remote I/O and the request
thread should be released while that work is in flight.
Three outcomes¶
An authenticator returns one of three results:
| Result | Meaning | Effect |
|---|---|---|
Success |
Credentials valid | Continue with identity + roles. |
NotAuthenticated |
No credentials present | Continue as anonymous. |
Failure |
Credentials present but invalid | Immediate 401 (reason logged, never sent to the client). |
NotAuthenticated vs Failure
Missing credentials must be NotAuthenticated — otherwise every public route becomes a 401.
Reserve Failure for bad passwords, expired tokens, or malformed headers.
Identity and roles¶
Authentication is the security token for a request. It holds an optional Identity (who is
calling) and a set of RouteRoles (what they may do). Those concerns stay separate:
Identityanswers “who?”. It declares onlyname— a human-readable identifier such as a username or subject. Concrete types may add scheme-specific fields, but roles are not part of the interface.Authentication.rolesanswers “with which rights?”. Roles are owned byAuthenticationand are supplied when the token is built viaAuthentication.authenticated(identity, roles)(rolesdefaults to empty when omitted).
Cast the identity with ctx.identity<YourType>() (checked at runtime, like ctx.attribute<T>()).
| Member | Meaning |
|---|---|
identity |
Who is calling (null when anonymous). |
roles |
Granted roles (empty when anonymous). |
isAuthenticated |
true when identity != null. |
Reading auth in handlers¶
Use ctx.authentication() (never null), ctx.identity<T>() (throws when anonymous), or
ctx.identityOrNull<T>() (null when anonymous). The same accessors are available on
WsContext after a successful upgrade, and auth is not re-checked per WebSocket message.
See Access caller identity for full HTTP and WebSocket examples.
Next steps¶
- Authorization — decide who is allowed.
- Access caller identity — read the caller in handlers.
- Error handling — customize 401 / 403 responses.
- Custom authentication — implement your own strategy.