Skip to main content

Session Bridge

Session bridge turns a Keycloak access token into an Endatix Hub session. A mobile app, or any other client that already holds a Keycloak access token, posts that token to Hub. Hub exchanges it with Keycloak and sets the Auth.js session cookie on the response.

Complete Keycloak setup first. The dev realm adds a public client mobile-app whose access tokens set aud to endatix-hub, which is what Keycloak requires of the subject token. Sign in as any realm user (admin@2), for example external-admin@endatix.com.

curl -s -X POST http://127.0.0.1:8080/realms/endatix/protocol/openid-connect/token \
-d 'grant_type=password' \
-d 'client_id=mobile-app' \
-d 'username=external-admin@endatix.com' \
-d 'password=admin@2'

The access_token from that response is the body field for POST /api/auth/session-bridge.

Hub then calls Keycloak as endatix-hub and asks for a refresh token with audience set to endatix-hub. Keycloak 26.8 rejects that request: requested_token_type unsupported for a refresh token, and Requested audience not available: endatix-hub when that audience is sent. An exchange that omits audience and asks for an access token returns id_token and access_token and no refresh_token. Hub still requires refresh_token before it writes the session cookie, so this realm does not make the Hub session-bridge call succeed on Keycloak 26.8. It does issue the subject token the call needs.

When the route is on​

POST /api/auth/session-bridge is allowed when NODE_ENV is not production. In production it returns 403 with detail Session bridge is not allowed, unless FLAG_EXPERIMENTAL_FEATURES=true.

The page /session-bridge is a development form for pasting a token. It uses the same switch and redirects to /signin when the route is off.

What Hub sends to Keycloak​

Hub calls ${AUTH_KEYCLOAK_ISSUER}/protocol/openid-connect/token as the endatix-hub client (AUTH_KEYCLOAK_CLIENT_ID and AUTH_KEYCLOAK_CLIENT_SECRET):

Form fieldValue
grant_typeurn:ietf:params:oauth:grant-type:token-exchange
subject_tokenthe access token from the request
subject_token_typeurn:ietf:params:oauth:token-type:access_token
requested_token_typeurn:ietf:params:oauth:token-type:refresh_token
audienceendatix-hub
scopeopenid email profile

Keycloak must return access_token, refresh_token, id_token, and expires_in. Hub reads sub, email, and name (or preferred_username) from the ID token, writes an Auth.js session cookie, and returns:

{
"success": true,
"user": {
"id": "keycloak-subject",
"name": "Test User",
"email": "external@endatix.com",
"image": null
}
}

The browser that should be signed in must receive that Set-Cookie. A mobile app that calls Hub from its own server does not sign in the user's browser unless it forwards the cookie to that browser.

Keycloak​

The posted access token's aud claim must include endatix-hub. That is the client Hub uses to call the token endpoint. Keycloak rejects the exchange otherwise. See standard token exchange.

One way to set aud is an audience mapper on the client that issues the source token, with included client audience endatix-hub. The dev realm's endatix-hub client maps audience account for Hub login. A source client used only for the mobile token needs its own mapper aimed at endatix-hub.

Example source token:

{
"iss": "http://127.0.0.1:8080/realms/endatix",
"azp": "mobile-app",
"aud": ["endatix-hub"],
"scope": "openid email profile"
}

iss must match AUTH_KEYCLOAK_ISSUER. Use 127.0.0.1 when Keycloak is the local compose file.

Lightweight access tokens are not required for this call. Use them when you also follow Keycloak RBAC.

Call the endpoint​

curl -i -X POST http://localhost:3000/api/auth/session-bridge \
-H 'Content-Type: application/json' \
-d '{"access_token":"<keycloak-access-token>"}'

The JSON field is access_token. A body that uses token returns 400 with detail Missing access_token. Please provide a valid access token.

Failures​

Responses are problem details (type, title, detail, status).

  • 403 Session bridge is not allowed. Production without FLAG_EXPERIMENTAL_FEATURES=true.
  • 400 Missing access_token. Please provide a valid access token. The body failed the request schema.
  • 500 title Session bridge server error, detail Missing required auth provider. Please contact support. KeycloakAuthProvider is not registered. See Keycloak setup.
  • 400 detail Token exchange failed, errorCode validation_error. Keycloak rejected the grant (HTTP 400 from the token endpoint). Check that aud includes endatix-hub, the client secret matches, and standard token exchange is enabled. Keycloak's error_description is logged with the failure and is not copied into detail.
  • 500 detail Network error. Failed to connect to the Keycloak token exchange endpoint. Hub could not reach ${AUTH_KEYCLOAK_ISSUER}/protocol/openid-connect/token.
  • 400 errorCode EXCHANGED_TOKEN_INVALID, detail Session bridge token exchange failed. Insufficient information to establish a session. The exchange response was missing a field the session needs (id_token, refresh_token, expires_in, or a subject). fields names the bad properties.
  • 400 errorCode MISSING_ID_TOKEN when the exchange response has no ID token.

Next steps​