Summary

Error "invalid_token_response" don't emit a corresponding ApplicationEvent.

In my specific case, the application failed to obtain an OAuth2 token and I have the follow exception: [invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: 401 : [no body].

The exception obviously is correct. The real problem is being unable to register the log for such critical situation. The exception is clearly correct. The real problem is not being able to log this critical situation.

Actual Behavior

When using an application that configures an OAuth 2.0 client and there are some situations where the application failed to obtain an OAuth2 token I'd like to trigger a listener (ApplicationListener) to log those failures.

One of those situations is the configuration of a client-secret with wrong value. "spring.security.oauth2.client.registration.uaa.client-secret". With the stacktrace below we are able to trace the class ClientCredentialsOAuth2AuthorizedClientProvider that throws an error

org.springframework.security.oauth2.client.ClientAuthorizationException: [invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: 401 : [no body]
    at org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider.getTokenResponse(ClientCredentialsOAuth2AuthorizedClientProvider.java:96) ~[spring-security-oauth2-client-5.4.6.jar:5.4.6]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ Request to GET http://localhost:8080/testingEvent [DefaultWebClient]
Stack trace:
        at org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider.getTokenResponse(ClientCredentialsOAuth2AuthorizedClientProvider.java:96) ~[spring-security-oauth2-client-5.4.6.jar:5.4.6]
        at org.springframework.security.oauth2.client.ClientCredentialsOAuth2AuthorizedClientProvider.authorize(ClientCredentialsOAuth2AuthorizedClientProvider.java:85) ~[spring-security-oauth2-client-5.4.6.jar:5.4.6]
        at org.springframework.security.oauth2.client.DelegatingOAuth2AuthorizedClientProvider.authorize(DelegatingOAuth2AuthorizedClientProvider.java:71) ~[spring-security-oauth2-client-5.4.6.jar:5.4.6]
        at org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager.authorize(DefaultOAuth2AuthorizedClientManager.java:176) ~[spring-security-oauth2-client-5.4.6.jar:5.4.6]
        at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.lambda$authorizeClient$24(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:552) ~[spring-security-oauth2-client-5.4.6.jar:5.4.6]
        at reactor.core.publisher.MonoSupplier.call(MonoSupplier.java:85) ~[reactor-core-3.4.5.jar:3.4.5]
        at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:227) ~[reactor-core-3.4.5.jar:3.4.5]
...

Expected Behavior

The expected behavior in that case would be the class ClientCredentialsOAuth2AuthorizedClientProvider (or DefaultClientCredentialsTokenResponseClient) using a DefaultAuthenticationEventPublisher to publish an event and taking action if necessary.

Configuration

Dependencies:

Spring Boot 2.4.5
org.springframework.security:spring-security-core:5.4.6
org.springframework.security:spring-security-oauth2-client:5.4.6

Configuration for Auth Server in my application.yml:

spring:
  application:
    name: my-app
  jmx:
    enabled: false
    default-domain: my-app
  security:
    user:
      name: myuser
      password: mypwd
    oauth2:
      client:
        provider:
          uaa:
            token-uri: ${baseUrl.uaa}/oauth/token
            authorization-uri: ${baseUrl.uaa}/oauth/authorize
            jwk-set-uri: ${baseUrl.uaa}/token_keys
        registration:
          uaa:
            provider: uaa
            client-id: my-clientcredentials
            client-secret: WRONG-SECRET
            authorization-grant-type: client_credentials

Comment From: jgrandja

@andrecampanini

The expected behavior in that case would be the class ClientCredentialsOAuth2AuthorizedClientProvider (or DefaultClientCredentialsTokenResponseClient) using a DefaultAuthenticationEventPublisher to publish an event and taking action if necessary.

It doesn't make sense to add a DefaultAuthenticationEventPublisher to ClientCredentialsOAuth2AuthorizedClientProvider or DefaultClientCredentialsTokenResponseClient. It might make sense to add it to the high-level component OAuth2AuthorizedClientManager but I'm not sure. Are you using OAuth2AuthorizedClientManager or directly using ClientCredentialsOAuth2AuthorizedClientProvider?

Comment From: andrecampanini

I am not directly using ClientCredentialsOAuth2AuthorizedClientProvider.

This is the class that is used depending on the grant-type configuration in Spring Security / Spring Boot. So yes, we use OAuth2AuthorizedClientManager (DefaultOAuth2AuthorizedClientManager according to my stack trace).

Thank you.

Comment From: jgrandja

@andrecampanini Take a look at the reference for OAuth2AuthorizedClientManager / OAuth2AuthorizedClientProvider.

The OAuth2AuthorizationFailureHandler handles authorization failures so this is the extension point for applications to configure handling scenarios, e.g. publish event, log, etc. You can supply a custom OAuth2AuthorizationFailureHandler via DefaultOAuth2AuthorizedClientManager.setAuthorizationFailureHandler().

I'm going to close this as applications should supply their own custom behaviour for handling authorization failures.