Reputation: 1
Continue for this problem, I still don't get any solution for handling my problem, issue 1865. I want to have spring authorization server with opaque token and oidc enabled.
Once oidc enabled, it allows client to call /userinfo endpoint. It's throw an error because of opaque bearer token can't handle by any provider.
When I try to configure custom authenticationManagerResolver
,
.oauth2ResourceServer(resourceServer ->
resourceServer .authenticationManagerResolver(authenticationManagerResolver(resourceServerProperties)));
It throws another error because of OAuth2AuthorizationServerConfigurer
that config jwt
when oidc is enabled
OidcConfigurer oidcConfigurer = getConfigurer(OidcConfigurer.class);
if (oidcConfigurer != null) {
if (oidcConfigurer.getConfigurer(OidcUserInfoEndpointConfigurer.class) != null
|| oidcConfigurer.getConfigurer(OidcClientRegistrationEndpointConfigurer.class) != null) {
httpSecurity
// Accept access tokens for User Info and/or Client Registration
.oauth2ResourceServer(
(oauth2ResourceServer) -> oauth2ResourceServer.jwt(Customizer.withDefaults()));
}
}
Error stack
Caused by: java.lang.IllegalStateException: If an authenticationManagerResolver() is configured, then it takes precedence over any jwt() or opaqueToken() configuration.
at org.springframework.util.Assert.state(Assert.java:79) ~[spring-core-6.2.0.jar:6.2.0]
at org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer.validateConfiguration(OAuth2ResourceServerConfigurer.java:299) ~[spring-security-config-6.4.1.jar:6.4.1]
at org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer.init(OAuth2ResourceServerConfigurer.java:260) ~[spring-security-config-6.4.1.jar:6.4.1]
at org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer.init(OAuth2ResourceServerConfigurer.java:147) ~[spring-security-config-6.4.1.jar:6.4.1]
at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.init(AbstractConfiguredSecurityBuilder.java:366) ~[spring-security-config-6.4.1.jar:6.4.1]
at org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder.doBuild(AbstractConfiguredSecurityBuilder.java:328) ~[spring-security-config-6.4.1.jar:6.4.1]
at org.springframework.security.config.annotation.AbstractSecurityBuilder.build(AbstractSecurityBuilder.java:38) ~[spring-security-config-6.4.1.jar:6.4.1]
at app.chameleon.authorization.server.config.AuthorizationServerConfig.authorizationServerSecurityFilterChain(AuthorizationServerConfig.java:143) ~[classes/:na]
at app.chameleon.authorization.server.config.AuthorizationServerConfig$$SpringCGLIB$$0.CGLIB$authorizationServerSecurityFilterChain$9(<generated>) ~[classes/:na]
at app.chameleon.authorization.server.config.AuthorizationServerConfig$$SpringCGLIB$$FastClass$$1.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:258) ~[spring-core-6.2.0.jar:6.2.0]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:348) ~[spring-context-6.2.0.jar:6.2.0]
at app.chameleon.authorization.server.config.AuthorizationServerConfig$$SpringCGLIB$$0.authorizationServerSecurityFilterChain(<generated>) ~[classes/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:580) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.lambda$instantiate$0(SimpleInstantiationStrategy.java:171) ~[spring-beans-6.2.0.jar:6.2.0]
... 42 common frames omitted
How to overcome this problem ?
Upvotes: 0
Views: 158
Reputation: 11134
While looking into this issue, I discovered that moving
.oauth2ResourceServer(oauth2 ->
oauth2.opaqueToken(Customizer.withDefaults())
)
above
.with(authorizationServerConfigurer, authorizationServer ->
authorizationServer.oidc(Customizer.withDefaults())
)
app starts, and
curl -X GET "http://localhost:9000/userinfo" -H "Authorization: Bearer <OPAQUE TOKEN>"
returns successfully.
Complete authorizationServerSecurityFilterChain
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(
HttpSecurity http,
AuthenticationManagerResolver<HttpServletRequest> tokenAuthenticationManagerResolver) throws Exception {
var authorizationServerConfigurer =
OAuth2AuthorizationServerConfigurer.authorizationServer();
http
.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
.oauth2ResourceServer(oauth2 ->
oauth2.opaqueToken(Customizer.withDefaults())
)
.with(authorizationServerConfigurer, authorizationServer ->
authorizationServer.oidc(Customizer.withDefaults())
)
.authorizeHttpRequests(authorize ->
authorize.anyRequest().authenticated()
)
// Redirect to the login page when not authenticated from the
// authorization endpoint
.exceptionHandling(exceptions -> exceptions
.defaultAuthenticationEntryPointFor(
new LoginUrlAuthenticationEntryPoint("/login"),
new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
)
);
return http.build();
}
OpaqueTokenIntrospector (application.yml)
spring:
security:
oauth2:
resourceserver:
opaque-token:
introspection-uri: http://localhost:9000/oauth2/introspect
client-id: my-client
client-secret: secret
You will most likely have values for client-id
and client-secret
somewhere else in your application.yml, assuming that section is oauth
:
client-id: ${oauth.client-id}
client-secret: ${oauth.client-secret}
Let's test the /userinfo-endpoint with this example JwtCustomizerConfig
. In your app you should get values from context.getPrincipal()
; I'm using hard-coded values for simplicity here. Why JwtEncodingContext
instead of OAuth2TokenClaimsContext
which is targeting opaque tokens? That relates to the original issue where OIDC-endpoints getting configured with JWT.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
@Configuration
public class JwtCustomizerConfig {
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> exampleJwtCustomizer() {
return context -> context.getClaims()
.claim("name", "Malvin Patrick")
.claim("given_name", "Malvin")
.claim("family_name", "Patrick")
.claim("picture", "https://example.com/image.jpg")
.claim("email", "[email protected]")
.claim("email_verified", true)
.claim("locale", "en");
}
}
With scope openid
, assuming username is user1:
{"sub":"user1"}
With scopes openid, email
{
"sub": "user1",
"email_verified": true,
"email": "[email protected]"
}
With scopes openid, email, profile
{
"sub": "user1",
"email_verified": true,
"given_name": "Malvin",
"locale": "en",
"picture": "https://example.com/image.jpg",
"name": "Malvin Patrick",
"family_name": "Patrick",
"email": "[email protected]"
}
You can also test the introspect
-endpoint. Replace my-client
with client-id
and secret
with client-secret
from application.yml.
curl -X POST \
http://localhost:9000/oauth2/introspect \
-H "Content-Type: application/x-www-form-urlencoded" \
-u "my-client:secret" \
-d "token=<OPAQUE_TOKEN>"
Please note that this solution is WIP regarding discovering any unwanted side-effects.
Upvotes: 0