Skip to content

Adding client credentials authorization filter - Still In Progress #63

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

Closed

Conversation

paurav-munshi
Copy link
Contributor

@paurav-munshi paurav-munshi commented Apr 23, 2020

Closes #5

  • Adding a templated OAuth filter which performs following actions

    • Identifying if Filter will support an incoming request using RequestMatcher
    • Validating the request using RequestValidator
    • Converting the request to Authentication using AuthenticationConverter
    • Performing the authenticaion using supplied AuthenticaionManager
  • Adding a configuration which will build the templated OAuth filter to
    work as Client Credentials filter using custom validators, converters,
    matchers and auth managers

- Adding a templated OAuth filter which performs following actions
	- Identifying if support a request using RequestMatcher
	- Validating the request using RequestValidator
	- Converting the request to Authentication using
AuthenticationConverter
	- Performing the authenticaion using supplied AuthenticaionManager

- Adding a configuration which will build the templated OAuth filter to
work as Client Credentials filter using custom validators, converters,
matchers and auth managers
@pivotal-issuemaster
Copy link

@paurav-munshi Please sign the Contributor License Agreement!

Click here to manually synchronize the status of this Pull Request.

See the FAQ for frequently asked questions.

@pivotal-issuemaster
Copy link

@paurav-munshi Thank you for signing the Contributor License Agreement!

Comment on lines +31 to +34
http.authorizeRequests()
.antMatchers("/token").permitAll();

http.addFilterBefore(clientCredentialsFilter, BasicAuthenticationFilter.class);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are not validating the client credentials yourself which leaves me to the conclusion that you are relying on the BasicAuthenticationFilter from the spring-security-filterchain.
This is actually a problem. RFC6749 defines an additional operation to the basic authenticaton scheme that is not directly mentioned in RFC7617 (Basic Auth Scheme). This causes problems with libraries like nimbus that do honor this additional operation.
https://tools.ietf.org/html/rfc6749#section-2.3.1
Simplified, if a client uses the basic authentication scheme the client_id parameter and the client_secret must be url-encoded. the BasicAuthenticationFilter of spring does not decode such authentication details and thus the authentication will fail.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I am not relying on the BasicAuthorizationFilter in my flow. The configuration of the client credentials filter that I have developed could be seen in the AuthorizationFilterConfigurations.java in my PR. But for extracting the client id and secret from authorization header, I am using BasicAuthenticationConverter which I can replace with a custom class and inject.

As far as the basic auth is concerned, current implementation will extract the authorization header, make sure its Basic, then decode the base64 encoded string and split the result by colon to obtain client id and secret.

Now for the missing piece, do you mean that client id and secret obtained from header should further be url-decoded or do you mean that client id and secret passed in request body should be url-decoded ? If you mean the latter then that is not yet developed. I am a bit confused because the spec link you mentiond specifies application/x-www-form-urlencoded which is more relevant to content of the body rather the header (but correct me if I am wrong).

Once that is clear I can make changes accordingly.

Thanks for your inputs.

Paurav.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean the base64 decoded client_id and client_secret from the header. I recently made the experience that if this is not done it might come to a clash with the nimbus library. As pointed out in section 2.3.1 of RFC6749 the client_id and client_secret must be encoded.

Clients in possession of a client password MAY use the HTTP Basic
authentication scheme as defined in [RFC2617] to authenticate with
the authorization server. The client identifier is encoded using the
"application/x-www-form-urlencoded" encoding algorithm per
Appendix B, and the encoded value is used as the username; the client
password is encoded using the same algorithm and used as the
password.

and then from https://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#h-17.13.4.1

application/x-www-form-urlencoded

