Describe the bug
If "spring.security.oauth2.resourceserver.jwt.jwk-set-uri" fails to retrieve remote JWK set due to UnknownHostException, AuthenticationServiceException is thrown correctly. Exception handling is delegated from BearerTokenAuthenticationFilter#doFilterInternal to this.authenticationFailureHandler.onAuthenticationFailure(request, response, failed);
Instead of delegating exception to authenticationEntryPoint for safe handling exception is re-thrown instead causing unhandled exception.
private AuthenticationFailureHandler authenticationFailureHandler = (request, response, exception) -> {
if (exception instanceof AuthenticationServiceException) {
throw exception;
}
this.authenticationEntryPoint.commence(request, response, exception);
};
To Reproduce Fail to retrieve JWK set during jwt oauth authentication due to UnknownHostException.
Expected behavior Delegate handling to authenticationEntryPoint in every situation, remove if statement completely
Comment From: jzheaux
Hi, @jukkaleh. Thanks for the suggestion. Unfortunately, we cannot do this as it would regress #9395.
I guess I'm not clear on what problem this is causing. Can you elaborate?
In the meantime, you can configure your own AuthenticationFailureHandler that does as you describe.
Comment From: jukkaleh
@jzheaux As context we are using spring remoting which requires us to do exception handling in a safe way. Regardless problem does also apply to spring web, being an exception which cannot be handled. AuthenticationServiceException causes spring boot server to respond with http 500 internal server error. With remoting you cannot return http 500 and expect client to deal with it (It can't, only RemoteInvocationResult's are expected)
.and()
.oauth2ResourceServer()
.authenticationEntryPoint(customAuthenticationEntryPoint())
.jwt()
.jwtAuthenticationConverter(customjwtAuthenticationConverter());
We have configured our own authenticationEntryPoint to deal with all exceptions by using entry point. Problem being it is never called when AuthenticationServiceException occurs. #9395 Can still be satisfied if the default authenticationEntryPoint would re-throw exception instead of the filter. We just need some way of handle this exception in our own code. Also in #9395 you commented that this can be overriden by providing custom authenticationEntryPoint now in this filter you are expected to override authenticationEntryPoint and FailureHandler. Why failureHandler even exists in the first place when it its only delegating calls to authenticationEntryPoint
You are suggesting overriding FailureHandler, but quick look at the code left me confused if its actually being overridden.
authenticationEntryPoint certaintly is, but setAuthenticationFailureHandler is not called from the same place as setAuthenticationEntryPoint
.and()
.oauth2ResourceServer()
.authenticationFailureHandler(customAuthenticationFailureHandler())
.authenticationEntryPoint(customAuthenticationEntryPoint())
.jwt()
.jwtAuthenticationConverter(customjwtAuthenticationConverter());
Comment From: jzheaux
You are suggesting overriding FailureHandler, but quick look at the code left me confused if its actually being overridden.
Not all fields are exposed via the DSL. You can adjust the filter using a post-processor:
http
.oauth2ResourceServer()
.withObjectPostProcessor(new ObjectPostProcessor<BearerTokenAuthenticationFilter>() {
@Override
public <O extends BearerTokenAuthenticationFilter> O postProcess(O object) {
object.setAuthenticationFailureHandler(failureHandler);
return object;
}})
As context we are using spring remoting which requires us to do exception handling in a safe way. Regardless problem does also apply to spring web, being an exception which cannot be handled. AuthenticationServiceException causes spring boot server to respond with http 500 internal server error.
I think what I'm missing is why not address the configuration issue (e.g. correct the url)? The application fundamentally cannot work with incorrect configuration.
With remoting you cannot return http 500 and expect client to deal with it
There is never an expectation that a client deal with a 500 error. A 500 error means it's a problem for the server to solve, which is the case here. Semantically, a 500 error is the correct response.
Can still be satisfied if the default authenticationEntryPoint would re-throw exception instead of the filter.
The entry point is about commencing authentication, not about handling authentication failures. Given that, it would seem odd to me to have the entry point throw an exception related to authentication failure.
Either way, I'm not certain we can do this at this point since it would change the behavior of those already overriding the authentication entry point. I'd prefer things to remain backward compatible, especially when a workaround is available.
Why failureHandler even exists in the first place when it its only delegating calls to authenticationEntryPoint
See https://github.com/spring-projects/spring-security/issues/7009. This is a question you can answer by doing a git blame on the file.
I think I have enough information at this point to decline the suggestion, so I'm going to close this issue. However, please feel free to keep the conversation going as needed.
Comment From: jukkaleh
I think what I'm missing is why not address the configuration issue (e.g. correct the url)?
Initially we had a mysterious http 500 internal server error which did not show up in any of the logs. This was weird because we log every unhandled exception so that in the future it won't cause unhandled error. Error ended up being found on console (stdout), it had escaped logback logging somehow. This led me to investigate and found that this filter throws Exception without anything handling it.
The application fundamentally cannot work with incorrect configuration.
Configuration was correct, this exception will be thrown even if the service is temporarily down
The entry point is about commencing authentication, not about handling authentication failures.
System can have multiple backup login options if one of the primary fails, you instruct client to use backup solution. Every other exception are being passed to entrypoint why AuthenticationServiceException is any different?
Anyway thanks for the tip on how to to override failure Handler, will definitely put it to good use!
Comment From: jzheaux
@jukkaleh, good points.
Initially we had a mysterious http 500 internal server error which did not show up in any of the logs.
I see, I'm sorry to hear that. For future reference, Spring Security has trace logging that can help. In the situation you described, this log emits at the trace level.
Every other exception are being passed to entrypoint why AuthenticationServiceException is any different?
To ensure the appropriate HTTP response. Any other AuthenticationException means authentication should commence. AuthenticationServiceException means authentication cannot commence due to a system problem. AuthenticationEntryPoint is only called to commence authentication.
Comment From: jukkaleh
@jzheaux
To ensure the appropriate HTTP response.
This is tad problematic statement as it assumes that Servlets have common HTTP response behaviour. It is reasonable default but not technology-agnostic approach, spring security is not just used to protect rest-apis and therefore common rest-api behaviour should not dictate how spring security works internally. For example this approach is not compatible with spring-remoting or other exotic ways of communication. During enterprise migration to spring security you come across things that would work well if they weren't hardcoded to one use case ( #10464 is relatated). For every modification to spring security, I open an issue here. For one so that I could delete my custom copy-pasta filter code from codebase and second that it helps others having to do the same work.
My test for being technology-agnostic is two questions. 1) Can you communicate to server using smoke signals. This forces to separation between input and business logic. Business logic doesn't know how the data got in and it doesn't care how it goes out. You can have RestController or smoke signal analyzer as your input layer and share same Service-layer.
2) Can you secure all endpoints Smoke signal may have bearer token inside it. Can I extract it and secure application using spring security. For most part I would answer yes. I would need to customize Filter, AuthenticationProvider and UserDetailsProvider but doable. However my campfire used to send responses cannot deal with unhandled HTTP 500 as it would need be able to go though exception translation beforehand.
AuthenticationServiceException means authentication cannot commence due to a system problem.
There are two Exceptions: AuthenticationServiceException and InternalAuthenticationServiceException one indicates problem with external system and another internal. Which one prevents authentication due to a system problem? My database has a fallback address if the primary one is not available and spring boot works just fine with primary down