Skip to content

OIDC token endpoint rejects public clients with invalid_client (regression from #452) #513

@melvincarvalho

Description

@melvincarvalho

Summary

xlogin (and any Solid app using dynamic client registration with token_endpoint_auth_method: "none") can complete the authorization code flow against JSS but cannot exchange the code for tokens — the token endpoint returns 401 invalid_client. This is blocking login across the solid-apps suite (explorer, plaza, timeline, hub-mashlib all hit it).

Reproduced against JSS at HEAD (commit fdc219f, package.json says 0.0.201).

Repro

jss start \
  --port 5446 \
  --host localhost \
  --root /tmp/jss-test/pod-data \
  --single-user \
  --single-user-password test \
  --idp \
  --notifications \
  --mashlib-module <any solid app entry>

Visit http://localhost:5446/public/ in a browser, click any Solid app's Login button (e.g. xlogin's), enter username/password.

  1. POST /idp/interaction/<id>/login → 302 (login succeeds)
  2. Browser redirected back to the app with ?code=...
  3. xlogin posts to /idp/token with the code → 401 invalid_client

Log excerpt

err: invalid_client
stack: InvalidClientAuth: invalid_client
    at authenticateClient (oidc-provider/lib/shared/client_auth.js:166:17)
    at async setWWWAuthenticateHeader (oidc-provider/lib/shared/client_auth.js:49:11)
    at async selectiveBody (oidc-provider/lib/shared/selective_body.js:49:5)
error_description: "client authentication failed"
msg: "oidc-provider grant error"

POST /idp/token statusCode=401 contentLength=77 responseTime=1.74ms

Body length 77 bytes is consistent with the standard public-client payload (grant_type=authorization_code&code=...&redirect_uri=...&client_id=...&code_verifier=...) — no client_secret, which is correct for a none-auth client.

Why this matters

This isn't an xlogin-specific problem. It's the OIDC token endpoint refusing every public client. Public clients are the canonical Solid flow: browser-based apps that dynamically register and authenticate per-session via PKCE alone. If JSS's token endpoint can't honour that, no in-browser Solid app can log in — including:

  • solid-apps/explorer (logging in to read private containers)
  • solid-apps/plaza (logging in to post)
  • solid-apps/timeline (logging in to post)
  • solid-apps/hub (via the new hub-mashlib.js data-browser bundle, just spiked)
  • Any third-party Solid client using @inrupt/solid-client-authn-browser or similar

Suspected origin

PR #452 (merged 2026-05-14, included in JSS 0.0.194+) reworked stale-session-cookie + consent-crash recovery. It touched src/idp/provider.js and src/idp/interactions.js. The token endpoint went from working (≤ 0.0.193) to silently rejecting public clients sometime after that merge.

Relevant code paths:

  • src/idp/provider.js:341token_endpoint_auth_method: 'none' is in clientDefaults, so dynamically-registered clients SHOULD inherit none. They're being rejected anyway, so either the default isn't taking effect or oidc-provider's client-auth check is over-strict for the none path.
  • src/idp/provider.js:379 — there's a CORS branch on client.tokenEndpointAuthMethod === 'none' that exists, suggesting we knew about this case at some point.

Suggested investigation

  1. Inspect the dynamically-registered client record in the IdP adapter — confirm token_endpoint_auth_method is being persisted as none after POST /idp/reg.
  2. Trace authenticateClient in oidc-provider's client_auth.js:166 — what client metadata is it seeing for these requests? Is it expecting client_secret_basic or client_secret_post when it should accept the none path?
  3. Diff provider.js against pre-OIDC server_error after account delete + re-create (stale session) #452 — anything in the client-auth or grant-type pipeline that would affect public-client acceptance?
  4. Consider whether expireSessionCookiesKoa or the stale-session redirect is corrupting state in a way that makes the subsequent /idp/token request appear to be on a different client.

Workaround

Pin JSS to 0.0.193 in package.json (and npm install) to restore login. This is what jspod / nosdav-server consumers may need to do until the fix lands.

Related

  • The solid-apps suite of in-browser Solid clients all share this dependency. Fixing the token endpoint unblocks all of them at once.
  • Earlier conversation in the issue tracker about the same observation (search: invalid_client, regression).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions