Skip to content
This repository has been archived by the owner on May 31, 2022. It is now read-only.

[Question] How to add custom authorities after authenticated using Oauth2 flow #640

Closed
LeeU1911 opened this issue Nov 25, 2015 · 9 comments
Assignees

Comments

@LeeU1911
Copy link

Hey,

Our goal is to secure our backend API endpoints using roles of Cloud Foundry users.

The app is implemented using spring-security-oauth2 in order to redirect anonymous user to Cloud Foundry UAA server, authenticate it then get an access token back (briefly, some intermediate steps omitted).

After we obtain the access token, it looks like spring security only extracts the token type Bearer and token value then stores them in Authentication object. We need to have the object OAuth2AccessToken avaiable in order to make another call to Cloud Foundry API to get the logged in user's role. So there are 2 questions:

  1. After OAuth2 flow completes, are we able to retrieve the OAuth2AccessToken object (which contains bearer token, refresh token along with expire date data, etc.) from SecurityContext?
  2. Is there any way we can set custom Authority for the User based on the result we got from Cloud Foundry? Current OAuth2ClientAuthenticationProcessingFilter inserts a hard-code "ROLE_USER" value, we want to extend this filter or add another one to do custom processing to add proper GrantedAuthority to the Authentication object (OAuth2Authentication in this case)

Thanks a lot!

@michaeltecourt
Copy link
Contributor

I don't see any hard coded "ROLE_USER" in the authentication filters, the authorities depend on how you verify tokens : do you use the remote endpoint (like /oauth/check_token), a self supporting JWT or a shared database ? The user authorities come straight the authorization server.

Using a spring-security-oauth2 authorization server you could implement UserAuthenticationConverter (or extend the DefaultUserAuthenticationConverter) to provide additional infos about the authenticated user (the authorities are already present by default). I don't how CloudFoundry handles that though.

@LeeU1911
Copy link
Author

Thank you for your response. I got lost somewhere during debugging so it was incorrect to say the ROLE_USER was hard-coded in the filters. Thanks for reminding me. Actually the hard-coded value was from FixedAuthoritiesExtractor class which was used by UserInfoTokenServices class (in our case /userinfo endpoint is used to verify the token I suppose). So basically Cloud Foundry doesn't include the authorities value in the access token (I manually checked using check_token endpoint). Cloud Foundry only tells whether a specific user is a regular user/auditor/manager of a particular resource via its API endpoints when you make another call and supply enough params. Because there is no authorities by default, the initial ROLE_USER value is the only value in authorities property of Authentication object.

We already have the answer for question <1> above by injecting OAuth2ClientContext object and get the access token from there. However, we're still stuck with adding custom additional authorities in the Authentication object using our logic (make another call to Cloud Foundry API and decide) so that we can use standard spring security method level access control. We would imagine of adding a custom filter into security filter chain but not sure what the correct way is.

Thanks again.

@dsyer
Copy link
Contributor

dsyer commented Dec 7, 2015

I don't think the client is usually in a position to modify the user authentication - that data comes from a server (usually the authorization server). If you want to create a facade over remote authority values, to adapt your code to an external server I guess I can understand why you might want to do it, but really without more detail I couldn't advise you to do anything except just consume the data as it comes from the server, and modify the server if you can. Ideally you would use the OAuth2 scopes (and other data that are generally decoded directly with the access token) to make all your access decisions.

@LeeU1911
Copy link
Author

Thank you Dave for your response.

You're right, the client shouldn't modify the user authentication. However, our requirements are a bit beyond the authority values that the authorization server gives us.

Let me take Github as an example.
We build an app and protect it using Oauth2 using Github as Authorization server. This allows our users to login using Github account. Assuming correct Oath2 flow is implemented, the below happens:

  1. After logging in successfully, we want to retrieve a list of repositories that user has access to from Github on behalf of the user, given sufficient scopes granted.
  2. In our app, we present user with a dropdown list of repositories we retrieved above.
  3. If user selects a repository from the list, we would like to know whether the user is a repository owner or a collaborator for that particular repository so that we can allow user to read/write or forbid access to our app's custom data accordingly - these data are tight to selected repository name / id but it belongs and makes sense to our app only.

Step 3 is where it caused confusion, since we would like to use the OAuth authentication object to have spring security method level restrictions applied on our app's controller endpoints at this point - something like @Preauthorized"(hasanyrole(ROLE_REPO_OWNER)" - but after step 1, looks like we don't have enough authorities to do so.

I hope the example makes sense to you and I would love to hear your thoughts and suggestions on this. Please note that we are not in a position to be able to modify authorization server such as Github in this example.

Thank you and sorry for long post.

@jgrandja
Copy link
Contributor

@LeeU1911 Have you been able to come up with a solution to your use case?

@dsyer
Copy link
Contributor

dsyer commented May 13, 2016

Spring Boot has a strategy called AuthoritiesExtractor that can be used when you authenticate using a user info endpoint.

@LeeU1911
Copy link
Author

LeeU1911 commented Jun 13, 2016

@jgrandja We create a filter to intercept on every request coming from front-end, and make another the call to the resource server to find out more about the user (role of the user on a particular organization). Then we extract the authorities and put into SecurityContext manually (build Authentication object out of authorities and SecurityContextHolder.getContext().setAuthentication(newOAuth2Auth)). We may try to use the AuthoritiesExtractor hinted by Dave to replace this hack.

@dsyer Thank you Dave! As mentioned above, we look forward to using this to extract authorities of user.

@bazaha
Copy link

bazaha commented Feb 8, 2018

@jgrandja
Copy link
Contributor

Closing this as questions are better suited on Stack Overflow. We prefer to use GitHub issues for bugs and enhancements.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Development

No branches or pull requests

6 participants