Skip to content

fix: apply renewed access token in embed mode with delegated authentication - #12856

Open
Eflyax wants to merge 1 commit into
owncloud:masterfrom
Eflyax:fix/web-embed-delegated-token-renewal
Open

fix: apply renewed access token in embed mode with delegated authentication#12856
Eflyax wants to merge 1 commit into
owncloud:masterfrom
Eflyax:fix/web-embed-delegated-token-renewal

Conversation

@Eflyax

@Eflyax Eflyax commented Aug 27, 2026

Copy link
Copy Markdown

Description

Fixes token renewal in Web embed mode with delegated authentication. The owncloud-embed:update-token message listener in AuthService had two defects that made renewal impossible:

  1. It was registered as an unbound method reference (window.addEventListener('message', this.handleDelegatedTokenUpdate)), so this was not the AuthService instance and the handler threw TypeError on its first line for every incoming message. The method is now an arrow function class property, which keeps this bound and preserves a stable reference for the listener.
  2. It passed the whole message payload (event.data, i.e. { name, data: { access_token } }) to updateContext(accessToken: string, …). It now extracts event.data.data?.access_token — consistent with the initial token handover in oidcCallback.vue — and ignores messages without a token.

Adds two unit tests: a renewed token from the configured origin is applied via updateContext, and messages from unexpected origins are ignored. Both tests fail against the previous implementation.

Related Issue

Motivation and Context

Token renewal in embed mode with delegated authentication has never worked (the defects exist since the feature was introduced in owncloud/web v8.0.0). Host applications that deliver renewed tokens per the documented contract still lose the embedded session as soon as the initial access token expires, which makes the embed mode unusable for sessions longer than one access token lifetime.

How Has This Been Tested?

  • test environment: oCIS 8.2.0 / Web 12.5.0 deployment embedded in a host application, Keycloak as IdP with a short access token lifespan to force frequent renewals
  • test case 1: without the fix — every owncloud-embed:update-token message logs Uncaught TypeError: can't access property "options", this.configStore is undefined, requests keep the expired token, user is logged out on token expiry
  • test case 2: with the fixed handler — renewed tokens are applied, requests carry the fresh token and the embedded session survives token rotations indefinitely
  • test case 3: pnpm vitest run authService.spec — 17 tests pass; the two new tests fail against the unfixed implementation

Screenshots (if appropriate):

Open tasks:

@Eflyax
Eflyax requested a review from a team as a code owner August 27, 2026 11:52
@Eflyax
Eflyax force-pushed the fix/web-embed-delegated-token-renewal branch from 029fad0 to f41b6ca Compare August 27, 2026 11:52
@kw-security

kw-security commented Aug 27, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

…cation

The "owncloud-embed:update-token" message listener was registered unbound,
so it crashed on `this.configStore` before applying the renewed token. It
also passed the whole message payload to updateContext instead of the
access token string. The embedded instance therefore kept the initial
access token until it expired and the user got logged out.
@Eflyax
Eflyax force-pushed the fix/web-embed-delegated-token-renewal branch from f41b6ca to 39346b8 Compare August 27, 2026 11:56

@mzner mzner left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified locally — the fix is correct and both defects reproduce independently:

  • master source + this PR's tests only → TypeError: ... reading 'options' at authService.ts:409
  • binding fix only → updateContext receives {name, data:{access_token}} instead of the token string
  • full PR → 17/17 pass

The arrow property is the right choice over an inline .bind(this): one stable reference per instance, so the addEventListener de-dup at line 292 still holds.

Requesting changes for test hygiene only — the source changes LGTM.

One point I can't attach inline because the line is unchanged context outside the diff: in this PR's version of authService.spec.ts, line 289 asserts only expect(mockUpdateContext).toHaveBeenCalled(). That weak assertion is what let the payload defect go unnoticed since v8.0.0 — it confirmed that something was passed, never what. Tightening it to toHaveBeenCalledWith(...) belongs in this PR.

Note: only the Snyk checks have run; the web unit-test pipeline hasn't been triggered, so the new tests aren't verified in CI yet.

)
})

describe('handleDelegatedTokenUpdate', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suite already has a describe('handleDelegatedTokenUpdate') block further down (line 224 in this file). Please merge the new tests into the existing block instead of adding a second one with the same name.

)
})

it('ignores "owncloud-embed:update-token" messages from unexpected origins', async () => {

@mzner mzner Aug 27, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: the two pre-existing origin-rejection tests in this same file (now at lines 231 and 248) already cover "wrong origin → no token update", so this makes a third. Not a blocker if you prefer to keep it — it does combine dispatchEvent with a wrong origin.

The test below it is the one that carries the real value: it goes through window.dispatchEvent, which is the path the browser actually uses and where this was being lost. The older tests invoke the handler directly as a method ((authService as any).handleDelegatedTokenUpdate(event), line 287), so this is bound correctly there and the defect could never surface — which is why they passed for two years while the feature was broken.

}

const accessToken = event.data.data?.access_token
if (!accessToken) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new guard isn't covered by a test. Worth adding a case where an owncloud-embed:update-token message arrives without an access_token and asserting updateContext is not called.

@mzner mzner left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Eflyax Thanks for tracking this down and for the thorough write-up in the issue. The root-cause analysis made this quick to verify. I reproduced both defects locally and confirmed the fix works.

The source changes look good. I've left a few notes on the tests only, nothing structural.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Web embed mode: renewed access token from "owncloud-embed:update-token" is never applied (delegated authentication)

3 participants