Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

spring jwt decoder openid token

External OAuth2 Provider doesn't have public JwkUri, so I tried too override default behavior using following code snippet:

@EnableWebSecurity
public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
 @Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .authorizeRequests()
            .antMatchers("**/oauth2/code/esia**", "**/code/esia**", "**esia**").permitAll()
            .antMatchers("/user").fullyAuthenticated()
            .anyRequest().authenticated()
            .and()
            .csrf().disable()
            .cors().disable()
            .oauth2Client()
            .clientRegistrationRepository(this.clientRegistrationRepository)
            .authorizationCodeGrant()
            .authorizationRequestResolver(new CustomAuthorizationRequestResolver(
                    this.clientRegistrationRepository, esiaConfig, signatureUtil, timeUtil))
            .accessTokenResponseClient(customAccessTokenResponseClient())
            .and().and().oauth2Login().tokenEndpoint().accessTokenResponseClient(customAccessTokenResponseClient())
            .and().and().oauth2ResourceServer().jwt();
}
@Bean
JwtDecoder jwtDecoder() {
 return  new CustomJwtDecoder();
}
}

class CustomJwtDecoder implements JwtDecoder {
@Override
public Jwt decode(String token) throws JwtException {
    System.out.println(token);
    return null;
}
}

However Spring Security somehow still uses default realization and I am getting the following error...

[missing_signature_verifier] Failed to find a Signature Verifier for Client Registration: 'esia'. Check to ensure you have configured the JwkSet URI.

Also, I tried to set custom AuthenticationProvider but spring ignores it.

I guess the catch is that spring`s OAuth2LoginConfigurer method init(B http) calls new OidcAuthorizationCodeAuthenticationProvider(accessTokenResponseClient, oidcUserService)

like image 650
Unrealman1 Unrealman1 Avatar asked Jan 28 '26 15:01

Unrealman1 Unrealman1


2 Answers

I was facing the same issue even with 5.2.x release. In my case, the real problem was not in the JwtDecoder. I have fixed the issue by setting the jwk-set-uri property (you can change the provider name by the provider which you are using e.g okta, google etc.) :

security.oauth2.client.provider.azure.jwk-set-uri: https://login.microsoftonline.com/{tenant}/discovery/keys

like image 109
user3325637 Avatar answered Jan 31 '26 01:01

user3325637


For 5.1.3.RELEASE it looks like you cannot get around this problem easily.

It stems from the OidcAuthorizationCodeAuthenticationProvider.getJwtDecoder

This happens in line 156 which is a call to a private method

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        ...
        OidcIdToken idToken = createOidcToken(clientRegistration, accessTokenResponse);
        ...
    }

The one option I see is if you make a copy of this code, and modify it yourself. Extending the class itself is not meaningful because all of the logic happens pretty much in the authenticate method. So you're still overriding it. then you add your provider using the http.authenticationProvider method

Another option is to override the SecurityConfigurerAdapter.postProcess method of the OAuth2LoginConfigurer class and do something clever there. Like populating the JWT decoder map through reflection.

Neither are admittedly preferred solutions. I believe that's why the refactoring happened for the 5.2 release.

Given the latest 5.2.x release then You're almost there, but you must override the correct bean

    @Bean
    public JwtDecoderFactory<ClientRegistration> jwtDecoderFactory() {
        final JwtDecoder decoder = jwtDecoder();
        return context -> decoder;

    }

and if you don't want to use lambdas

    @Bean
    public JwtDecoderFactory<ClientRegistration> jwtDecoderFactory() {
        final JwtDecoder decoder = jwtDecoder();
        return new JwtDecoderFactory<ClientRegistration>() {
            @Override
            public JwtDecoder createDecoder(ClientRegistration context) {
                return decoder;
            }
        };

    }

How did I figure this out, well I took a look at the OAuth2LoginConfigurer.java class which does

    JwtDecoderFactory<ClientRegistration> jwtDecoderFactory = getJwtDecoderFactoryBean();

and the private method that fetches the bean look like this

    private JwtDecoderFactory<ClientRegistration> getJwtDecoderFactoryBean() {
        ResolvableType type = ResolvableType.forClassWithGenerics(JwtDecoderFactory.class, ClientRegistration.class);
        String[] names = this.getBuilder().getSharedObject(ApplicationContext.class).getBeanNamesForType(type);
        if (names.length > 1) {
            throw new NoUniqueBeanDefinitionException(type, names);
        }
        if (names.length == 1) {
            return (JwtDecoderFactory<ClientRegistration>) this.getBuilder().getSharedObject(ApplicationContext.class).getBean(names[0]);
        }
        return null;
    }
like image 40
Filip Hanik VMware Avatar answered Jan 31 '26 00:01

Filip Hanik VMware