Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OAuth2ResourceServerConfigurer#authenticationManagerResolver should override #jwt #16406

Open
jgrandja opened this issue Jan 13, 2025 · 7 comments
Assignees
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) status: ideal-for-contribution An issue that we actively are looking for someone to help us with type: enhancement A general enhancement

Comments

@jgrandja
Copy link
Contributor

Given the following configuration:

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
		throws Exception {

	OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
			OAuth2AuthorizationServerConfigurer.authorizationServer();

	http
		.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
		.with(authorizationServerConfigurer, (authorizationServer) ->
			authorizationServer
				.oidc(Customizer.withDefaults())	// Enable OpenID Connect 1.0
		)
		.authorizeHttpRequests((authorize) ->
			authorize
				.anyRequest().authenticated()
		)
		.exceptionHandling((exceptions) -> exceptions
			.defaultAuthenticationEntryPointFor(
				new LoginUrlAuthenticationEntryPoint("/login"),
				new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
			)
		)
		.oauth2ResourceServer(resourceServer ->
			resourceServer
				.authenticationManagerResolver((context) -> customAuthenticationManager)
		);

	return http.build();
}

The application context will fail to build with the error message:

Caused by: java.lang.IllegalStateException: If an authenticationManagerResolver() is configured, then it takes precedence over any jwt() or opaqueToken() configuration.

The reason is because OAuth2AuthorizationServerConfigurer will default to resourceServer.jwt() if the OIDC UserInfo endpoint or OIDC Client Registration endpoint is enabled. However, if an application configures a client to use opaque tokens for an OpenID Connect flow, then configuring the authenticationManagerResolver() should be possible if support for both JWT and Opaque access tokens is required. As of now, it's not possible since resourceServer.jwt() was previously configured as the default by OAuth2AuthorizationServerConfigurer.

A similar error condition occurs with the following configuration:

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
		throws Exception {

	OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
			OAuth2AuthorizationServerConfigurer.authorizationServer();

	http
		.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
		.with(authorizationServerConfigurer, (authorizationServer) ->
			authorizationServer
				.oidc(Customizer.withDefaults())	// Enable OpenID Connect 1.0
		)
		.authorizeHttpRequests((authorize) ->
			authorize
				.anyRequest().authenticated()
		)
		.exceptionHandling((exceptions) -> exceptions
			.defaultAuthenticationEntryPointFor(
				new LoginUrlAuthenticationEntryPoint("/login"),
				new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
			)
		)
		.oauth2ResourceServer(resourceServer ->
			resourceServer
				.opaqueToken(Customizer.withDefaults())
		);

	return http.build();
}

The error message is:

Caused by: java.lang.IllegalStateException: Spring Security only supports JWTs or Opaque Tokens, not both at the same time.

The application is not able to override the default resourceServer.jwt() configured by OAuth2AuthorizationServerConfigurer to configure support for resourceServer.opaqueToken() instead.

@jgrandja jgrandja added status: waiting-for-triage An issue we've not yet triaged type: bug A general bug labels Jan 13, 2025
@jgrandja jgrandja removed the status: waiting-for-triage An issue we've not yet triaged label Jan 13, 2025
@jzheaux jzheaux added type: enhancement A general enhancement in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) status: ideal-for-contribution An issue that we actively are looking for someone to help us with and removed type: bug A general bug labels Jan 14, 2025
@jzheaux
Copy link
Contributor

jzheaux commented Jan 14, 2025

Thanks, @jgrandja, I'll take a look. It's quite common in Spring Security for the last-configured component to take precedence, so I imagine this is a reasonable improvement.

The only thing I hesitate on is that disallowing this seems to have been intentional in the code.

Either way, I've marked this as ideal-for-contribution and would be happy to work with someone on a PR.

@jzheaux jzheaux changed the title Unable to configure resourceServer.authenticationManagerResolver() if resourceServer.jwt() previously configured OAuth2ResourceServer#authenticationManagerResolver() should override #jwt() Jan 14, 2025
@jzheaux jzheaux changed the title OAuth2ResourceServer#authenticationManagerResolver() should override #jwt() OAuth2ResourceServer#authenticationManagerResolver should override #jwt Jan 14, 2025
@jzheaux jzheaux changed the title OAuth2ResourceServer#authenticationManagerResolver should override #jwt OAuth2ResourceServerConfigurer#authenticationManagerResolver should override #jwt Jan 14, 2025
@roar-skinderviken
Copy link

roar-skinderviken commented Jan 14, 2025

One possible solution might be to just remove the code block starting on line 356
and let users of the code be responsible for configuring this.

As I see this, there are 3 possible configurations

  • oauth2.authenticationManagerResolver
  • oauth2.jwt
  • oauth2.opaqueToken

and just one of them can be applied.

This will be a breaking change, therefore some code for fail-fast if oauth is not configured properly, will be required.

@jgrandja
Copy link
Contributor Author

@roar-skinderviken

One possible solution might be to just remove the code block starting on line 356
and let users of the code be responsible for configuring this.

That block of code was added in spring-authorization-server#707 to simplify configuring Authorization Server. So I don't think we want to remove that block and force the user to explicitly configure resourceServer(). Defaulting to resourceServer().jwt() is a sensible default.

@jgrandja
Copy link
Contributor Author

@jzheaux

It's quite common in Spring Security for the last-configured component to take precedence, so I imagine this is a reasonable improvement.

I agree.

As of now, if a default is set (resourceServer().jwt()) by the framework (Authorization Server) then it still should be possible for the application to override the default. However, this is not possible for this specific configuration scenario so it is a blocker.

@roar-skinderviken
Copy link

roar-skinderviken commented Jan 15, 2025

It seems like putting

                .oauth2ResourceServer(oauth2 ->
                        oauth2.opaqueToken(Customizer.withDefaults())
                )

before

                .with(authorizationServerConfigurer, authorizationServer ->
                        authorizationServer.oidc(Customizer.withDefaults())
                )

fixes the issue, but this should be verified by @malvinpatrick as well. I need some more Spring Security debugging-time to explain why and to discover any gotchas with this solution, unless one of you guys know the answers.

curl -X GET "http://localhost:9000/userinfo" -H "Authorization: Bearer <OPAQUE TOKEN>"

works as expected.

@malvinpatrick
Copy link

malvinpatrick commented Jan 16, 2025

Woahh thankyou @roar-skinderviken, in my case it solved the problem, but when you add oauth2.opaqueToken, how can you config OpaqueTokenIntrospector ?

I've tried your solution on this config, but I'm not pretty sure my code is a good way.

Based on this documentation, default OpaqueTokenIntrospector needs information about clientId and clientSecret which weird things in my case, an authorization server needs clientId and clientSecret for himself to handle opaqueToken, which Authorization Server already have repository to query token data on database.

So I create CustomOpaqueTokenIntrospector

Can you guys review my code ? If it good, maybe this can be a default OpaqueTokenIntrospector for Spring Authorization Server Project.

@roar-skinderviken
Copy link

@malvinpatrick
Good to hear that it solved your problem. Please see my answer to your question on StackOverflow https://stackoverflow.com/a/79339116/14072498.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: oauth2 An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose) status: ideal-for-contribution An issue that we actively are looking for someone to help us with type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

4 participants