Skip to content

feat: report the token endpoint response so apps can learn their WebID - #26

Open
PreciousOritsedere wants to merge 1 commit into
solid-contrib:feat/dpop-session-cachefrom
PreciousOritsedere:feat/expose-token-response
Open

feat: report the token endpoint response so apps can learn their WebID#26
PreciousOritsedere wants to merge 1 commit into
solid-contrib:feat/dpop-session-cachefrom
PreciousOritsedere:feat/expose-token-response

Conversation

@PreciousOritsedere

Copy link
Copy Markdown

Why

An app that signs the user in by picking an Authorization Server never learns who it signed in as. Solid OIDC requires the ID Token to carry a webid claim, but DPoPTokenProvider read the access token out of the token endpoint response and dropped the rest, so the claim was validated and then thrown away.

The concrete case is a Next.js Solid file manager: the user picks their Pod provider, the first authenticated request establishes the session — and the app then has to ask the user for the WebID it was already told, just to know whose storage to list.

What changed

  • IssuerSession keeps the processed token endpoint response it was established from.
  • DPoPTokenProvider.tokenEndpointResponse(issuer: URL): Promise<TokenEndpointResponse | undefined> reports it. It never starts a flow, and reports nothing until one for that issuer has completed, so it is safe to call speculatively — the natural call is right after the first authenticated request.
  • New src/webIdFrom.ts: webIdFrom(response: TokenEndpointResponse): string | undefined, exported from mod.ts.
  • A short README section showing the two together.
const tokens = await provider.tokenEndpointResponse(issuer)
const webId = tokens === undefined ? undefined : webIdFrom(tokens)

Why the WebID logic is a util, not a provider method

DPoPTokenProvider is an OAuth client: it obtains tokens and binds them to requests. Nothing about the authorization code flow, the session cache or the DPoP proofs changes because a Solid deployment happens to assert a WebID, and putting a webId(issuer) accessor on the provider would have made every one of those concerns import a Solid profile concept it does not otherwise touch.

Reporting the response instead keeps that split honest. The provider hands back exactly what the Authorization Server said; webIdFrom is one small pure function that interprets one claim, and a caller who needs sub, azp or an ESS-specific claim tomorrow can reach them without another provider method being added for each. It also matches the existing issuerFrom — one exported function per file, named after what it produces — so I put it at src/webIdFrom.ts rather than inventing a utils module the repo does not have.

Why the response and not the claims

webIdFrom takes the token endpoint response rather than an IDToken, because that is the only object oauth4webapi will give claims for: getValidatedIdTokenClaims refuses any reference it did not process itself, which is what makes the claim trustworthy. Taking claims instead would have forced the provider to unwrap them — and then the provider is back in the business of knowing what an ID Token is. The response is also the honest unit of "what this session rests on": it may carry no ID Token at all (a refreshed or client-credentials session), and webIdFrom simply reports nothing in that case.

One caveat worth stating: the reported response contains the session's tokens, so it is a secret. That is documented on the method.

Base

Branched off feat/dpop-session-cache (#11), because the response belongs to the per-issuer session that PR introduces — on main there is no session to hang it off. Happy to rebase onto main once #11 lands, or onto a different branch in the stack if you would rather it sat elsewhere.

Tests

npm test — 13 passed (2 files), and npx tsc is clean.

Four cases cover the accessor (reports the response the session rests on, reports nothing before a flow without starting one, follows the session across re-authentication, swallows a failing flow rather than surfacing the failure) and three cover webIdFrom (the asserted WebID, an ID Token without the claim, a response without an ID Token). The fake Authorization Server gained a webId option so the claim can be present or absent.

The red CI on this branch is pre-existing and unrelated: npm i fails to resolve typedoc's peer range against typescript 7 on main as well. Everything above was run with npm i --legacy-peer-deps.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AaiG7eXEMjoTgCjNiQgfRH

Solid OIDC requires the ID Token to carry a webid claim, but the token
endpoint response was discarded once its access token had been read. An
app that signs in by picking an Authorization Server therefore had no way
to learn who it signed in as, and had to ask the user for a WebID it
could have been told.

DPoPTokenProvider now keeps the processed token endpoint response on the
issuer's session and reports it from tokenEndpointResponse(issuer), which
never starts a flow of its own and reports nothing until one has
completed.

Reading the WebID out of that response is a separate exported function,
webIdFrom, so the provider stays concerned with tokens alone and callers
can reach the other claims the ID Token was validated for.
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.

1 participant