feat: report the token endpoint response so apps can learn their WebID - #26
Open
PreciousOritsedere wants to merge 1 commit into
Conversation
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.
PreciousOritsedere
requested review from
jeswr,
langsamu and
matthieubosquet
as code owners
August 6, 2026 09:08
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.
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
webidclaim, butDPoPTokenProviderread 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
IssuerSessionkeeps 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.src/webIdFrom.ts:webIdFrom(response: TokenEndpointResponse): string | undefined, exported frommod.ts.Why the WebID logic is a util, not a provider method
DPoPTokenProvideris 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 awebId(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;
webIdFromis one small pure function that interprets one claim, and a caller who needssub,azpor an ESS-specific claim tomorrow can reach them without another provider method being added for each. It also matches the existingissuerFrom— one exported function per file, named after what it produces — so I put it atsrc/webIdFrom.tsrather than inventing autilsmodule the repo does not have.Why the response and not the claims
webIdFromtakes the token endpoint response rather than anIDToken, because that is the only object oauth4webapi will give claims for:getValidatedIdTokenClaimsrefuses 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), andwebIdFromsimply 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 — onmainthere is no session to hang it off. Happy to rebase ontomainonce #11 lands, or onto a different branch in the stack if you would rather it sat elsewhere.Tests
npm test— 13 passed (2 files), andnpx tscis 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 awebIdoption so the claim can be present or absent.The red CI on this branch is pre-existing and unrelated:
npm ifails to resolve typedoc's peer range against typescript 7 onmainas well. Everything above was run withnpm i --legacy-peer-deps.🤖 Generated with Claude Code
https://claude.ai/code/session_01AaiG7eXEMjoTgCjNiQgfRH