Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: rework sessions guide #122

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 101 additions & 71 deletions guides/sessions/session-management.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,88 +25,41 @@ header.
Subsequent requests to protected API endpoints must then provide the session token in a `Cookie` request header
or alternatively in an `Authorization` header with the `Bearer` scheme (i.e. `Authorization: Bearer <JWT>`).

## Stateless vs. stateful sessions

Sessions can be configured as either stateless or stateful (server-side) sessions.

- <u>**Stateless**</u>: with stateless sessions, a session JWT is generated that contains claims about the user. For
all future requests to protected endpoints, the API validates the JWT's expiry and signature (see
[below](#session-jwt-structure) for details on the JWT structure).
- <u>**Stateful**</u> (default): with stateful sessions, a JWT is generated in the same way but session validation
makes use of a unique identifier that is generated, persisted in the database
alongside its association to a user, and stored in the session JWT under the `session_id` claim. For all future requests,
the Hanko API validates the JWT signature and uses the `session_id` to query the database for a matching active session.
Stateful sessions have the benefit of allowing for remote session revocation. This means that if a user is logged in
on one device but also has multiple sessions across other devices, the API's
[Profile](https://docs.hanko.io/api-reference/flow/profile) flow allows listing and explicitly terminating the session
on those devices. This feature is also reflected in the UI provided by the Hanko
Elements' [Profile Component](https://docs.hanko.io/guides/hanko-elements/profile-component).

To configure the desired session type:

1. Log in to [Hanko Cloud](https://cloud.hanko.io) and select your project.
2. Navigate to `Settings > Session`.
3. Under `Server side sessions`, toggle the desired session type and click `Save`.
Upon session creation, a unique identifier is generated and stored in the database, linked to the corresponding user.
This identifier is also included in the session JWT under the `session_id` claim. For subsequent requests, the API
validates the JWT signature and uses the `session_id` to query the database for an active session match. This enables
remote session revocation. As a result, if a user is logged in on one device but has multiple active sessions across
other devices, the API’s [Profile](/api-reference/flow/profile) flow facilitates the listing and
explicit termination of sessions on those devices. This functionality is also accessible through the UI via the Hanko
Elements’ [Profile Component](/guides/hanko-elements/profile-component).

## Session validation

JWTs should be parsed and its claims and signature validated to ensure the token is authentic and has not been tampered
with or has expired.

### Validating stateless sessions

The JWT token signature is generated using the RS256 (RSA Signature with SHA-256) algorithm. It is an asymmetric
algorithm, that uses a public/private key pair. The private key is kept secret and stored by the Hanko API. It is
used to create the token's signature.
The public key, a JSON Web Key ([JWK](https://datatracker.ietf.org/doc/html/rfc7517)), is published through the Hanko
API's `.well-known/jwks.json` endpoint as part of a JSON Web Key Set
([JWKS](https://datatracker.ietf.org/doc/html/rfc7517#section-5)). To validate the JWT:

1. Fetch the JWKS from the`.well-known/jwks.json`
[endpoint](https://docs.hanko.io/api-reference/public/well-known/get-json-web-key-set).
2. Parse and validate the JWT using the JWKS. We recommend using a web framework middleware (if you are using one) or
a third-party library instead of parsing and validating the token yourself. Use a library that supports the
aforementioned signing algorithm (RS256).

<Accordion title="Third-party library JWK set support">
Some libraries contain convenience functions for fetching remote or local JWK Sets and using the result of this
operation as an optional argument in their central verification functions
(e.g. the JavaScript [jose](https://www.npmjs.com/package/jose) package's
[createRemoteJWKSet](https://github.com/panva/jose/blob/main/docs/jwks/remote/functions/createRemoteJWKSet.md#examples)
, see [this guide](https://docs.hanko.io/quickstarts/backend/node#express) for an example of its
usage for validating a stateless session token). If the library of your choice does not support this, you have to match
the `kid` property in the [header](#jwt-header) of your session token with the `kid` of a JWK contained in the fetched JWK Set and use the matching
JWK to verify the token (see [this](https://docs.hanko.io/quickstarts/backend/rust) guide for an example).
</Accordion>

### Validating stateful sessions

With stateful sessions you can instead use the `sessions/validate`
[endpoint](https://docs.hanko.io/api-reference/public/session-management/validate-a-session-1)
to validate the session. You need to provide the session token in the request body. The endpoint validates the token's
To manually validate a session you can use the API's `sessions/validate` [endpoint](/api-reference/public/session-management/validate-a-session-1).
You need to provide the session token in the request body. The endpoint validates the token's
expiry and signature and ensures that the encoded `session_id` matches a persisted session.

The session endpoint returns an object that contains information about the validity of a session, the
session token's expiry as well as the ID of the user the session is associated with. The latter allows you to later retrieve
additional user information through [public](https://docs.hanko.io/api-reference/public/user-management/get-a-user-by-id)
or [administrative](https://docs.hanko.io/api-reference/admin/user-management/get-a-user-by-id) user management
endpoints.
session token's expiry as well as claims about the user the session is associated with.

You can also use the Hanko Frontend SDK to check for session validity. See
[Using the Frontend SDK](/guides/hanko-elements/using-frontend-sdk#sessions) for details.

## Session termination

Depending on configuration, there multiple ways of (explicitly or implicitly) terminating a session.
Depending on your tenant's configuration, there are various methods available for explicitly or implicitly terminating
a session.

### Explicit user logout

A user can explicitly request a logout. This requires a request to the `/users/logout`
[endpoint](https://docs.hanko.io/api-reference/public/user-management/log-out-the-current-user).
A successful request clears session cookies and, if stateful sessions are enabled, terminates the session associated
with the session token.
A successful request clears session cookies and terminates the session associated with the session token.

### Limiting session duration

Sessions can be terminated (or: become invalid) by exceeding an expiry. This expiry can be configured by setting the
session duration. Session duration determines how long a user remains logged in after authenticating.
session duration. The session duration determines how long a user remains logged in after authenticating.
By default, the session duration is set to 12 hours in the Hanko Cloud Console.
You can change this to a value between 1 minute and 1 month to suit your application's needs.

Expand All @@ -122,17 +75,89 @@ Sessions can also be (implicitly) terminated by exceeding the number of allowed
Hanko allows 5 concurrent sessions. If a user establishes another session, then the oldest session becomes
invalid.

<Note>
Stateful (server-side) sessions must be enabled (see:
[Stateless vs. stateful sessions](#stateless-vs-stateful-sessions)) to configure concurrent session limits.
</Note>

To configure the allowed amount of concurrent sessions:

1. Log in to [Hanko Cloud](https://cloud.hanko.io) and select your project.
2. Navigate to `Settings > Session`.
3. Under `Session limit`, enter the desired amount and click `Save`.

### Limiting cookie retention period

You can configure the retention period of session cookies. Possible configurations are:

- **Persistent** (default): This type sets the `Max-Age` attribute of the cookie to the specified
[session duration](#limiting-session-duration) (in seconds). The cookie will be automatically deleted from the browser
once the specified expiry time has been reached.
- **Session**: This type sets the `Max-Age` attribute of the cookie to 0, resulting in the cookie being deleted from the
browser when the browser tab or window is closed.
- **Prompt**: If you use [Hanko Elements](/guides/hanko-elements/introduction) (+1.3) then selecting this type results
in Hanko Elements displaying a `Stay signed in` checkbox on the login form which allows users to determine the cookie
retention period: if the checkbox is checked, the cookie will be a `Persistent` cookie, if it is unchecked the
cookie will be a `Session` cookie.

To configure the cookie retention period:

1. Log in to [Hanko Cloud](https://cloud.hanko.io) and select your project.
2. Navigate to `Settings > Session`.
3. Under `Stay signed in (cookie type)`, select the desired type and click `Save`.

## Session self-service

There are a number of configurations options for your tenant that provide control over how users manage their session
through the profile.

### Allowing session revocation

You can control whether the `Profile` flow permits revocation of sessions (via dedicated `session_delete` action).
If you use [Hanko Elements](/guides/hanko-elements/introduction) (+1.3) then the
[Profile component](guides/hanko-elements/profile-component) provides interface elements that allow users to revoke
active sessions (except the than the one currently in use).

To control self-service session revocation behaviour:

1. Log in to [Hanko Cloud](https://cloud.hanko.io) and select your project.
2. Navigate to `Settings > Session`.
3. Use the `Allow session revocation` toggle to allow or disallow end-user session revocation via profile.

### Showing sessions on profile

You can control whether information about active sessions is returned in the `Profile` flow.
If you use [Hanko Elements](/guides/hanko-elements/introduction) (+1.3) then information about active sessions is
displayed in the [Profile component](guides/hanko-elements/profile-component).

To control whether information about active sessions is returned from the `Profile` flow (and shown in Hanko Elements):

1. Log in to [Hanko Cloud](https://cloud.hanko.io) and select your project.
2. Navigate to `Settings > Session`.
3. Use the `Show sessions on profile` toggle to include information about active sessions in the profil API response
(and the [Hanko Elements profile component](guides/hanko-elements/profile-component)).

## Session metadata retention

You can control whether active session metadata is persisted with a session and returned from session validation
[endpoints](/api-reference/public/session-management/validate-a-session). This metadata currently includes the IP
address and the user agent used to establish a session.

To control the type of metadata retained with a session:

1. Log in to [Hanko Cloud](https://cloud.hanko.io) and select your project.
2. Navigate to `Settings > Session`.
3. You can configure the following options:
- Use the `Acquire IP address` toggle to include information about the IP (IPv4/IPv6) address used to establish
the session.
- Use the `Acquire user agent` toggle to include information about the user agent used to establish the session.

## Session callbacks

You can hook into the session lifecycle by using the Hanko Frontend SDKs central client. See
[Using the Frontend SDK](/guides/hanko-elements/using-frontend-sdk#events) for details.

## Administrative session management

The [Admin API](/api-reference/admin/introduction#learn-how-to-use-the-hanko-admin-api) allows administrators to
[create sessions](/api-reference/admin/session-management/create-session),
[list active sessions](/api-reference/admin/session-management/list-sessions) and
[delete sessions](/api-reference/admin/session-management/delete-session) for a specific user.

## Session JWT structure

Expand Down Expand Up @@ -176,7 +201,8 @@ dot (`.`). The JWT payload contains the claims about a user.
"exp": 17123108000,
"iat": 1712307200,
"session_id": "140f3967-ab87-4caa-80bd-603ac59c545f",
"sub": "9930ac89-1584-488c-bd63-1607f03ab1e8"
"sub": "9930ac89-1584-488c-bd63-1607f03ab1e8",
"username": "johndoe"
}
```

Expand Down Expand Up @@ -213,7 +239,11 @@ dot (`.`). The JWT payload contains the claims about a user.
The ID of the session.
</ResponseField>

<ResponseField name="sub" type="integer">
<ResponseField name="sub" type="string">
The ID of the user.
</ResponseField>

<ResponseField name="username" type="string">
The username of the user (if set).
</ResponseField>

58 changes: 53 additions & 5 deletions openapi-flow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1832,7 +1832,7 @@ components:
type: object
properties:
user:
$ref: "#/components/schemas/ProfileData"
$ref: "#/components/schemas/ProfileDataUser"
PayloadRequestOptions:
type: object
properties:
Expand All @@ -1857,8 +1857,51 @@ components:
format: uri
required:
- redirect_url
ProfileData:
description: User data
ProfileDataSessions:
description: List of active sessions for this user.
type: array
items:
type: object
properties:
created_at:
description: Time of creation of the session.
type: string
format: date-time
current:
description: Indicates whether this session is the session currently used.
type: boolean
expires_at:
description: Time of expiry of the session.
type: string
format: date-time
id:
description: The ID of the session.
type: string
format: uuid
ip_address:
oneOf:
- description: IPv4 address the session was initialized from.
type: string
format: ipv4
- description: IPv6 address the session was initialized from.
type: string
format: ipv6
last_used:
description: Time of last usage of this session.
type: string
format: date-time
user_agent:
description: |
User agent string consisting of the native platform that the browser is running on (Windows, Mac,
Linux, Android, etc.) and a parenthesised name of the user agent.
type: string
user_agent_raw:
description: |
The complete user agent, i.e. the exact value of the `User-Agent` header the API received
in the request(s) for establishing the session.
type: string
ProfileDataUser:
description: Data pertaining to the user associated with the current session.
type: object
properties:
user_id:
Expand Down Expand Up @@ -1928,7 +1971,7 @@ components:
type: string
payload:
description: |
Additional data that can be used by the client (e.g `user` data provided in the
Additional data that can be used by the client (e.g. `user` or `sessions` data provided in the
profile flow) or should/must be used as intermediary data in an out of band process to produce input data
for advancing the flow (e.g. the WebAuthn credential request/creation options that must be passed to the
Webauthn API to produce an assertion/attestation).
Expand Down Expand Up @@ -2230,7 +2273,12 @@ components:
enum:
- "profile_init"
payload:
$ref: "#/components/schemas/PayloadProfileData"
allOf:
- $ref: "#/components/schemas/PayloadProfileData"
- type: object
properties:
sessions:
$ref: "#/components/schemas/ProfileDataSessions"
status:
type: integer
enum:
Expand Down