How to configure Eclipse Che with custom OAuth2 Authorization Server/OIDC provider?

129 views Asked by At

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?

0

There are 0 answers