fix(auth): align cookie lifetimes with session and refresh token boundaries#3235
Open
ralf157 wants to merge 7 commits intotemporalio:mainfrom
Open
fix(auth): align cookie lifetimes with session and refresh token boundaries#3235ralf157 wants to merge 7 commits intotemporalio:mainfrom
ralf157 wants to merge 7 commits intotemporalio:mainfrom
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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.