Search code examples
springspring-securityoauth-2.0jwtaudit

AuthenticationEvent is not published on error while decoding jwt


I can't catch authentication failure event on error occurred while attempting to decode the Jwt due to my validator failed. I'm using Spring Security 5.2.1. Please note that I do catch authorization failure event when I do not pass token in 'Authorization' header at all. I guess some additional config has to be done with spring configuration.

Thrown exception:

org.springframework.security.oauth2.core.OAuth2AuthenticationException: An 
error occurred while attempting to decode the Jwt: This aud claim does not 
contain configured audience

Audit is implemented as described here: https://www.baeldung.com/spring-boot-authentication-audit

Current spring security config:

@EnableWebSecurity
  public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

private static final OAuth2Error INVALID_AUDIENCE =
        new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,
                "This aud claim does not contain configured audience",
                "https://tools.ietf.org/html/rfc6750#section-3.1");


@Value("${spring.security.oauth2.claim-to-validate.audience}")
private String audience;

@Value("${spring.security.oauth2.claim-to-validate.scope}")
private String scope;

@Value("${spring.security.oauth2.resourceserver.jwt.public-key-location:#{null}}")
private RSAPublicKey publicKeyLocation;

@Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri:#{null}}")
private String jwkSetUri;

@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri:#{null}}")
private String issuerUri;


@Override
protected void configure(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests()
                    .antMatchers( "/v1/resource/**")
                    .hasAuthority("SCOPE_" + scope)
                    .and()
                    .oauth2ResourceServer()
                    .jwt();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication();
}

@Bean
public JwtDecoder jwtDecoder() {
    final OAuth2TokenValidator<Jwt> withAudience = audienceValidator(audience);

    final JwtDecoder jwtDecoder;

    if (publicKeyLocation != null) {
        jwtDecoder = NimbusJwtDecoder.withPublicKey(publicKeyLocation).build();
    } else if (StringUtils.hasLength(jwkSetUri)) {
        jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
    } else if (StringUtils.hasLength(issuerUri)) {
        jwtDecoder = JwtDecoders.fromOidcIssuerLocation(issuerUri);
    } else {
        throw new IllegalStateException(
                "Invalid OAuth2 configuration: provide value for any of " +
                        "'publicKeyLocation', 'jwkSetUri' or 'issuerUri'");
    }

    ((NimbusJwtDecoder) jwtDecoder).setJwtValidator(withAudience);

    return jwtDecoder;
}

OAuth2TokenValidator<Jwt> audienceValidator(String audience) {
    return jwt -> {
        Assert.notNull(jwt, "token cannot be null");

        final List<String> audiences = jwt.getAudience();

        return audiences.contains(audience) ?
                OAuth2TokenValidatorResult.success() :
                OAuth2TokenValidatorResult.failure(INVALID_AUDIENCE);
    };
}
}

Solution

  • Update Spring Security to 5.3.0 or higher and declare in Spring Security Configuration custom AuthenticationEventPublisher bean like here:

    @Autowired
    private ApplicationEventPublisher publisher;
    
    @Bean
    public AuthenticationEventPublisher authenticationEventPublisher() {
        final Properties properties = new Properties();
        properties.put(
            OAuth2AuthenticationException.class.getCanonicalName(),
            AuthenticationFailureBadCredentialsEvent.class.getCanonicalName());
    
        final DefaultAuthenticationEventPublisher eventPublisher = new DefaultAuthenticationEventPublisher(publisher);
    
        eventPublisher.setAdditionalExceptionMappings(properties);
    
        return eventPublisher;
    }
    

    Please note that in 5.3.0 you can directly add mappings without Properties structure.

    If you need to keep on 5.2.x then use workaround pointed here: https://github.com/spring-projects/spring-security/issues/7793