Sessions and JSON Web Tokens

Hanko creates a session when users authenticate (registration or sign-in). Each session is represented by a JSON Web Token (JWT) that encodes user claims as JSON objects. All Hanko JWTs are signed using JSON Web Signature (JWS) standards for security. Authentication operations (login, registration) return session tokens in the X-Auth-Token response header.
When using Hanko Elements or the Hanko Frontend SDK, the session token is read from the X-Auth-Token header and used to set a cookie (name: hanko) on the client-side. If you use neither Hanko Elements nor the SDK, you have to take care of storing the session token yourself.
Protected API requests must include the session token either in a Cookie header or as a Bearer token in the Authorization header (Authorization: Bearer <JWT>). 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 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.

Session validation

Validate sessions manually using the sessions/validate endpoint. Send the session token in the request body for validation of expiry, signature, and session persistence. The validation response includes session validity status, token expiry information, and associated user claims. Alternatively, use the Hanko Frontend SDK for session validation. See Using the Frontend SDK for implementation details.

Session termination

Sessions can be terminated through various explicit and implicit methods, depending on your tenant configuration.

Explicit user logout

Users can manually log out through the /users/logout endpoint. Successful logout requests clear session cookies and terminate the associated session.

Limiting session duration

Sessions automatically expire based on configured duration settings. The session duration controls how long users remain authenticated after login. Hanko Cloud defaults to 12 hours, but you can configure anywhere from 1 minute to 1 month. Configure session duration:
  1. Log in to Hanko Cloud and select your project
  2. Navigate to Settings > Session
  3. Set your desired Session duration and click Save

Limiting concurrent sessions

Sessions terminate automatically when exceeding the allowed concurrent session limit. Hanko defaults to 5 concurrent sessions - creating a new session invalidates the oldest existing session. Configure concurrent session limits:
  1. Log in to Hanko Cloud and select your project
  2. Navigate to Settings > Session
  3. Set your desired Session limit and click Save
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 (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 (+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 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 configure whether the Profile flow permits session revocation via the dedicated session_delete action. If you are using Hanko Elements version 1.3 or later, the Profile component provides interface elements that allow users to revoke active sessions, excluding the session currently in use. This session can be revoked manually through a manual logout. To control self-service session revocation behaviour:
  1. Log in to Hanko Cloud 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 (+1.3) then information about active sessions is displayed in the 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 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).

Session metadata retention

You can control whether active session metadata is persisted with a session and returned from session validation endpoints. 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 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 for details.

Administrative session management

The Admin API allows administrators to create sessions, list active sessions and delete sessions for a specific user.

Session token customization

Session tokens can be extended with custom claims by defining them using Go’s text/template syntax. These claims are evaluated during JWT generation and may include static values, dynamic expressions, or nested structures. To define custom claims:
  1. Log in to Hanko Cloud and select your project.
  2. Navigate to Settings > Session.
  3. Locate the Customize session token section.
  4. Enter your custom claims in YAML format within the provided textarea.
  5. Click Save to apply your changes.

Accessing user data in templates

Each template has access to user data via the .User field, which includes:
  • .User.UserID: The user’s unique ID (string)
  • .User.Email: Email details (optional)
    • .User.Email.Address: The email address (string)
    • .User.Email.IsPrimary: Indicates if the email is the primary address (boolean)
    • .User.Email.IsVerified: Indicates if the email is verified (boolean)
  • .User.Username: The user’s username (string, optional)
  • .User.Metadata: The user’s public and unsafe metadata (optional)
    • .User.Metadata.Public: The user’s public metadata (object)
    • .User.Metadata.Unsafe: The user’s unsafe metadata (object)

Accessing user metadata

.User.Metadata.Public and .User.Metadata.Unsafe can be accessed and queried using GJSON Path Syntax (try it out in the playground). Assume that a user’s public metadata consisted of the following data:
{
    "display_name": "GamerDude",
    "favorite_games": [
        {
            "name": "Legends of Valor",
            "genre": "RPG",
            "playtime_hours": 142.3
        },
        {
            "name": "Space Raiders",
            "genre": "Sci-Fi Shooter",
            "playtime_hours": 87.6
        }
    ]
}
Then you could, for example, access this data in the following ways in your templates:
display_name: '{{ .User.Metadata.Public "display_name" }}'
favorite_games: '{{ .User.Metadata.Public "favorite_games" }}'
favorite_games_with_playtime_over_100: '{{ .User.Metadata.Public "favorite_games.#(playtime_hours>100)" }}'
favorite_genres: '{{ .User.Metadata.Public "favorite_games.#.genre" }}'
Ensure you use proper quoting when accessing metadata. .User.Metadata.Public and .User.Metadata.Unsafe are function calls internally and and the given path argument must be a string, so it must be double quoted. If you use use double quotes for your entire claim template then the path argument must be escaped, i.e.:"{{ .User.Metadata.Public \"display_name\" }}"

Example configuration

role: "user"                                           # Static value
user_email: "{{.User.Email.Address}}"                  # Templated string
is_verified: "{{.User.Email.IsVerified}}"              # Boolean from user data
metadata:                                              # Nested map
  greeting: "Hello {{.User.Username}}"
  source: '{{ .User.Metadata.Public "display_name" }}' # Data read from public metadata
  ui_theme: '{{ .User.Metadata.Unsafe "ui_theme" }}'   # Data read from unsafe metadata
scopes:                                                # Slice with templated value
    - "read"
    - "write"
    - "{{if .User.Email.IsVerified}}admin{{else}}basic{{end}}"

Important notes and limitations

  • Custom claims are added at the top level of the session token payload.
  • Claims with the following keys will be ignored because they are currently added to the JWT by default:
    • sub
    • iat
    • exp
    • aud
    • iss
    • email
    • username
    • session_id
  • Templates must conform to valid Go text/template syntax. Invalid templates are logged and excluded from the generated token.
  • Boolean strings (“true” or “false”) from templates are automatically converted to actual booleans.

Session JWT structure

The session JWT is a compact JWS. It is a string composed of three base64 URL safe encoded parts (header, payload and signature), separated by a dot (.). The JWT payload contains the claims about a user.

JWT Header

{
  "alg": "RS256",
  "kid": "2288bfa9-3214-4f19-9757-92631190420b",
  "typ": "JWT"
}
alg
string
The algorithm used for signing this token.
kid
string
The key ID indicating which key was used to secure the token.
typ
string
The media type of this token.

JWT Payload

{
  "aud": [ "example.com" ],
  "email": {
    "address": "user@example.com",
    "is_primary": true,
    "is_verified": true
  },
  "exp": 17123108000,
  "iat": 1712307200,
  "session_id": "140f3967-ab87-4caa-80bd-603ac59c545f",
  "sub": "9930ac89-1584-488c-bd63-1607f03ab1e8",
  "username": "johndoe"
}
aud
string[]
The audience for which the JWT was created. It specifies the intended recipient or system that should accept this JWT. When using Hanko Cloud, the aud will be your App URL.
email
object
An object containing information about the user’s email address.
exp
integer
The (UNIX) timestamp indicating when the JWT will expire.
iat
integer
The (UNIX) timestamp indicating when the JWT was created.
session_id
string
The ID of the session.
sub
string
The ID of the user.
username
string
The username of the user (if set).
The payload further contains any custom claims that you have defined.