-
Notifications
You must be signed in to change notification settings - Fork 143
ADR for OIDC improvement to use TTL from oidc token #4655
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| # ADR: OIDC Token TTL Improvements | ||
|
|
||
| ## Status | ||
|
|
||
| **Proposed** | ||
|
|
||
| ## Context | ||
|
|
||
| In multi-application deployments where Twake Workplace serves applications (like Twake Mail) in iframes, | ||
| there is a fundamental disconnect between cozy-stack session management and OIDC token validity. | ||
|
|
||
| #### Real-World Scenario: pommes.fr Deployment | ||
|
|
||
| ``` | ||
| Domain Structure: | ||
| ├── auth.pommes.fr ← OIDC Provider | ||
| └── *.workplace.pommes.fr ← Twake Workplace apps | ||
| ├── home.workplace.pommes.fr | ||
| └── Twake Mail.workplace.pommes.fr (iframe) | ||
| ``` | ||
|
|
||
| **Problem Flow:** | ||
|
|
||
| ```mermaid | ||
| sequenceDiagram | ||
| participant User | ||
| participant Cozy as Cozy Home | ||
| participant Twake Mail as Twake Mail (iframe) | ||
| participant Stack as Cozy Stack | ||
| participant OIDC | ||
|
|
||
| User->>Cozy: Login via OIDC | ||
| Cozy->>OIDC: Authenticate | ||
| OIDC-->>Cozy: Tokens (TTL: 1 hour) | ||
| Cozy->>Stack: Create session | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Create session is not done by Cozy Home. Cozy Stack will generate a token and pass this token to cozy home
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, I've mixed app with mobile app flow |
||
| Stack-->>Cozy: Session (TTL: 30 days) | ||
|
|
||
| Note over User,OIDC: Time passes... OIDC token expires | ||
|
|
||
| User->>Twake Mail: Open Twake Mail in iframe | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should be User->>Twake Mail Iframe ->> Twake Mail
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if Twake Mail Iframe is a cozy app, yes |
||
| Twake Mail->>Stack: API request | ||
| Stack-->>Twake Mail: 401 Unauthorized | ||
| Twake Mail->>OIDC: Redirect to login | ||
| OIDC--xTwake Mail: ❌ Blocked! CSP doesn't allow iframe | ||
|
|
||
| Note over Twake Mail: User stuck - can't authenticate | ||
| ``` | ||
|
|
||
| #### Root Cause | ||
|
|
||
| Cozy-stack sessions (30 days) are completely independent of OIDC token validity (typically 1 day) | ||
|
|
||
| #### Impact | ||
|
|
||
| - Users appear logged into Cozy but embedded apps fail silently | ||
| - OIDC provider login page cannot load in iframe (CSP `frame-ancestors` restriction) | ||
| - Poor user experience with no clear path to resolution | ||
|
|
||
|
|
||
| ### Current Implementation | ||
|
|
||
| The cozy-stack currently supports OpenID Connect (OIDC) for delegated authentication. When a user authenticates via OIDC, the stack: | ||
|
|
||
| 1. Exchanges the authorization code for tokens (`access_token`, `id_token`, optionally `refresh_token`) | ||
| 2. Uses the `access_token` once to call the UserInfo endpoint | ||
| 3. Extracts the `sid` (session ID) from the `id_token` for logout support | ||
| 4. Discards all OIDC tokens immediately after use | ||
| 5. Issues its own cozy-stack OAuth tokens to the client | ||
|
|
||
| The current token response parsing only captures two fields: | ||
|
|
||
| ```go | ||
| type tokenResponse struct { | ||
| AccessToken string `json:"access_token"` | ||
| IDToken string `json:"id_token"` | ||
| } | ||
| ``` | ||
|
|
||
| ### Current Limitations | ||
|
|
||
| | Limitation | Impact | | ||
| |------------|--------| | ||
| | OIDC `refresh_token` not captured | Cannot refresh OIDC access token when it expires | | ||
| | OIDC `expires_in` not captured | No awareness of token TTL | | ||
| | OIDC `access_token` not stored | Cannot make subsequent calls to OIDC-protected APIs on behalf of user | | ||
| | Session TTL independent of OIDC | Sessions valid long after OIDC token expires | | ||
| | No lifecycle validation | Token validity not checked after initial auth | | ||
| | Back-channel logout deletes ALL sessions | Should use `sid` for per-device logout | | ||
|
|
||
|
|
||
| ## Proposal | ||
|
|
||
| ### Extend Token Response Parsing | ||
|
|
||
| Update the `tokenResponse` struct to capture all relevant fields: | ||
|
|
||
| ```go | ||
| type tokenResponse struct { | ||
| AccessToken string `json:"access_token"` | ||
| IDToken string `json:"id_token"` | ||
| RefreshToken string `json:"refresh_token,omitempty"` | ||
| ExpiresIn int64 `json:"expires_in,omitempty"` | ||
| TokenType string `json:"token_type,omitempty"` | ||
| } | ||
| ``` | ||
|
|
||
| #### Store OIDC Tokens on OAuth Client | ||
|
|
||
| Extend the `oauth.Client` struct to store OIDC tokens: | ||
|
|
||
| ```go | ||
| type Client struct { | ||
| // ... existing fields ... | ||
|
|
||
| // OIDC token storage | ||
| OIDCSessionID string `json:"oidc_session_id,omitempty"` // Existing | ||
| OIDCAccessToken string `json:"oidc_access_token,omitempty"` // New | ||
| OIDCRefreshToken string `json:"oidc_refresh_token,omitempty"` // New | ||
| OIDCTokenExpiresAt *time.Time `json:"oidc_token_expires_at,omitempty"` // New | ||
| } | ||
| ``` | ||
|
|
||
| #### Store OIDC Token Expiry on Session | ||
|
|
||
| Extend the `session.Session` struct: | ||
|
|
||
| ```go | ||
| type Session struct { | ||
| // ... existing fields ... | ||
| SID string `json:"sid,omitempty"` // Existing | ||
| OIDCTokenExpiresAt *time.Time `json:"oidc_token_expires_at,omitempty"` // New | ||
| } | ||
| ``` | ||
|
|
||
| ### Session TTL Alignment | ||
|
|
||
| #### Session Validity Middleware | ||
|
|
||
| Add HTTP middleware that checks OIDC token expiry on each request: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. based on the
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or based on introspect?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yep, base on expires_in |
||
| if the token is expired, return a 401 Unauthorized response with an `oidc_token_expired` error indicating re-authentication is required. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This http middleware will only work for "cozy apps" not for "iframed ones". But let's focus on theses cozy-apps first I don't think cozy-stack should return a 401 Unauthorized. Because a cozy application will never know what to do with Why the stack do not refresh the We'll still have issue if the Then in this case, maybe cozy-stack should redirect to login page directly? Now let's talk about "iframed" app. I'm on an iframed app for a long time without any activities. I do not refresh my browser. So I've my cozy bar authenticated because I didn't do any http request throught the stack. I start navigating in my iframed app, and this iframed app is not able to refresh its own token, then this iframed app will redirect me to the sso. And we'll be in the use case we want to fix: not displaying the sso page in the iframe. Maybe we can do some magic from cozy-bridge (cc @zatteo) to detect the redirection to the SSO and do stuff at the cozy-app level (redirect to the SSO on top window). But maybe also, the cozy front end application can be aware of the expired time? Based on its own token with an expired_at attribute? In this case, cozy-bar can be able to make an http request and then run into this http middleware and so on?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because we don't have a problem when the token expires in the stack, we have problem when the token expires in twake male, and twake male doesn't refresh the session. Yes, cozy stack I think even should do this, but it's just more changes to OIDC flow. But we don't know at what moment we should redirect to OIDC flow. we can't send 322 loging redirect to every post instead of 401. But the bigget problem is that you've mentioned, and it's not solved with this ADR
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We won't be able to monitor from parent window URL change from twake mail if it redirects to SSO because we are not same origin so no access to url updates from iframe. But otherwise yes we can do stuff.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That's not totally true. TMail refresh the session, but sometimes your refresh token is expired. So you just can't refresh. I still think this iframe stuff is not the right thing to do. We should let the app (stack / tmail / tchat) identify itself, and then inject the cozy-bar. Like that, no more issue like this one.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Crash-- it's not 100% solution, since Twake Mail app has it's own auth oidc flow, but we can also can listen an event form stack in the bridge app when session is expired. We should be carefull at this step, to have the same oidc refresh flow and timeouts for both app(all settings shoudl be the same or mo restrictive on the stack side)
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we can do something:
I still think that, regarding this embedded/embedder approach, we might be going in the wrong direction.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But let's fix at least the cozy's app issue. This ADR should fix that. Can you update it to target this objective please?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you mean by "cozy's app issue"?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It'll fix these scenarios :
|
||
|
|
||
| ### Back-Channel Logout Fix | ||
|
|
||
| Per-Device Logout Using SID | ||
|
|
||
| ### Extended Configuration Options | ||
|
|
||
| ```yaml | ||
| authentication: | ||
| the-context-name: | ||
| oidc: | ||
| # ... existing config ... | ||
|
|
||
| # Token storage (Part A) | ||
| store_tokens: true # Enable OIDC token storage (default: false) | ||
|
|
||
| # Session alignment (Part C) | ||
| align_session_ttl: true # Tie session validity to OIDC token (default: false) | ||
| ``` | ||
|
|
||
| ## Alternatives | ||
|
|
||
| ### Store Tokens in Session Instead of OAuth Client | ||
|
|
||
| Store OIDC tokens in the `session.Session` document instead of `oauth.Client`. | ||
|
|
||
|
|
||
| ### Encrypt Stored Tokens | ||
|
|
||
| Encrypt OIDC tokens before storing in CouchDB. | ||
|
|
||
|
|
||
| ## Decision | ||
|
|
||
| ## Consequences | ||
|
|
||
| Desktop/Mobile apps will have token with the same short TTL | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # Architecture Decision Records (ADR) | ||
|
|
||
| This directory contains Architecture Decision Records (ADRs) for the cozy-stack project. | ||
|
|
||
| ## What is an ADR? | ||
|
|
||
| An Architecture Decision Record (ADR) is a document that captures an important architectural decision made along with its context and consequences. | ||
|
|
||
| ## ADR Template | ||
|
|
||
| Each ADR should include: | ||
|
|
||
| - **Status**: Proposed, Accepted, Deprecated, or Superseded | ||
| - **Context**: The situation and problem we are facing | ||
| - **Proposal**: The proposed solution | ||
| - **Alternatives**: Other options considered | ||
| - **Decision**: The final decision and rationale | ||
| - **Consequences**: The resulting effects (positive and negative) | ||
|
|
||
| ## Index | ||
|
|
||
| | ADR | Title | Status | | ||
| |-----|-------|--------| | ||
| | [004](004-oidc-token_ttl_improvements.md) | OIDC Token Management Improvements | Proposed | | ||
|
|
||
| ## Creating a New ADR | ||
|
|
||
| 1. Copy the template or an existing ADR | ||
| 2. Use the next sequential number: `NNNN-short-title.md` | ||
| 3. Fill in all sections | ||
| 4. Update this README index | ||
| 5. Submit for review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure that the current flow is this one?
Because Cozy Home will never communicate with the OIDC. It should be cozy-stack
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, it's a super-simplified one just to illustrate the problem. It doesn't interracts with OIDC except redirect to sign-up to do the auth