Skip to content

Get token fails for public client on grant_type "authorization_code" (with PKCE) #1812

Closed
@jkornatzki

Description

@jkornatzki

Describe the bug
Spring authorization server returns error response 'invalid_request ' when requesting a token with grant type "authorization_code" (with PKCE) from a client which is configured public (no client_secret, client-authentication-method "none").

To Reproduce
Started with a clean spring boot project for a local try-out of spring boot authorization server.
Configured two clients:

Via Postman I tried to issue a token from each client using grant_type "authorization_code" (with PKCE). Same user credentials were used in both cases, requested same /oauth2/authorize and /oauth2/token endpoints.

Both requests get a code returned from /oauth2/authorize endpoint & try to exchange it for a token at /oauth2/token sending grant_type (authorization_code), code, redirect_uri and code_verifier. The request for the confidential client succeeds, while the request for public client gets returned {"error": "invalid_request"}.

Error log from spring authorization server:

2024-11-11T22:47:04.407+01:00 TRACE 79294 --- [identityprovider] [nio-9000-exec-3] o.s.a.w.OAuth2ClientAuthenticationFilter : Client authentication failed: [invalid_request] 

org.springframework.security.oauth2.core.OAuth2AuthenticationException: null
	at org.springframework.security.oauth2.server.authorization.web.authentication.ClientSecretBasicAuthenticationConverter.convert(ClientSecretBasicAuthenticationConverter.java:78) ~[spring-security-oauth2-authorization-server-1.3.3.jar:1.3.3]
	at org.springframework.security.oauth2.server.authorization.web.authentication.DelegatingAuthenticationConverter.convert(DelegatingAuthenticationConverter.java:60) ~[spring-security-oauth2-authorization-server-1.3.3.jar:1.3.3]
	at org.springframework.security.oauth2.server.authorization.web.OAuth2ClientAuthenticationFilter.doFilterInternal(OAuth2ClientAuthenticationFilter.java:134) ~[spring-security-oauth2-authorization-server-1.3.3.jar:1.3.3]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.14.jar:6.1.14]
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) ~[spring-security-web-6.3.4.jar:6.3.4]

Expected behavior
Authorization server successfully issues a token for the public client.

Sample
Please see client config and requests below or in sample project

application.yml

server:
  port: 9000

spring:
  application:
    name: spring-authorization-sample
  security:
    user:
      name: user1
      password: password
    oauth2:
      authorizationserver:
        client:
          confidential-client:
            registration:
              client-id: "confidential-client"
              client-secret: "{noop}secret"
              client-authentication-methods:
                - "client_secret_basic"
              authorization-grant-types:
                - "authorization_code"
                - "refresh_token"
                - "client_credentials"
              redirect-uris:
                - "http://127.0.0.1:8080/login/oauth2/code/confidential-client-oidc"
              scopes:
                - "openid"
                - "profile"
                - "message.read"
            require-authorization-consent: true
          public-client:
            registration:
              client-id: "public-client"
              client-authentication-methods:
                - "none"
              authorization-grant-types:
                - "authorization_code"
              redirect-uris:
                - "http://127.0.0.1:8080/login/oauth2/code/public-client-oidc"
              scopes:
                - "openid"
                - "profile"
                - "message.read"
            require-authorization-consent: true
            require-proof-key: true

Postman collection

{
	"info": {
		"_postman_id": "8cb75b26-f6b7-4ddf-82bf-c5e6e9e0600f",
		"name": "Spring Authorization Sample",
		"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
		"_exporter_id": "29654337"
	},
	"item": [
		{
			"name": "Confidential client token request",
			"request": {
				"auth": {
					"type": "oauth2",
					"oauth2": [
						{
							"key": "grant_type",
							"value": "authorization_code_with_pkce",
							"type": "string"
						},
						{
							"key": "password",
							"value": "",
							"type": "string"
						},
						{
							"key": "username",
							"value": "",
							"type": "string"
						},
						{
							"key": "scope",
							"value": "message.read",
							"type": "string"
						},
						{
							"key": "clientSecret",
							"value": "secret",
							"type": "string"
						},
						{
							"key": "redirect_uri",
							"value": "http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc",
							"type": "string"
						},
						{
							"key": "clientId",
							"value": "messaging-client",
							"type": "string"
						},
						{
							"key": "authUrl",
							"value": "http://localhost:9000/oauth2/authorize",
							"type": "string"
						},
						{
							"key": "accessTokenUrl",
							"value": "http://localhost:9000/oauth2/token",
							"type": "string"
						},
						{
							"key": "tokenName",
							"value": "spring auth",
							"type": "string"
						},
						{
							"key": "headerPrefix",
							"value": "Bearer ",
							"type": "string"
						},
						{
							"key": "addTokenTo",
							"value": "header",
							"type": "string"
						}
					]
				},
				"method": "GET",
				"header": [],
				"url": {
					"raw": ""
				}
			},
			"response": []
		},
		{
			"name": "Public client token request",
			"request": {
				"auth": {
					"type": "oauth2",
					"oauth2": [
						{
							"key": "grant_type",
							"value": "authorization_code_with_pkce",
							"type": "string"
						},
						{
							"key": "username",
							"value": "",
							"type": "string"
						},
						{
							"key": "password",
							"value": "",
							"type": "string"
						},
						{
							"key": "scope",
							"value": "message.read",
							"type": "string"
						},
						{
							"key": "accessTokenUrl",
							"value": "http://localhost:9000/oauth2/token",
							"type": "string"
						},
						{
							"key": "authUrl",
							"value": "http://localhost:9000/oauth2/authorize",
							"type": "string"
						},
						{
							"key": "clientId",
							"value": "public-client",
							"type": "string"
						},
						{
							"key": "code_verifier",
							"value": "",
							"type": "string"
						},
						{
							"key": "challengeAlgorithm",
							"value": "S256",
							"type": "string"
						},
						{
							"key": "clientSecret",
							"value": "",
							"type": "string"
						},
						{
							"key": "redirect_uri",
							"value": "http://127.0.0.1:8080/login/oauth2/code/public-client-oidc",
							"type": "string"
						},
						{
							"key": "tokenName",
							"value": "spring auth",
							"type": "string"
						},
						{
							"key": "headerPrefix",
							"value": "Bearer ",
							"type": "string"
						},
						{
							"key": "addTokenTo",
							"value": "header",
							"type": "string"
						}
					]
				},
				"method": "GET",
				"header": [],
				"url": {
					"raw": ""
				}
			},
			"response": []
		}
	]
}

Metadata

Metadata

Assignees

Labels

for: stackoverflowA question that's better suited to stackoverflow.com

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions