spring-security icon indicating copy to clipboard operation
spring-security copied to clipboard

Spring boot/security encoding introspection credentials incorrectly

Open ianHowlett1 opened this issue 1 month ago • 3 comments

Describe the bug I am upgrading Spring boot from 3.3.5 to 3.5.8. This is incrementing Spring security from 6.3.4 to 6.5.7. The introspection credentials in the Basic Auth header are being encoded incorrectly after the upgrade.

application.yml

spring: security: oauth2: resourceserver: opaquetoken: introspection-uri: http://localhost:7171/introspect client-id: someClientId client-secret: h25spw7I_y0Kt=s5NPo

Spring boot 3.3.5 / Spring security 6.3.4 Encoded Authorization: Basic c29tZUNsaWVudElkOmgyNXNwdzdJX3kwS3Q9czVOUG8=

Spring boot 3.5.8 / Spring security 6.5.7 Encoded Authorization: Basic c29tZUNsaWVudElkOmgyNXNwdzdJX3kwS3QlM0RzNU5Qbw==

To Reproduce Link to project to reproduce. Run the application and invoke the example RestController. Change version of spring-boot-starter-parent from 3.3.5 to 3.5.8.

Expected behavior The encoded credentials should be encoded correctly as they were before, i.e. someClientId:h25spw7I_y0Kt=s5NPo should be encoded as: c29tZUNsaWVudElkOmgyNXNwdzdJX3kwS3Q9czVOUG8= and not: c29tZUNsaWVudElkOmgyNXNwdzdJX3kwS3QlM0RzNU5Qbw==

The encoding bug was introduced in: Spring boot - 3.5.0 Spring security - 6.5.0

Sample

https://github.com/ianHowlett1/spring-security-oauth-endcoding-bug

ianHowlett1 avatar Dec 02 '25 13:12 ianHowlett1

I think it's related to https://github.com/spring-projects/spring-security/releases/tag/6.5.0-M1

Encode clientId and clientSecret for OpaqueTokenIntrospector and ReactiveOpaqueTokenIntrospector

clientId and clientSecret will be encoded, and the result is c29tZUNsaWVudElkOmgyNXNwdzdJX3kwS3QlM0RzNU5Qbw==

ngocnhan-tran1996 avatar Dec 03 '25 03:12 ngocnhan-tran1996

Hi, @ianHowlett1. Thank you for the report and for your sample. Can you clarify how I should use your sample to verify the issue? Give that the change to SpringOpaqueTokenIntrospector is only additive, I'm curious to learn how the encoding changed between those two releases.

jzheaux avatar Dec 15 '25 17:12 jzheaux

Hi @jzheaux, The encoding behaviour prior using spring boot 3.3.5:

Run the Application class in an IDE, I use Intellij. Invoke the api created on this path:

PUT http://localhost:8080/example. Then check the logs, the line in the logs to look at are:

2025-12-15T17:38:02.312Z DEBUG 33260 --- [spring-security-oauth-endcoding-bug] [nio-8080-exec-3] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader@11a58ef68 pairs: {POST /introspect HTTP/1.1: null}{Accept: application/json}{Content-Type: application/x-www-form-urlencoded;charset=UTF-8}{Authorization: Basic c29tZUNsaWVudElkOmgyNXNwdzdJX3kwS3Q9czVOUG8=}{User-Agent: Java/17.0.12}{Host: localhost:7171}{Connection: keep-alive}{Content-Length: 94}

The client-id and client-secret in the application.yml are being encoded as:

c29tZUNsaWVudElkOmgyNXNwdzdJX3kwS3Q9czVOUG8=

To recreate issue:

In the pom.xml amend from:

<artifactId>spring-boot-starter-parent</artifactId> 3.3.5

<artifactId>spring-boot-starter-parent</artifactId> 3.5.0

Run the Application class again in the IDE. Invoke the api again on the same path

PUT http://localhost:8080/example. Then check the logs, the line in the logs to look at are:

025-12-15T17:49:06.846Z DEBUG 11380 --- [spring-security-oauth-endcoding-bug] [nio-8080-exec-3] s.n.www.protocol.http.HttpURLConnection : sun.net.www.MessageHeader@18246df68 pairs: {POST /introspect HTTP/1.1: null}{Accept: application/json}{Content-Type: application/x-www-form-urlencoded}{Authorization: Basic c29tZUNsaWVudElkOmgyNXNwdzdJX3kwS3QlM0RzNU5Qbw==}{User-Agent: Java/17.0.12}{Host: localhost:7171}{Connection: keep-alive}{Content-Length: 94}

The client-id and client-secret are now being encoded as:

c29tZUNsaWVudElkOmgyNXNwdzdJX3kwS3QlM0RzNU5Qbw==

I think the issue is in the Builder in SpringOpaqueTokenIntrospector in spring-security-oauth2-resource-server-6.5.0.jar, these methods:

	public Builder clientId(String clientId) {
		Assert.notNull(clientId, "clientId cannot be null");
		this.clientId = URLEncoder.encode(clientId, StandardCharsets.UTF_8);
		return this;
	}

	public Builder clientSecret(String clientSecret) {
		Assert.notNull(clientSecret, "clientSecret cannot be null");
		this.clientSecret = URLEncoder.encode(clientSecret, StandardCharsets.UTF_8);
		return this;
	}

URL encoding the clientId and clientSecret is encoding the "=" in the clientSecret to "%3D". This could also include other characters.

The clientId and clientSecret are then used in the below method:

	public SpringOpaqueTokenIntrospector build() {
		RestTemplate restTemplate = new RestTemplate();
		restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(this.clientId, this.clientSecret));
		return new SpringOpaqueTokenIntrospector(this.introspectionUri, restTemplate);
	}

These credentials are then base64 encoded in the BasicAuthenticationInterceptor. As the "=" has been changed to "%3D", when my security server base64 decodes these, the clientSecret has been changed from:

h25spw7I_y0Kt=s5NPo

to

h25spw7I_y0Kt%3Ds5NPo

And rejects it.

Please let me know, if there is anything else I can help with.

ianHowlett1 avatar Dec 15 '25 18:12 ianHowlett1