I am trying to implement the OAuth2 Authorization Server with OpenID Connect, using Spring Security. For this, I am using the authorization code flow with refresh token and JWT. Here is my configuration code-
@Configuration
public class SecurityConfig {
@Bean
@Order(1)
public SecurityFilterChain asSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.authorizationEndpoint(
a -> a.authenticationProviders(getAuthorizationEndpointProviders()))
.oidc(Customizer.withDefaults());
http.exceptionHandling(
e -> e.authenticationEntryPoint(
new LoginUrlAuthenticationEntryPoint("/login")));
return http.build();
}
private Consumer<List<AuthenticationProvider>> getAuthorizationEndpointProviders() {
return providers -> {
for (AuthenticationProvider p : providers) {
if (p instanceof OAuth2AuthorizationCodeRequestAuthenticationProvider x) {
x.setAuthenticationValidator(new CustomRedirectUriValidator());
}
}
};
}
@Bean
@Order(2)
public SecurityFilterChain appSecurityFilterChain(HttpSecurity http) throws Exception {
http.formLogin()
.and()
.authorizeHttpRequests().anyRequest().authenticated();
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
var u1 = User.withUsername("user")
.password("password")
.authorities("read")
.build();
return new InMemoryUserDetailsManager(u1);
}
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Bean
public RegisteredClientRepository registeredClientRepository() {
RegisteredClient r1 = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("client")
.clientSecret("secret")
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.redirectUri("https://springone.io/authorized")
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.tokenSettings(
TokenSettings.builder()
.accessTokenFormat(OAuth2TokenFormat.REFERENCE)
.accessTokenTimeToLive(Duration.ofSeconds(900))
.build())
.build();
return new InMemoryRegisteredClientRepository(r1);
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.build();
}
@Bean
public JWKSource<SecurityContext> jwkSource() throws Exception {
KeyPairGenerator kg = KeyPairGenerator.getInstance("RSA");
kg.initialize(2048);
KeyPair kp = kg.generateKeyPair();
RSAPublicKey publicKey = (RSAPublicKey) kp.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) kp.getPrivate();
RSAKey key = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID(UUID.randomUUID().toString())
.build();
JWKSet set = new JWKSet(key);
return new ImmutableJWKSet(set);
}
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> oAuth2TokenCustomizer() {
return context -> {
context.getClaims().claim("test", "test");
};
}
}
<>CustomRedirectUrlValidator
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationContext;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationException;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeRequestAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import java.util.function.Consumer;
public class CustomRedirectUriValidator implements Consumer<OAuth2AuthorizationCodeRequestAuthenticationContext> {
@Override
public void accept(OAuth2AuthorizationCodeRequestAuthenticationContext context) {
OAuth2AuthorizationCodeRequestAuthenticationToken a = context.getAuthentication();
RegisteredClient registeredClient = context.getRegisteredClient();
String uri = a.getRedirectUri();
if (!registeredClient.getRedirectUris().contains(uri)) {
var error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST);
throw new OAuth2AuthorizationCodeRequestAuthenticationException(error, null);
}
}
}
<PKCE 代码 verifier和代码挑战作为生成
SecureRandom sr = new SecureRandom();
byte[] code = new byte[32];
sr.nextBytes(code);
String codeVerifier = Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(code);
MessageDigest md;
{
try {
md = MessageDigest.getInstance("SHA-256");
byte[] digested = md.digest(codeVerifier.getBytes());
String code_challenge = Base64.getUrlEncoder().withoutPadding().encodeToString(digested);
log.info("Code verifier: {}", codeVerifier);
log.info("Code challenge: {}", code_challenge);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
现在,这是我为获得象征性的准入而采取的步骤。
- GET request to oauth2/authorize-
http:// localhost:80/oauth2/authorize?response_type=code&client_id=client&cop= openid&redirect_uri=https://merone.io/authorized&code_challenge=z5f7uuzQ2f0c1CNpuY0UoQE5jSN30YpcxS2s6wmoPq0&code_challenge_method=256
- After providing the login username and password as user and password, the browser redirects to the specified redirection url-
https://childrenone.io/authorized?code=TfzC56cc7xwa0wS-O0VvMm1k6kOhYchOcj7sW_pXyeEaRIvw9V6N5YuXoeqwQka1Cvf0ZY9EzGg0dM9zlCXLPYU3q7_T9KVsuc1_sGTV7XBxCHPxtq1VRufg3Zx
现在,如果一切都正确的话,我就必须能够在反应机构获得信号。 但我却发现错误。
{
"error_description": "OAuth 2.0 Parameter: grant_type",
"error": "unsupported_grant_type",
"error_uri": "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"
}
Now, I checked with Spring Security Grant Types and Spring Security Authorization Server that the grant type authorization_code is valid. But, I can t understand, why am I getting this error? Please help me resolve this.