I have a Spring Authorization Server configured as both an authorization server and a resource server. I want to integrate it with Eclipse Che, which currently works with Keycloak. I want configure Eclipse Che with my custom authorization server.
Here is my authorization server configuration:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class AuthorizationServerConfig {
private final UserAccountDetailsService userAccountDetailsService;
private final String principal;
public AuthorizationServerConfig(UserAccountDetailsService userAccountDetailsService, @Value("${iam.registered-client.principal}") String principal) {
this.userAccountDetailsService = userAccountDetailsService;
this.principal = principal;
}
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0
http
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt
.jwtAuthenticationConverter(jwtAuthenticationConverter())
)
);
return http
.build();
}
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/assets/**", "/webjars/**", "/login", "/**").permitAll()
.anyRequest().authenticated()
)
.csrf((csrf) -> csrf.disable())
.httpBasic(Customizer.withDefaults())
.formLogin(Customizer.withDefaults())
.oauth2ResourceServer((resourceServer) -> resourceServer
.jwt(Customizer.withDefaults()))
.sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
@Bean
public RegisteredClientRepository registeredClientRepository() {
//Authorization code
RegisteredClient registeredClient = RegisteredClient.withId("eclipse-che")
.clientId("eclipse-che")
.clientSecret(passwordEncoder().encode("secret"))
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.redirectUri("http://127.0.0.1:8080/login/oauth2/code/eclipse-che")
.redirectUri("http://127.0.0.1:8080/authorized")
.scope(OidcScopes.OPENID)
.build();
//Client Credentials Flow
RegisteredClient msClient = RegisteredClient.withId(principal)
.clientId(principal)
.clientSecret(passwordEncoder().encode("secret"))
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
.scope(OidcScopes.OPENID)
// .scope(OidcScopes.PROFILE)
.build();
return new InMemoryRegisteredClientRepository(msClient, registeredClient);
}
@Autowired
DataSource dataSource;
@Bean
@Profile("ide")
public JdbcOAuth2AuthorizationService jdbcOAuth2AuthorizationService() {
JdbcOperations jdbcOperation = new JdbcTemplate(dataSource);
return new JdbcOAuth2AuthorizationService(jdbcOperation, registeredClientRepository());
}
@Bean
JWKSource<SecurityContext> jwkSource(KeyPair keyPair) {
RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
RSAKey rsaKey = new RSAKey.Builder(publicKey)
.privateKey(privateKey)
.keyID("main")
.build();
JWKSet jwkSet = new JWKSet(rsaKey);
return new ImmutableJWKSet<>(jwkSet);
}
/**
* Loads the RSA public and private keys from the specified values.
*
* # Generate a RSA private key with 2048 bits
* openssl genrsa -out private_key.pem 2048
*
* # Extract the public key from the private key
* openssl rsa -in private_key.pem -pubout -out public_key.pem
*
* @param publicKey the RSA public key
* @param privateKey the RSA private key
* @return a new KeyPair containing the public and private keys
*/
@Bean
KeyPair loadRsaKey(
@Value("${iam.jwt.public.key}") RSAPublicKey publicKey,
@Value("${iam.jwt.private.key}") RSAPrivateKey privateKey
) {
return new KeyPair(publicKey, privateKey);
}
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
@Bean
public JwtEncoder jwtEncoder(JWKSource<SecurityContext> jwkSource) {
return new NimbusJwtEncoder(jwkSource);
}
@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(users());
authenticationProvider.setPasswordEncoder(passwordEncoder());
return authenticationProvider;
}
@Bean
UserDetailsService users() {
return userAccountDetailsService;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
// .issuer("http://auth-server:9000")
.build();
}
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> resourceServerAccessTokenCustomizer() {
return new ClientCredentialsFlowTokenCustomizer();
}
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
grantedAuthoritiesConverter.setAuthorityPrefix("");
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
@Bean
public JwtIssuer jwtIssuer(@Value("${iam.jwt.expiry-seconds}") long expirySeconds, @Value("${iam.jwt.issuer}") String issuer, KeyPair keyPair) {
return new JwtIssuer(expirySeconds, issuer, jwtEncoder(jwkSource(keyPair)));
}
}
I am using spring boot 3.1.4 and spring security specific dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
This is my setting for Keycloak which works:
authentication:
- name: oidc
oidc:
clientID: kubernetes
clientSecret: 148df4eba82f4d87b9fa85b641631d56
groupsClaim: groups
enableAccessToken: true
issuerURI: https://keycloak.optime.ai/realms/master
kubectlRedirectURI: http://localhost:10000/callback
scopes: openid profile email
userClaim: email
I don't know how it should like for custom OAuth2 Authorization Server/OIDC provider?