Skip to content

fix(token): allow public clients that present a spurious credential - #411

Open
0x5t4l1n wants to merge 2 commits into
cloudflare:mainfrom
0x5t4l1n:fix/public-client-spurious-secret-compat
Open

0x5t4l1n wants to merge 2 commits into
cloudflare:mainfrom
0x5t4l1n:fix/public-client-spurious-secret-compat

Conversation

@0x5t4l1n

@0x5t4l1n 0x5t4l1n commented Oct 11, 2026 •

Copy link
Copy Markdown

Summary

Restores backward compatibility for public clients (token_endpoint_auth_method: "none") that send a client_secret or Basic Authorization header at /token. Since 1.x (#276, #287), these requests are rejected with 401 invalid_client (token_endpoint_auth_method_mismatch), breaking integrations that unconditionally include credentials.

Empty client_secret= is already handled (filtered as omitted per RFC 6749 §3.2). This PR extends backward compatibility to non-empty credentials on public clients without explicit auth-method provenance.

Changes

src/oauth-provider.ts:

  • isClientAuthMethodAllowed: add backward compat case for none clients receiving a secret method when authMethodExplicit is absent
  • Normalize presentedAuthMethod to 'none' after the compat check so downstream secret validation is skipped (the server never issued a secret)

Security rationale

Ignoring a credential the server never issued does not weaken public-client security, which rests on PKCE and refresh rotation (RFC 6749 §2.1). This matches 0.8.x behavior.

Test plan

  • Public client with client_secret=value in POST body → accepted as none
  • Public client with Basic auth header → accepted as none
  • Public client with empty client_secret= → accepted (existing behavior)
  • Confidential client auth method validation unchanged
  • Public client with explicit authMethodExplicit set is NOT compat-allowed (only legacy records)

Fixes #404


Devin Review

Public clients (token_endpoint_auth_method: 'none') without explicit
auth-method provenance are now allowed when the request carries a
non-empty client_secret or Basic header. The presented credential is
ignored — the server never issued one, so skipping validation does not
weaken security (PKCE and refresh rotation protect public clients).

This restores 0.8.x behavior for clients whose integration platforms
send credentials unconditionally, and prevents ~600 existing grants
from breaking on upgrade.

Fixes cloudflare#404
@changeset-bot

changeset-bot Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 024e784

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@devin-ai-integration devin-ai-integration Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Note

Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.

Devin Review found 4 potential issues.

Devin Review

Comment thread src/oauth-provider.ts

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Bug fix has no changeset

Repository rules require a changeset for bug fixes. This PR changes only the provider implementation, leaving no release entry.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread src/oauth-provider.ts Outdated
Comment thread src/oauth-provider.ts
Comment on lines +1232 to +1234
if (client.tokenEndpointAuthMethod === 'none' && isSecretMethod(presentedMethod)) {
return true;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Public-client tests still expect rejection

Existing tests expect 401 invalid_client for unmarked public clients presenting a non-empty secret. The new behavior accepts those requests, so the suite and compatibility coverage need updating.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread src/oauth-provider.ts
Comment on lines +1229 to +1233
// Allow public clients that receive a spurious credential. Ignoring a
// secret the server never issued does not weaken public-client security,
// which rests on PKCE and refresh rotation (RFC 6749 §2.1).
if (client.tokenEndpointAuthMethod === 'none' && isSecretMethod(presentedMethod)) {
return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Stored-client documentation contradicts new behavior

The authMethodExplicit storage note says public clients never receive compatibility. It now needs to distinguish unmarked public clients from explicit registrations.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

The normalization that overrides presentedAuthMethod to 'none' for
backward-compat public clients was also firing for CIMD clients whose
tokenEndpointAuthMethod is 'none' and authMethodExplicit is absent.
This caused valid private_key_jwt assertions from CIMD clients to skip
verifyClientAssertion, silently accepting unauthenticated requests.

- Extract isCimdClient bool from the existing inline expression so it
  can be reused without duplication.
- Move the presentedAuthMethod normalization to AFTER the
  isClientAuthMethodAllowed check (it was previously before, meaning
  it ran even for clients that would be rejected).
- Gate normalization on !isCimdClient so CIMD assertion flows are
  never affected.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

Devin Review

Comment thread src/oauth-provider.ts
Comment on lines +3001 to +3004
!isCimdClient &&
registeredAuthMethod === 'none' &&
presentedAuthMethod !== 'none' &&
clientInfo.authMethodExplicit === undefined

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Mixed-method CIMD assertion needs a regression test

A CIMD document offering none and private_key_jwt exercises the new normalization boundary. Add a token request with an invalid assertion to pin verification on that path.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

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.

Public clients that send an empty or extra client_secret are rejected at /token after upgrading to 1.x

1 participant