Introspect endpoint Authorization Server support

Issue: gh-5200
This commit is contained in:
Josh Cummings 2018-11-01 17:30:10 -06:00
parent ef9c3e4771
commit c59d40593b
2 changed files with 74 additions and 8 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2018 the original author or authors. * Copyright 2002-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,12 +21,15 @@ import java.security.KeyPair;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec; import java.security.spec.RSAPublicKeySpec;
import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jose.jwk.RSAKey;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
@ -37,18 +40,23 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.endpoint.FrameworkEndpoint; import org.springframework.security.oauth2.provider.endpoint.FrameworkEndpoint;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter; import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter; import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;
import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
/** /**
@ -66,17 +74,20 @@ import org.springframework.web.bind.annotation.ResponseBody;
*/ */
@EnableAuthorizationServer @EnableAuthorizationServer
@Configuration @Configuration
public class JwkSetConfiguration extends AuthorizationServerConfigurerAdapter { public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
AuthenticationManager authenticationManager; AuthenticationManager authenticationManager;
KeyPair keyPair; KeyPair keyPair;
boolean jwtEnabled;
public JwkSetConfiguration( public AuthorizationServerConfiguration(
AuthenticationConfiguration authenticationConfiguration, AuthenticationConfiguration authenticationConfiguration,
KeyPair keyPair) throws Exception { KeyPair keyPair,
@Value("${security.oauth2.authorizationserver.jwt.enabled:true}") boolean jwtEnabled) throws Exception {
this.authenticationManager = authenticationConfiguration.getAuthenticationManager(); this.authenticationManager = authenticationConfiguration.getAuthenticationManager();
this.keyPair = keyPair; this.keyPair = keyPair;
this.jwtEnabled = jwtEnabled;
} }
@Override @Override
@ -94,23 +105,37 @@ public class JwkSetConfiguration extends AuthorizationServerConfigurerAdapter {
.authorizedGrantTypes("password") .authorizedGrantTypes("password")
.secret("{noop}secret") .secret("{noop}secret")
.scopes("message:write") .scopes("message:write")
.accessTokenValiditySeconds(600_000_000)
.and()
.withClient("noscopes")
.authorizedGrantTypes("password")
.secret("{noop}secret")
.scopes("none")
.accessTokenValiditySeconds(600_000_000); .accessTokenValiditySeconds(600_000_000);
// @formatter:on // @formatter:on
} }
@Override @Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) { public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// @formatter:off // @formatter:off
endpoints endpoints
.authenticationManager(this.authenticationManager) .authenticationManager(this.authenticationManager)
.accessTokenConverter(accessTokenConverter())
.tokenStore(tokenStore()); .tokenStore(tokenStore());
if (this.jwtEnabled) {
endpoints
.accessTokenConverter(accessTokenConverter());
}
// @formatter:on // @formatter:on
} }
@Bean @Bean
public TokenStore tokenStore() { public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter()); if (this.jwtEnabled) {
return new JwtTokenStore(accessTokenConverter());
} else {
return new InMemoryTokenStore();
}
} }
@Bean @Bean
@ -137,7 +162,11 @@ class UserConfig extends WebSecurityConfigurerAdapter {
http http
.authorizeRequests() .authorizeRequests()
.mvcMatchers("/.well-known/jwks.json").permitAll() .mvcMatchers("/.well-known/jwks.json").permitAll()
.anyRequest().authenticated(); .anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().ignoringRequestMatchers(request -> "/introspect".equals(request.getRequestURI()));
} }
@Bean @Bean
@ -152,6 +181,41 @@ class UserConfig extends WebSecurityConfigurerAdapter {
} }
} }
/**
* Legacy Authorization Server (spring-security-oauth2) does not support any
* Token Introspection endpoint.
*
* This class adds ad-hoc support in order to better support the other samples in the repo.
*/
@FrameworkEndpoint
class IntrospectEndpoint {
TokenStore tokenStore;
public IntrospectEndpoint(TokenStore tokenStore) {
this.tokenStore = tokenStore;
}
@PostMapping("/introspect")
@ResponseBody
public Map<String, Object> introspect(@RequestParam("token") String token) {
OAuth2AccessToken accessToken = this.tokenStore.readAccessToken(token);
Map<String, Object> attributes = new HashMap<>();
if (accessToken == null || accessToken.isExpired()) {
attributes.put("active", false);
return attributes;
}
OAuth2Authentication authentication = this.tokenStore.readAuthentication(token);
attributes.put("active", true);
attributes.put("exp", accessToken.getExpiration().getTime());
attributes.put("scope", accessToken.getScope().stream().collect(Collectors.joining(" ")));
attributes.put("sub", authentication.getName());
return attributes;
}
}
/** /**
* Legacy Authorization Server (spring-security-oauth2) does not support any * Legacy Authorization Server (spring-security-oauth2) does not support any
* <a href target="_blank" href="https://tools.ietf.org/html/rfc7517#section-5">JWK Set</a> endpoint. * <a href target="_blank" href="https://tools.ietf.org/html/rfc7517#section-5">JWK Set</a> endpoint.

View File

@ -1 +1,3 @@
server.port: 8081 server.port: 8081
# security.oauth2.authorizationserver.jwt.enabled: false