Skip to content

feat: reject secret creation and fix reporting for public clients - #28097

Draft
BobbyHo wants to merge 2 commits into
oauth2-public-clients-token-exchangefrom
oauth2-public-clients-guards
Draft

feat: reject secret creation and fix reporting for public clients#28097
BobbyHo wants to merge 2 commits into
oauth2-public-clients-token-exchangefrom
oauth2-public-clients-guards

Conversation

@BobbyHo

@BobbyHo BobbyHo commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Split out of #27873 to make that PR smaller to review. Fifth in the stack (on top of #28047). Closes two remaining gaps beyond registration and the token endpoint: the admin secrets API, and what auth method gets reported back for a legacy-mismatched client.

  • CreateAppSecret rejects minting a secret for a public client (RFC 7591 §2) — deleting a secret the token endpoint never validated would look like a kill switch while revoking nothing, since a public client's tokens carry no app_secret_id
  • reportedAuthMethod() normalizes what token_endpoint_auth_method registration, GET, and PUT report back when a client's stored method and client_type disagree, so Coder never tells a client to authenticate in a way the token endpoint won't accept

Refs https://linear.app/codercom/issue/ENG-3029/oauth2-support-public-client

…ic clients

Split out of #27873 to make that PR smaller to review. Fourth in the
stack; this is the half that makes the public client registered by the
previous PR in the stack actually able to obtain a token.

The token endpoint no longer requires a client_secret for a public
client: extractTokenRequest skips the client_secret presence check, and
authorizationCodeGrant skips secret validation entirely for a public
client, since it has none. PKCE was already mandatory for every
authorization_code flow, so public clients inherit it with no new
validation code. That makes the code ownership check (dbCode.AppID !=
app.ID) the only binding between the exchange and the app named by
client_id for a public client, where it was defense in depth for
confidential ones. It is retained and now covered with a public client
on both sides.

Issued tokens for a public client carry a NULL app_secret_id rather
than referencing a secret row that does not exist. The refresh and
revocation paths already verify ownership directly via app_id rather
than joining through app_secret_id, so they need no code change, only
updated comments and coverage confirming they handle a NULL
app_secret_id correctly.

Refs https://linear.app/codercom/issue/ENG-3029/oauth2-support-public-client
Fourth in the stack splitting up #27873 (public OAuth2 clients), on top
of #28047. Closes two remaining gaps beyond registration and the token
endpoint: the admin/API surface for managing client secrets, and what
auth method Coder reports back to a client whose stored method and
client_type disagree.

CreateAppSecret now rejects minting a secret for a public client
(RFC 7591 §2, OAuth 2.1 §2.1: a public client authenticates with PKCE
alone). Without this, an operator could create a secret the token
endpoint never validates, and deleting it would look like a kill
switch while revoking nothing, since a public client's tokens carry a
NULL app_secret_id.

reportedAuthMethod() normalizes what token_endpoint_auth_method
CreateDynamicClientRegistration, GetClientConfiguration, and
UpdateClientConfiguration report back for a client whose stored method
and client_type disagree. This only arises for clients registered
before client_type was derived from the method: such a row is stored
confidential with a method of "none", and reporting "none" verbatim
would tell the client to drop a secret its exchange still requires.
Reporting the enforced behavior instead means the client's next PUT
repairs the mismatch on its own.
@github-actions

Copy link
Copy Markdown

Docs preview

Check off each page once it's been reviewed. If a page changes in a later push, its checkbox clears automatically so it gets a fresh look. Pages not yet wired into the docs navigation aren't listed here.

@BobbyHo
BobbyHo force-pushed the oauth2-public-clients-token-exchange branch from b6d1a46 to 240db54 Compare August 15, 2026 16:40
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