Repository navigation
Answer token endpoint failures with the RFC 6749 error codes, and look up the UserInfo client by azp - #282
Merged
longsleep merged 2 commits intoOct 14, 2026
Conversation
A refresh token which failed to parse, verify or validate, being malformed, expired or signed with a key lico does not hold, was answered with invalid_request. RFC 6749 section 5.2 assigns that case to invalid_grant, and the difference is what a client acts on: an invalid grant means the user has to sign in again, an invalid request means the request itself is broken. TokenRequest.Validate now answers with it. A failed client lookup at the token endpoint, for an unknown client_id or a wrong secret alike, was answered with access_denied, which is a code of the authorization endpoint (RFC 6749 section 4.1.2.1). It now answers invalid_client. oidc-go does not define that code, so it is added as ErrorCodeOAuth2InvalidClient in oidc/errors.go. Section 5.2 also asks that a client which authenticated through the Authorization header be answered with 401 and a WWW-Authenticate challenge of the same scheme. TokenRequest gains ClientAuthenticationBasic, set when the Basic header supplied the credentials, and in that case TokenHandler answers an invalid_client with 401 and WWW-Authenticate: Basic realm="<issuer>". Credentials sent in the body keep the 400. This matters once clients are long-lived and pre-registered rather than minted per connection by dynamic registration. Such clients outlive their refresh tokens and key rotations, and a wrong secret pasted into a client dialog is the likeliest way an operator meets invalid_client. The API spec now documents which failures of the token endpoint answer invalid_grant and which invalid_client, adds invalid_client to the OIDCError enum, and describes the 401 with WWW-Authenticate: Basic. TestTokenRefresh gains an expired and a malformed refresh token, both expecting invalid_grant. The new TestTokenClientAuthentication covers an unknown client and a wrong secret, each sent in the body and in the header, for which the test provider gains a confidential client with a secret.
UserInfoHandler took the client from claims.Audience[0], both for the p.clients.Get lookup that decides whether user info is answered signed and for the LookupContext.Audience it hands to the identity manager, which documents that value as the client ID. Since resource indicators (RFC 8707) a token bound to a resource carries the resource as its audience and the client in azp. For such a token the registration lookup missed, so a client which had asked for userinfo_signed_response_alg was answered with unsigned JSON, and the backend was told that the resource was the client. Both now use claims.AuthorizedPartyOrAudience(), as every other lookup of a client by token already does. This also matters for a later change: client IDs which are URLs will be resolved by fetching them, and a lookup by audience would then request the resource URL whenever its host is allowed. The API spec now says that userinfo accepts resource-bound tokens and takes their client from azp. TestAuthorizeAndTokenWithResource gains a subtest which gives the test client a signed user info algorithm, obtains a resource-bound token and expects signed user info whose aud is the client. Without the fix it receives unsigned JSON.
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.
Two fixes in how the token and UserInfo endpoints identify the client and report failures.
Token endpoint error codes
The token endpoint answered two kinds of failure with error codes RFC 6749 section 5.2 does not assign to them. What a client does next depends on the code, so it took the wrong action.
invalid_request. It now answersinvalid_grant, which tells the client to sign in again rather than to fix its request.client_idor a wrong secret alike, was answered withaccess_denied, which belongs to the authorization endpoint. It now answersinvalid_client.oidc-godoes not define that code, so it is added inoidc/errors.go.Authorizationheader getsinvalid_clientwith status 401 andWWW-Authenticate: Basic realm="<issuer>", as section 5.2 asks. Credentials sent in the body keep the 400.This matters most for pre-registered, long-lived clients: they outlive refresh tokens and key rotations, and a mistyped secret is how an operator usually meets
invalid_client.UserInfo client lookup
UserInfo took the client from the first
audvalue. Since #273, a token bound to a resource carries the resource asaudand the client inazp. For such a token:userinfo_signed_response_alggot unsigned JSON;UserInfo now uses
azpwhen present, as every other lookup of a client by token already does.Tests and docs
TestTokenRefreshgains an expired and a malformed refresh token, both expectinginvalid_grant.TestTokenClientAuthenticationcovers an unknown client and a wrong secret, each sent in the body and in the header.TestAuthorizeAndTokenWithResourcegains a subtest expecting signed user info for a resource-bound token. Without the fix it gets unsigned JSON.invalid_grantand whichinvalid_client, the 401, and the UserInfo behaviour.