ch4mpy / spring-addons

Ease spring OAuth2 resource-servers configuration and testing
Apache License 2.0
521 stars 84 forks source link

(Not a bug)Why the custom JwtDecoder bean is useless #189

Closed spkingr closed 6 months ago

spkingr commented 6 months ago

Describe the bug

I have a resource server and client springboot 3 project with spring security 6, and the KeyCloak 23.0.6 as the authorization server, the code is simple and can run with login/logout without problem, but after login with OAuth2, the authentication token lacks of custom roles which I setup in KeyCloak console, I can see them in the original JWT token when decoded:

"resource_access": {
    "myclient": {
      "roles": [ "myrole-client-admin" ]
    },
...

So I created the custom JwtDecoder or other custom claims parser related configurations or beans including these below:

class UsernameSubClaimAdapter implements Converter<Map<String, Object>, Map<String, Object>>{}
// or
Converter<Jwt, ? extends AbstractAuthenticationToken> converter() {...}
http.oauth2ResourceServer(s -> s.jwt(jwt -> jwt.decoder(jwtDecoder)
    .jwtAuthenticationConverter(converter()));

But all the code fails without doubt running in my web browser test, all the authentication results I can get are filled with just the default roles like: OIDC_USER, SCOPE_email, SCOPE_openid and so on, no custom ones within the resource_access.{custom_client}.roles field specified.

I found your answer in stackflow: https://stackoverflow.com/questions/58205510/spring-security-mapping-oauth2-claims-with-roles-to-secure-resource-server-endp, and find your repo here to look for your help, hope you can give me some ideas, thanks in advance!

Code sample

The main code:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig_Bak {
    @Value("${keycloak.jwk-set-uri}")
    private String jwkUri;

    // @Bean // no usage at all, spring will create a new one in OidcIdTokenDecoderFactory.createDecoder method
    public JwtDecoder jwtDecoder() {
        return JwtDecoders.fromOidcIssuerLocation(jwkUri);
    }

    // @Bean // not work neither
    public JwtDecoder jwtDecoder2() {
        NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkUri).build();
        jwtDecoder.setClaimSetConverter(claims -> {
            System.out.println("---------------------------just a test, no invocation at all...");
            MappedJwtClaimSetConverter delegate = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());
            return delegate.convert(claims);
        });
        // jwtDecoder.setClaimSetConverter(new SecurityConfig.UsernameSubClaimAdapter());
        return jwtDecoder;
    }

    @Bean
    public SecurityFilterChain chain(HttpSecurity http) throws Exception {
        return http.csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(r -> r.anyRequest().authenticated())
                .oauth2Login(Customizer.withDefaults())
                .formLogin(AbstractHttpConfigurer::disable)
                .build();
    }

    // convert realm_access.roles or resource_access.{client_id}.roles claims to roles/authorities
    public Converter<Jwt, ? extends AbstractAuthenticationToken> converter() {
        var converter = new JwtAuthenticationConverter();
        converter.setJwtGrantedAuthoritiesConverter(jwt -> {
            System.out.println("---------------------------just a test, no invocation at all...");
            return List.of(new SimpleGrantedAuthority("ROLE_test"));
        });
        return converter;
    }
}

Thanks again!

ch4mpy commented 6 months ago

Apparently, you're not using spring-addons-starter-oidc and haven't read the tutorials. This is an issue tracker for spring-addons, not a forum. I answered this problem many times on Stackoverflow already, go there

spkingr commented 6 months ago

Apparently, you're not using spring-addons-starter-oidc and haven't read the tutorials. This is an issue tracker for spring-addons, not a forum. I answered this problem many times on Stackoverflow already, go there

Sorry, and that's the greatest and much detailed tutorial i ever seen about OAuth2 + Spring, thanks!