Type-safe session authentication
The session scheme is part of the type-safe authentication scheme API. It separates two things that the named provider session authentication keeps together:
The session is the value you store for the caller, such as an access token or a user ID.
The principal is what your route handlers work with, such as a full user record.
Inside a protected route, call.session gives you the stored session and call.principal gives you the principal. Both are non-null and correctly typed.
Create a session scheme
The session<S, P>() factory takes the session type first and the principal type second. The validate block turns a session into a principal or returns null to reject the session.
By default, a request without a valid session receives 401 Unauthorized. For a browser application, you can redirect the caller to the sign-in page instead. Set the onUnauthorized handler to change the response:
A handler passed to authenticateWith() overrides this one for that route. For the complete order, see Handle failures.
Choose a transport
The transport property controls how the session travels between the client and the server:
Transport | What the client holds | Where session data lives |
|---|---|---|
| A session ID in a cookie | On the server, in a |
| A session ID in a header | On the server, in a |
| The serialized session in a cookie | On the client |
| The serialized session in a header | On the client |
The default is CookieId backed by an in-memory storage, which keeps session data on the server:
SessionStorageMemory loses everything on restart and is not shared between instances, so it suits local development. In production, use a persistent storage such as directorySessionStorage(), or your own SessionStorage implementation.
Install the Sessions plugin
A session scheme reads the session through the Sessions plugin, so the plugin must be installed before any route uses the scheme. Pass the scheme to install() and Ktor applies the transport you configured:
You can also install it on a single route subtree:
If you configure Sessions yourself, call applyTransport() inside the configuration block so the scheme and the plugin agree on the name, type, and transport:
If the plugin is missing, or if it has no provider matching the scheme's name and session type, the scheme fails at startup.
Read and change the session
Inside a route protected by a session scheme, call.session is a read-write property:
These accessors are available in the same block:
Accessor | Purpose |
|---|---|
| Reads or replaces the session. |
| Reads the session, applies your change, stores the result, and returns it. |
| Removes the session, which signs the caller out. |
Use updateSession when the new value depends on the old one:
Inside authenticateWithOptional(), there may be no session at all, so call.session is not available. Use call.sessionOrNull instead:
Sign users in and out
setSession() and clearSession() on the scheme work on any route, whether or not the scheme protects it.
Sign-in belongs on a route the scheme does not protect. The caller has no session yet, so a protected route would reject the request before your handler runs:
Update a session during authentication
Use transformSession to change the stored session as part of authentication, before validate runs. This is useful for sessions that carry an expiring token.
Return the session to use for this request, or null to reject it. Ktor writes the session back only when the value you return differs from the incoming one:
Add CSRF protection
Cookie transports send the session automatically on every request, including requests started by another site. Use the csrfProtection {} block to install the CSRF plugin for the routes this scheme protects: