Skip to content

/application/o/token/ doesn't accept url-encoded client_id/client_secret in Basic-Auth #20739

@pmenke-de

Description

@pmenke-de

Describe the bug

When trying to obtaining a token via /application/o/token/ using client_credentials in the Authorization: Basic header, the request is rejected with "error": "invalid_grant", if the client_id or client_secret contain urlencoded characters (as per rfc6749#section-2.3.1)

How to reproduce

You Need:

  • An Application with an OAuth2/OpenID Provider.
  • A service-account with an App password.

Let

# the username of the service-account
USER="service-account"

# the App passwort of the service account (60 random characters)
PASS="IkZRYWekQfBaZTRh6bpeBQQKKoqCoToQL6cTPI5BetoxxyzWof3BFZMuZaty"

# form service-account client_credentials as per rfc6749#section-2.3.1.
# neither USER nor PASS contain characters, that need urlencoding at this stage
CLIENT_CREDENTIALS="$(echo -n "$USER:$PASS" | base64 -w0)"

# the OAuth2/OpenID Provider client_id (40 random characters)
CLIENT_ID="f2jgG2J40E6yai6t30aDiJirdgtKYyGtpUXFckzj"

# form the token request credentials as per rfc6749#section-2.3.1.
# CLIENT_CREDENTIALS may contain base64 padding `=`-chars, which must be replaced with `%3D`.
# the length of `service-account` is chosen, such that there are two padding-chars in this example.
ENCODED_CLIENT_CREDENTIALS="${CLIENT_CREDENTIALS//=/%3D}"
TOKEN_REQUEST_CREDENTIALS="$(echo -n "$CLIENT_ID:$ENCODED_CLIENT_CREDENTIALS" | base64 -w0)"

Request:

POST https://authentik/application/o/token/
Accept: application/json
Content-Type: application/x-www-form-urlencoded
Authorization: Basic $TOKEN_REQUEST_CREDENTIALS

grant_type=client_credentials&scope=openid

Observe:

{
  "error": "invalid_grant",
  "error_description": "The provided authorization grant or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client",
  "request_id": "9fc942296bf344f081a6870648d2470f"
}

If TOKEN_REQUEST_CREDENTIALS is formed with the unencoded CLIENT_CREDENTIALS like follows, the request succeeds:

TOKEN_REQUEST_CREDENTIALS="$(echo -n "$CLIENT_ID:$CLIENT_CREDENTIALS" | base64 -w0)"

Expected behavior

The request should succeed, when the TOKEN_REQUEST_CREDENTIALS are formed, as described.

Probably: https://github.com/goauthentik/authentik/blob/main/authentik/providers/oauth2/utils.py#L123 should url-decode the received client_id and client_secret as specified in rfc6749#section-2.3.1.

Screenshots

No response

Additional context

I don't know, whether there are clients that rely on the "broken" behaviour. I don't know the OAuth2 spec and its history too well. I only found the spec reference by links from my client-library, which even allows me to circumvent this behaviour (spring-projects/spring-security#11440).
A existing client would break, if its client_id or client_secret contain char-sequences, that would be processed by url-decoding.

Deployment Method

Docker

Version

2026.2.1

Relevant log output

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingbug/confirmedConfirmed bugs

    Type

    Projects

    Status

    Done

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions