Conversation
- Expose TEMPORAL_MAX_SESSION_DURATION in docker.yaml (default 2m)
- SetUser now accepts sessionExpiresAt and caps user* cookie MaxAge
to min(60s, remaining), preventing cookies from outliving the session
- authenticateCb passes session expiry at login time
- refreshTokens derives remaining session time from session_start cookie
and passes it to SetUser, so the final refresh cycle issues cookies
that expire exactly when the session does
Signed-off-by: ralf157 <ralf.dahmen14@googlemail.com>
The refresh cookie MaxAge was incorrectly set from oauth2.Token.Expiry, which per RFC 6749 §5.1 and OIDC Core §3.2.2.5 represents the access token lifetime — not the refresh token's. This caused the refresh cookie to expire alongside the access token, making token refresh fail with 401. Priority chain for refresh cookie MaxAge: 1. JWT exp claim decoded from the refresh token payload (no sig verify) 2. refreshTokenDuration configured per provider (covers opaque tokens) 3. 7-day default when neither is available Adds RefreshTokenDuration to AuthProvider config, exposed as TEMPORAL_AUTH_REFRESH_TOKEN_DURATION in docker.yaml. Signed-off-by: ralf157 <ralf.dahmen14@googlemail.com>
…n test Merged TestSetUserRefreshCookieJWTExp into TestSetUserRefreshCookieMaxAge, removing the duplicate extractCookieMaxAge helper and renaming makeJWT to jwtToken. All opaque and JWT token cases now live in one place with a unified struct that carries refreshToken and refreshTokenDuration per case. Signed-off-by: ralf157 <ralf.dahmen14@googlemail.com>
…TION.md Added refreshTokenDuration to the provider settings reference, a new Docker environment variables table covering all TEMPORAL_AUTH_* fields, and a troubleshooting note explaining the opaque vs JWT refresh token distinction with a config example. Signed-off-by: ralf157 <ralf.dahmen14@googlemail.com>
Mock OIDC (9 tests) and real Keycloak (5 tests) stacks covering user*/refresh/session_start MaxAge, HttpOnly flags, JWT exp priority, token refresh, and session expiry. All 14 tests passing. Signed-off-by: ralf157 <ralf.dahmen14@googlemail.com>
Signed-off-by: ralf157 <ralf.dahmen14@googlemail.com>
|
@ralf157 is attempting to deploy a commit to the Temporal Team on Vercel. A member of the Team first needs to authorize it. |
|
Any update on this? Would be great if this bug could be fixed / bugfix merged, as it forces us to relogin to the UI every hour when used together with Entra ID (access token lifetime ~1hour, refresh token several days) |
|
Any update ? |
|
I've been struggling with this lately as well and I'm eagerly awaiting the release. |
|
Thanks for this — the diagnosis is right on both counts, and I confirmed both bugs are still present on Since this was opened from a fork's Two changes worth flagging:
The one thing I can't reproduce is your live Keycloak run — if you're able to check #3940 against your Keycloak 26 instance, that would be much appreciated. |
Description & motivation 💭
Fixes two bugs in auth session and cookie handling:
1.
maxSessionDurationnot configurable in Docker deployments (#3223)The field existed in the config struct but was missing from
docker.yaml, making it impossible to enforce session expiry via environment variable. AddsTEMPORAL_MAX_SESSION_DURATION(default2m) and alignsuser*cookieMaxAgetomin(60s, remaining session time)so cookies never outlive the session boundary.2. Refresh cookie lifetime incorrectly derived from access token expiry (#3210)
oauth2.Token.Expiryis populated fromexpires_inin the token response, which per RFC 6749 5.1 and OIDC Core 3.2.2.5 represents the access token lifetime — not the refresh token's. This caused the refresh cookie to expire alongside the access token, making token refresh fail with 401 (confirmed on Keycloak).The refresh cookie
MaxAgeis now derived via a priority chain:expclaim decoded from the refresh token payload — covers Keycloak and other JWT-issuing IdPs automaticallyrefreshTokenDurationconfigured per provider (TEMPORAL_AUTH_REFRESH_TOKEN_DURATION) — covers opaque tokensDesign Considerations 🎨
refreshTokenDurationis placed onAuthProvider(notAuth) since refresh token lifetime is an IdP-specific characteristicexpis decoded without signature verification — only used for cookie lifetime, not for trust decisionsTesting 🧪
How was this tested 👻
E2E test suites
Mock OIDC stack (
tests/e2e-auth/,docker-compose.e2e-auth.yaml) — short TTLs: access=5s, refresh=30s, session=15s. Covers code paths unique to the mock (opaque token fallback, session timing):refresh-token-duration.spec.tsexptakes priority overrefreshTokenDurationconfig valuesession-expiry.spec.tsuser*MaxAge decreases after refresh; refresh fails at 16s (session expired)Real Keycloak stack (
tests/e2e-keycloak/,docker-compose.e2e-keycloak.yaml) — Keycloak 26 with realm import, access=5s, ssoSession=30s, maxSession=25s:login-cookies.spec.tsrefreshMaxAge from Keycloak JWTexp(~30s, no config fallback);user*MaxAge ≤ 25s;session_startMaxAge = 25s; HttpOnly flagsRun locally:
Session expiry:
TEMPORAL_MAX_SESSION_DURATION=30sin your Docker environmentRefresh token lifetime (Keycloak or JWT-issuing IdP):
refreshTokenDurationsetrefreshcookie MaxAge in browser devtoolsexpclaim, not the access token lifetimeRefresh token lifetime (opaque token IdP):
TEMPORAL_AUTH_REFRESH_TOKEN_DURATION=24hfor your providerrefreshcookie MaxAge is 86400 secondsChecklists
Draft Checklist
maxSessionDurationexposed indocker.yamlrefreshTokenDurationadded toAuthProviderconfigjwtExphelper implemented and unit testeddocker-compose.e2e-keycloak.yaml)AUTHENTICATION.mdupdatedMerge Checklist
Issue(s) closed
Closes #3223
Closes #3210
Docs
AUTHENTICATION.mdupdated to documentTEMPORAL_MAX_SESSION_DURATIONandTEMPORAL_AUTH_REFRESH_TOKEN_DURATIONenvironment variables, therefreshTokenDurationprovider config field, and the refresh token lifetime priority chain including a troubleshooting section for opaque vs JWT refresh tokens.