This is the default content type. Forms submitted with this content type must be encoded as follows:

  1. Control names and values are escaped. Space characters are replaced by +', and then reserved characters are escaped as described in [RFC1738], section 2.2: Non-alphanumeric characters are replaced by %HH', a percent sign and two hexadecimal digits representing the ASCII code of the character. Line breaks are represented as "CR LF" pairs (i.e., `%0D%0A').
  2. The control names/values are listed in the order they appear in the document. The name is separated from the value by =' and name/value pairs are separated from each other by &'.

at least the nimbus developers implemented it this way and I had a short discussion with them about this topic and agreed eventually on their solution

Copy link
Collaborator

@jgrandja jgrandja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @paurav-munshi. There is code in this PR that is not related to the issue we need to address. This issue simply addresses client authentication. So the minimal amount of code to achieve this is Filter, Authentication and AuthenticationProvider.

Take a look at #39. You will see the implementation should include OAuth2ClientAuthenticationFilter creates OAuth2ClientAuthenticationToken and passes to OAuth2ClientAuthenticationProvider. The Filter should encapsulate the request matching and credentials parsing logic - we will expose public API's for allowing customizations in a later story/issue. The plan is to add the minimal amount of code for each iteration to get things working. We will iterate a few times to add additional functionality as we build things out.

I'm going to close this PR based on my comments, as well, another PR has been previously submitted for this as per comment.

}

@Bean
public UserDetailsService clientCredentialsUserDetailsService() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UserDetailsService will not be used for Client Registrations. The UserDetailsService is really meant for the user database. See #40

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main intention was to have an authentication manager in the filter. But to provide a temporary function which could work and store client details I used user details service.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually my ultimate goal was to have an client cred authentication provider similar to what is there in #39 but the purpose of the PR at that point in time was to get the Design Pattern validated.

import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;

public class OAuthGrantBasedAuthenticationFilter extends AbstractAuthenticationProcessingFilter implements InitializingBean {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AbstractAuthenticationProcessingFilter is meant to be used for user authentication (login). Client authentication is different as the client is not capable (or required) of handling redirect based flows, which the AbstractAuthenticationProcessingFilter is design for. Instead we should use OncePerRequestFilter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. I was not aware of that. Thanks for clarifying.

@Override
public boolean matches(HttpServletRequest request) {
String grantType = request.getParameter(OAuthConstants.GARNT_TYPE_PARAM);
return grantsAllowed.contains(grantType);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Client authentication should not rely on the type of grant in the request. It should authenticate if there are credentials in the request and the target is the token endpoint.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm. Got it. Thanks for clarification.

@jgrandja jgrandja closed this Apr 27, 2020
@jgrandja jgrandja self-assigned this Apr 27, 2020
@jgrandja jgrandja added the status: duplicate A duplicate of another issue label Apr 27, 2020
Copy link
Contributor Author

@paurav-munshi paurav-munshi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jgrandja

Thanks a lot for your valuable inputs.

Thanks for the PR @paurav-munshi. There is code in this PR that is not related to the issue we need to address. This issue simply addresses client authentication. So the minimal amount of code to achieve this is Filter, Authentication and AuthenticationProvider.

Take a look at #39. You will see the implementation should include OAuth2ClientAuthenticationFilter creates OAuth2ClientAuthenticationToken and passes to OAuth2ClientAuthenticationProvider. The Filter should encapsulate the request matching and credentials parsing logic - we will expose public API's for allowing customizations in a later story/issue. The plan is to add the minimal amount of code for each iteration to get things working. We will iterate a few times to add additional functionality as we build things out.

I'm going to close this PR based on my comments, as well, another PR has been previously submitted for this as per comment.

I agree with your point that we can add customizations in a later story/issue. But if we observe closely even the OAuth2AuthorizationEndpointFilter from #66 has been designed with similar ideology of composable components constituting a filter. So won't it be useful if we setup and prescribe a design pattern, early on, which will be followed by all the Filter implementation ?

@jgrandja
Copy link
Collaborator

jgrandja commented Apr 27, 2020

@paurav-munshi

But if we observe closely even the OAuth2AuthorizationEndpointFilter from #66 has been designed with similar ideology of composable components constituting a filter. So won't it be useful if we setup and prescribe a design pattern, early on, which will be followed by all the Filter implementation ?

The Spring Security OAuth 2.0 re-write was a huge undertaking and we've established the coding conventions and patterns and want to ensure contributions to this project follow the same. Simplicity in design, reusability and consistency is key - but of course very hard to achieve. For more details please see getting started.

This PR introduced extra code that was not relevant to the issue being addressed. As outlined in #39, it really should contain OAuth2ClientAuthenticationFilter, OAuth2ClientAuthenticationToken and OAuth2ClientAuthenticationProvider only (and tests).

Our goal is to iterate fast with this project so we can deliver 1.0.0 sooner than later. In order to achieve this, features (epics) will break the work into smaller units (issues) so it's quicker to implement and easier on the review end of things. The bigger the PR, the more dialogue back-and-forth, and the slower it gets merged.

I hope this makes sense?

@paurav-munshi
Copy link
Contributor Author

@paurav-munshi

But if we observe closely even the OAuth2AuthorizationEndpointFilter from #66 has been designed with similar ideology of composable components constituting a filter. So won't it be useful if we setup and prescribe a design pattern, early on, which will be followed by all the Filter implementation ?

The Spring Security OAuth 2.0 re-write was a huge undertaking and we've established the coding conventions and patterns and want to ensure contributions to this project follow the same. Simplicity in design, reusability and consistency is key - but of course very hard to achieve. For more details please see getting started.

This PR introduced extra code that was not relevant to the issue being addressed. As outlined in #39, it really should contain OAuth2ClientAuthenticationFilter, OAuth2ClientAuthenticationToken and OAuth2ClientAuthenticationProvider only (and tests).

Our goal is to iterate fast with this project so we can deliver 1.0.0 sooner than later. In order to achieve this, features (epics) will break the work into smaller units (issues) so it's quicker to implement and easier on the review end of things. The bigger the PR, the more dialogue back-and-forth, and the slower it gets merged.

I hope this makes sense?

Thanks for a detailed response. Yes it makes a lot of sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: duplicate A duplicate of another issue
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add a Client Credentials Authentication Filter
4 participants