Add authentication converter for introspected tokens

Adds configurable authentication converter for resource-servers with
token introspection (something very similar to what
JwtAuthenticationConverter does for resource-servers with JWT decoder).

The new (Reactive)OpaqueTokenAuthenticationConverter is given
responsibility for converting successful token introspection result
into an Authentication instance (which is currently done by a private
methods of OpaqueTokenAuthenticationProvider and
OpaqueTokenReactiveAuthenticationManager).

The default (Reactive)OpaqueTokenAuthenticationConverter, behave the
same as current private convert(OAuth2AuthenticatedPrincipal principal,
String token) methods: map authorities from scope attribute and build a
BearerTokenAuthentication.

Closes gh-11661
This commit is contained in:
ch4mpy
2022-08-30 09:33:11 -10:00
committed by Steve Riesenberg
parent cc988fc287
commit 1efb63387f
16 changed files with 385 additions and 48 deletions

View File

@@ -23,6 +23,7 @@ import java.security.interfaces.RSAPublicKey;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -68,13 +69,17 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParser.JwtBeanDefinitionParser;
import org.springframework.security.config.http.OAuth2ResourceServerBeanDefinitionParser.OpaqueTokenBeanDefinitionParser;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.oauth2.core.OAuth2AuthenticatedPrincipal;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
@@ -87,6 +92,7 @@ import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.TestJwts;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.security.oauth2.server.resource.introspection.NimbusOpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
@@ -662,6 +668,20 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
// @formatter:on
}
@Test
public void configureWhenIntrospectingWithAuthenticationConverterThenUses() throws Exception {
this.spring.configLocations(xml("OpaqueTokenRestOperations"), xml("OpaqueTokenAndAuthenticationConverter"))
.autowire();
mockRestOperations(json("Active"));
// @formatter:off
this.mvc.perform(get("/authenticated").header("Authorization", "Bearer token"))
.andExpect(status().isNotFound());
this.mvc.perform(get("/authenticated").header("Authorization", "Bearer invalidToken"))
.andExpect(status().isUnauthorized());
// @formatter:on
}
@Test
public void getWhenIntrospectionFailsThenUnauthorized() throws Exception {
this.spring.configLocations(xml("OpaqueTokenRestOperations"), xml("OpaqueToken")).autowire();
@@ -1096,4 +1116,39 @@ public class OAuth2ResourceServerBeanDefinitionParserTests {
}
public static class TestAuthentication extends AbstractAuthenticationToken {
private final String introspectedToken;
public TestAuthentication(String introspectedToken, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.introspectedToken = introspectedToken;
}
@Override
public Object getCredentials() {
return this.introspectedToken;
}
@Override
public Object getPrincipal() {
return this.introspectedToken;
}
@Override
public boolean isAuthenticated() {
return "token".equals(this.introspectedToken);
}
}
public static class TestOpaqueTokenAuthenticationConverter implements OpaqueTokenAuthenticationConverter {
@Override
public Authentication convert(String introspectedToken, OAuth2AuthenticatedPrincipal authenticatedPrincipal) {
return new TestAuthentication(introspectedToken, Collections.emptyList());
}
}
}