At the moment if you do not configure jwt() (or opaqueToken() on the master branch) the configurer will throw an IllegalStateException.
However, all that is needed to support another format is to add an AuthenticationProvider supporting BearerTokenAuthenticationToken. Because the configurer throws, it forces you to manually configure everything else yourself as well.
Instead, it should either allow setting a specific provider, or warn instead of throwing if it cannot configure one.
Related: #6209
Comment From: jzheaux
@OrangeDog, thanks for the report! Can you describe a bit more what you are trying to accomplish?
Also, feel free to keep track of https://github.com/spring-projects/spring-security/pull/6563, which adds an AuthenticationManagerResolver. While still early, it may be of interest to you.
Comment From: OrangeDog
I want everything that it sets up (bearer token filter, endpoint, error handler) except for JWT. The only way to do it is to scrap the whole configurer and do everything manually.
It would be easily solved if it just didn't throw the IllegalStateException.
Comment From: jzheaux
it should either allow setting a specific provider
What token format are you using for your bearer token? If it's neither opaque nor JWT, then I wonder if what you are building is a Resource Server.
One thing that might work is to introduce something like the following:
http
.oauth2ResourceServer()
.jwt()
.authenticationManager(new MyCustomAuthenticationManager())
And the same for opaque().
Then, you can pick whichever semantics - local or remote - best matches your situation.
Comment From: OrangeDog
Why do it like that when neither JWT nor a whole AuthenticationManager are required?
All I need is this to not throw.
public void configure(HttpSecurity http) {
http.oauth2ResourceServer();
}
public void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(new MyTokenAuthProvider())
}
Comment From: jzheaux
a whole AuthenticationManager
I think you hit here on some API complexity that Spring Security doesn't need. Why have both AuthenticationManager and AuthenticationProvider in the first place when they both have the same contract? To me, they are largely the same thing -- you'll notice in the reactive stack that we have ReactiveAuthenticationManager, but no ReactiveAuthenticationProvider for that reason.
Given the example above, if you need to use a provider instead, you could do:
http
.oauth2ResourceServer()
.jwt()
.authenticationManager(provider()::authenticate)
Is that unreasonable? If so, help me understand your situation better.
UPDATE: Really, though, I'm curious why you need an AuthenticationProvider as AuthenticationManager is a simpler interface to implement, having only one method on its interface while AuthenticationProvider has two. Really, it's more complex applications that need something fine-grained like an AuthenticationProvider. I'd be interested in hearing if your scenario requires more than one AuthenticationProvider to handle bearer tokens.
All I need is this to not throw.
The reason oauth2ResourceServer() throws an exception is because Spring Security only supports JWT-based and Opaque token-based Resource Servers at this point. The semantics around non-JWT, non-Opaque token Resource Servers aren't clear.
It wouldn't be very secure for the DSL to give the impression that you're getting all the security benefits of OAuth 2.0 Resource Server when you really aren't.
neither JWT ... are required
I think some good questions to ask are: Is the application a Resource Server? And if so, what token format does it use? I'd say that if it's neither JWT nor Opaque token, then it's not a Resource Server. Instead, it's a server that uses the Bearer keyword in its Authorization header.
And if that's what you need to configure, then Spring Security makes that entirely possible with relatively little code, there's just not a specialized DSL for that setup.
If what I've said above sounds unreasonable, maybe a boarder picture would help me out. Have I missed something about your situation?
Comment From: OrangeDog
why you need an
AuthenticationProvider
Because that's how your code works. The configurer calls http.getSharedObject(AuthenticationManager.class). That manager is configured via the adaptor's configure(AuthenticationManagerBuilder auth) method, where you can add AuthenticationProviders to it.
There is no .jwt().authenticationManager() for MVC.
if it's neither JWT nor Opaque token, then it's not a Resource Server
Nothing in OAuth2 requires a Bearer Token to have any particular implementation, and a bearer token is by definition opaque. Neither does anything require a Resource Server to use a specific token implementation.
Moreover, the implementation of "Opaque" tokens currently in master is very narrow, mandating that the token be used in an HTTP request that returns its details.
I just want short, stateful tokens, but I also want a full resource server with everything else that .oauth2ResourceServer() provides. The current solution is to copy-paste OAuth2ResourceServerConfigurer and make it not throw if JWT isn't configured.
It wouldn't be very secure for the DSL to give the impression that you're getting all the security benefits of OAuth 2.0 Resource Server when you really aren't.
As far as I can see it is perfectly secure to just skip the JWT configuration. This results in it being impossible to authenticate any bearer tokens. Unless for example you've added a different AuthenticationProvider<BearerTokenAuthenticationToken>.
Comment From: OrangeDog
Now I've started using @EnableAuthorizationServer it the situation looks even more silly.
Via the provided TokenStore implementations you can persist access tokens in memory, JDBC and Redis but it's not possible to use any of them in your .oauth2ResourceServer().
Comment From: jzheaux
The current solution is to copy-paste OAuth2ResourceServerConfigurer and make it not throw if JWT isn't configured.
@OrangeDog could you provide a sample of what you can't do without copying OAuth2ResourceServerConfigurer?
Because that's how your code works
You and I are both proposing ways to change the code from its current state. My question is in the context of our conversation here, which is where you'd provide an authenticationManager via the DSL:
http
.oauth2ResourceServer()
.jwt()
.authenticationManager(...)
or, as you said, if your token is opaque, then
http
.oauth2ResourceServer()
.opaqueToken()
.authenticationManager(...)
Would you still need an AuthenticationProvider in that case? Is there something about the nature of your application that requires you to have more than one authentication provider within the same authentication manager?
Moreover, the implementation of "Opaque" tokens currently in master is very narrow, mandating that the token be used in an HTTP request that returns its details.
I'm open to discussion on this, too, but let's do that in another ticket just to keep this conversation focused. Since 5.2 is not yet released, there's a lot of breathing room, and it's community feedback that's going to make sure these features address real needs.
Comment From: OrangeDog
I've just stopped using this completely now. The resource server configuration in spring-security-oauth2 provides far more functionality, far more flexibly, so I'm using that.
Comment From: jzheaux
@OrangeDog I'm glad that you found something that addresses your needs. Sorry that we couldn't find a solution together.
Just in case it's not clear, note that spring-security-oauth2 is in maintenance mode. We still support it as a team, though it's soon to be marked for deprecation.
Comment From: OrangeDog
I see. I would suggest you ensure there is equivalent functionality (or at least the extension points) in core before doing that.
So far (on the resource server side) I've found the TokenStore and OAuth2SecurityExpressionMethods useful, though I am developing in a quite exploratory manner as new needs arise. The TokenStore also provides an opportunity to consistently customise the OAuth2Authentication implementation (changing the authorities and principal). I know one case will be also allowing session-based access to the resource server to simplify first-party API usage (it looks like ResourceServerSecurityConfigurer.stateless(false) covers this).
On the auth server side I'm using most features including custom approvals, persistent and revocable refresh tokens, different stores for different token types. User authentication is via other providers (currently SAML) so password grants are not available to me, which complicates matters.
Comment From: jzheaux
I would suggest you ensure there is equivalent functionality (or at least the extension points) in core before doing that.
Yes, we'll officially deprecate once we have feature parity. The extension points may change, of course.
Thanks for the feedback about what you are using in spring-security-oauth2. Feel free to continue to reach out on what is working for you.
Some quick notes about that:
The
TokenStorealso provides an opportunity to consistently customize theOAuth2Authentication... I know one case will be also allowing session-based access
These are both possible in the new JWT support via a custom authentication token. Opaque Token support for the same is coming in 5.2. In jwt(), you'd do:
http
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(this::convertJwtToCustomAuthenticationToken)
If an Authentication implementation is annotated with @Transient, then it won't be persisted in the session. Otherwise, it will be.
At this point, I'll close this ticket, but feel free to reopen if you'd like to further discuss ways to provide a custom AuthenticationManager via something like:
http
.oauth2ResourceServer()
.jwt()
.authenticationManager(...)
which I'm still thinking is quite similar to what you were originally thinking (allowing for a custom authentication provider) aside from the discussion about exceptions.