Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow configuring oauth2ResourceServer() with custom provider #6629

Closed
OrangeDog opened this issue Mar 20, 2019 · 12 comments
Closed

Allow configuring oauth2ResourceServer() with custom provider #6629

OrangeDog opened this issue Mar 20, 2019 · 12 comments
Assignees

Comments

@OrangeDog
Copy link
Contributor

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

@jzheaux
Copy link
Contributor

jzheaux commented Mar 21, 2019

@OrangeDog, thanks for the report! Can you describe a bit more what you are trying to accomplish?

Also, feel free to keep track of #6563, which adds an AuthenticationManagerResolver. While still early, it may be of interest to you.

@OrangeDog
Copy link
Contributor Author

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.

@jzheaux
Copy link
Contributor

jzheaux commented Mar 22, 2019

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.

@OrangeDog
Copy link
Contributor Author

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())
}

@jzheaux
Copy link
Contributor

jzheaux commented Mar 22, 2019

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?

@jzheaux jzheaux added the status: waiting-for-feedback We need additional information before we can continue label Apr 5, 2019
@OrangeDog
Copy link
Contributor Author

OrangeDog commented Apr 11, 2019

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>.

@OrangeDog
Copy link
Contributor Author

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().

@jzheaux
Copy link
Contributor

jzheaux commented Apr 30, 2019

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.

@OrangeDog
Copy link
Contributor Author

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.

@jzheaux
Copy link
Contributor

jzheaux commented May 1, 2019

@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.

@OrangeDog
Copy link
Contributor Author

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.

@jzheaux
Copy link
Contributor

jzheaux commented May 1, 2019

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 TokenStore also provides an opportunity to consistently customize the OAuth2Authentication ...
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.

@jzheaux jzheaux closed this as completed May 1, 2019
@rwinch rwinch removed the status: waiting-for-feedback We need additional information before we can continue label Dec 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants