Update the authorities in the Authentication object once the user is authenticated and before authorizing him for an endpoint - spring

I have a situation where i have to authorize the user to certain path (endpoint) depending on the role he has.
Problem: Once the user is authenticated, i see an empty set of authorities in authentication object (which is used by authorizing filter to determine access for the user.)
I am new to spring security and would need help to address this.
Background: We have a authorization server implemented in the organization which uses oauth2 and lets us do the single sign on. SSO happens in the front end which gets the token from the authorization server and embeds it to every request sent to my server. Now my server decodes the token and authenticates the user. the user claim doesn't have authorities information and i am not allowed to make any code changes in my authorization server..
What i am looking for : Once the user gets authenticated and before he proceeds for authorization (i am using filters in my web security config) i want to make a call to the DB and get the user respective roles and save it to my authentication object, so that the authorities are now updated, user will be authorized to the endpoint.

You can create a SecurityService and call it from #PreAuthorize annotation
#GetMapping()
#PreAuthorize("securityService.hasRole('ADMIN', authentication.principal.username)")
public String getData() {
return "response";
}
And call authorized server from SecurityService with rest template for example:
public class SecurityService {
public boolean hasRole(String role, String username) {
String role = restTemplate.getRoleByUserName...;
// check role
return false/true
}
}

Related

custom authorization in oAuth 2

I am creating a web application in asp.net mvc and I have the Api's for the same
now I have multiple users in my data base like below
AdminUser
GeneralUser 1
GeneralUser 2
AdminUser and GeneralUser 1 logged in my system and the token is generated using oAuth Token.
Token for AdminUser is (bbhights)
Token for GeneralUser 1 is (klkokoiu)
ideally my token should get validated with the particular user like the token which is generated by AdminUser should be worked only for that particular user and the same should be happened with all the general users
but when I hit my Api with the Id of AdminUser and the token of GeneralUser 1 user the data is being fetched from the Api's
I am using [Authorize] attribute in every api call but only token is getting authorize and it is not checking the user to which the particular token is assigned
as I have very less knowledge about the oAuth2 tokens I am not able to understand what should I do
should I create a override method to validate user and token?
but the problem is I am concerned about the existing functionality of the token varification attribute, can I use the below class in my application
public class CustomAuthAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
}
}
and there are total 3 different methods in AuthorizeAttribute class named as below
OnAuthorization
HandleUnauthorizedRequest
IsAuthorized
what should I use alongside OnAuthorization method
or should I create some custom filter to validate my token with userId?

How to enrich JWT in spring authorization server?

I have a SAAS server with microservice architecture. Authentication is done by the new Spring authorization server. For some domain situation, I want to be able to re-issue a JWT for a logged-in user without forcing the user to enter their password again to enrich their token with additional claims.
Having: Logged-in user with claim set A.
Required: Create a new token for the user with claim set B. (Without user intervention)
I'm looking for something like this:
#PostMapping("/renew")
public Authentication token() {
return jwtAuthenticationProvider.authenticate(
new BearerTokenAuthenticationToken(JwtUtil.getCurrentAuthenticationTokenValue())
);
}
Where JwtUtil.getCurrentAuthenticationTokenValue() extracts logged-in user token value from SecurityContextHolder. This setup creates no new token and returns the old one like no authentication process has been triggered.
But I cannot find a function/service that generates a new token in spring authorization server.
PS. I cannot use RefreshToken to get new AccessToken because my client is public and according to this, RefreshToken only is issued for confidential clients.
You can read about OAuth2TokenCustomizer in the docs. Here's an example of customizing the access token:
#Bean
public OAuth2TokenCustomizer<JwtEncodingContext> tokenCustomizer() {
return (context) -> {
if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {
context.getClaims().claims((claims) -> {
claims.put("claim-1", "value-1");
claims.put("claim-2", "value-2");
});
}
};
}
In your case, you could issue a new request to the authorization endpoint (e.g. GET /oauth2/authorize?...) from the client to begin the authorization_code flow with different scopes or additional request parameters and use the customizer to add whatever claims you need. Based on the information you've provided, this would be the recommended way to use the authorization server to issue new tokens.
Adding custom endpoints to perform OAuth2-related actions (such as a custom /renew endpoint) without incorporating best practices and standards from the specification(s) would not be recommended.

Update fields of the principal at the runtime

I use OAuth2 authentication with my own authorization and resource server in Spring Boot. I want to change some fields in my User implements UserDetails(it's my Principal) object at runtime on behalf of the same user or on behalf of another user(administrator/moderator). E.g. user1 with id=1 want to change his country, so he calls this method:
#PostMapping("/setMyCountry")
public void setMyCountry(#CurrentUser User user, #RequestParam String newCountry){
user.setCountry(newCountry);
userRepository.save(user);
}
But when I want to check his country using this:
#GetMapping("/getMyCountry")
public String getMyCountry(#CurrentUser User user){
return user.getCountry();
}
I get the same old country.
Similarly, with the changes as administrator:
#PostMapping("/setUserCountry")
public void setUserCountry(#CurrentUser Moderator moderator, #RequestParam String newCountry){
User user = userRepository.findById(1L).get();
user.setCountry(newCountry);
userRepository.save(user);
}
#GetMapping("/getUserCountry")
public String getUserCountry(#CurrentUser Moderator moderator){
User user = userRepository.findById(1L).get();
return user.getCountry();
}
It returns the same country. But, of course, the DB shows new value.
I already saw question about the similar issues, but if I use this in setMyCountry():
Authentication newAuth = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(newAuth);
then this still doesn't work. Please note, that I use my custom tokens, token providers and token granters, but they all return UsernamePasswordAuthenticationToken at the end.
So, how can I change field of the User and update current principal without log out and log in?
Common practice when using oauth2 is that the Authentication server knows nothing about the users, more than user name, password, what roles, and an some sort of key so it can look up the user object. This can be a unique UUID, or the username (subject) as in an email address.
The resource server gets a token from a client, it takes this token and then calls the authorization server to verify the token, the authorization server verifies it and if verified then sends back information to the resource server so that the resource server can populate its UserDetails object.
If the resource server needs to know say what country this user lives in, it gets the id from the Principal/UserDetails object and then calls maybe a user service, or another database, or another table, or even back to the authorization server that maybe has a /user endpoint and presents the token to the authorization server (that in turn gets the principal info, gets the subject and then looks up in a database for the user info) and then send the user object back.
What my point is that you should always separate Authentication and Authorization (roles etc) information, from the actual User information.
What if you change from say using facebook authentication to github authentication. Do you need to redo all the users? no, because you have all user information separated from the authorization information.

Pass a role to authenticate basic http from postman

I am using Spring boot security and defined user in memory authentication to authenticate http basic but I am not seeing option in Postman to pass user role so my question is how can i pass role in postman for all requests??
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password(passwordEncoder().encode("passowrd"))
.authorities("ADMIN");
}
You don't need to pass Role from Postman request as inMemoryAuthentication you have set user with role "ADMIN" so path you have defined can be accessed by ADMIN role and if you try to call end points by this user, you will have access as "user" as role is "ADMIN" in your memory where user will be authenticated in basic auth.
So from Postman just select Basic Auth and pass user as user and password as password, you will be able to call those end points for which have defined to access by "hasRole = ADMIN..

Spring OAuth2: support auth and resource access with both SSO and custom auth server

I've found similar issue but it's unanswered, so I suppose I'm going to duplicate question a little.
I am using Spring OAuth2 to implement separate resource and custom authentification servers.
I've already configured interaction with auth server through issuing&validating JWT tokens and everything seems fine.
Now I'm trying to add SSO functionality but really stuck with it. I've researched the official Spring examples and attached guide but it is very short worded when it comes to connecting SSO part with custom server authentication. And actually author uses only external provider resource ('user' info) to show process.
I think it is normal thing to have all this SSO means of authentication and also custom registration. I can see it works well with stackoverflow for example.
I am loking for directions where to find any info about handling on resource server different kind of tokens issued by multiply SSO providers and also from custom auth server.
Maybe I can use auth chain to do this and some mean to distinguish token format to know how to process it. Is it possible with Spring OAuth2? Or I need to do this magic somehow manually?
For now I have just one 'maybe strange' idea:
To not involve my own resource server with this SSO stuff at all. After receiving Facebook (for example) token - just exchange it for api JWT token with custom auth server (associating or creating user on the way) and then work with resource server on standard basics
EDITED:
I've found at least something. I've read about configuring filters in authorization chain and translate given social tokens to my custom JWT-s as 'post authenticate'(not a crazy idea after all). But it mostly done with SpringSocial.
So now question is: how to do that?
Forgot to say that I am using Password Grant for authentication on custom server. Clients will be only trusted application and I do not even sure about browser client (thinking about only native mobile options). Even if I decide to have browser client I'll make sure it's going to have backend to store sencetive information
Ok, so after struggling to implement such behavior I've stuck with two different libraries (Spring Social & OAuth2). I decided to go my own way and do it with just Spring OAuth2:
I have the resource server, authentication server and client(backed up by Java and uses OAuth2 Client library, but it can be any other client) - my resources can be consumed only with my own JWT auth token given by my own auth server
in a case of a custom registration: client obtains JWT token(with refresh token) from auth server and sends it to the res server. Res server validates it with public key and gives the resource back
in a case of SSO: client obtains Facebook(or other social platform token) and exchanges it for my custom JWT token with my custom auth server. I've implemented this on my auth server using custom SocialTokenGranter(currently handles facebook social token only. For every social network I'll need separate grant type). This class makes an additional call to facebook auth server to validate token and obtain user info. Then it retrieves the social user from my db or creates new and returns JWT token back to the client. No user merging is done by now. it is out of scope for now.
public class SocialTokenGranter extends AbstractTokenGranter {
private static final String GRANT_TYPE = "facebook_social";
GiraffeUserDetailsService giraffeUserDetailsService; // custom UserDetails service
SocialTokenGranter(
GiraffeUserDetailsService giraffeUserDetailsService,
AuthorizationServerTokenServices tokenServices,
OAuth2RequestFactory defaultOauth2RequestFactory,
ClientDetailsService clientDetailsService) {
super(tokenServices, clientDetailsService, defaultOauth2RequestFactory, GRANT_TYPE);
this.giraffeUserDetailsService = giraffeUserDetailsService;
}
#Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails clientDetails, TokenRequest request) {
// retrieve social token sent by the client
Map<String, String> parameters = request.getRequestParameters();
String socialToken = parameters.get("social_token");
//validate social token and receive user information from external authentication server
String url = "https://graph.facebook.com/me?access_token=" + socialToken;
Authentication userAuth = null;
try {
ResponseEntity<FacebookUserInformation> response = new RestTemplate().getForEntity(url, FacebookUserInformation.class);
if (response.getStatusCode().is4xxClientError()) throw new GiraffeException.InvalidOrExpiredSocialToken();
FacebookUserInformation userInformation = response.getBody();
GiraffeUserDetails giraffeSocialUserDetails = giraffeUserDetailsService.loadOrCreateSocialUser(userInformation.getId(), userInformation.getEmail(), User.SocialProvider.FACEBOOK);
userAuth = new UsernamePasswordAuthenticationToken(giraffeSocialUserDetails, "N/A", giraffeSocialUserDetails.getAuthorities());
} catch (GiraffeException.InvalidOrExpiredSocialToken | GiraffeException.UnableToValidateSocialUserInformation e) {
// log the stacktrace
}
return new OAuth2Authentication(request.createOAuth2Request(clientDetails), userAuth);
}
private static class FacebookUserInformation {
private String id;
private String email;
// getters, setters, constructor
}
}
And from class extending AuthorizationServerConfigurerAdapter:
private TokenGranter tokenGranter(AuthorizationServerEndpointsConfigurer endpoints) {
List<TokenGranter> granters = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));
granters.add(new SocialTokenGranter(giraffeUserDetailsService, endpoints.getTokenServices(), endpoints.getOAuth2RequestFactory(), endpoints.getClientDetailsService()));
return new CompositeTokenGranter(granters);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
...
.allowFormAuthenticationForClients() // to allow sending parameters as form fields
...
}
Every JWT token request is going to 'host:port + /oauth/token' url
Depending on 'Grant type' the server will handle such requests differently. Currently I have 'password'(default), 'refresh_token' and 'facebook_social'(custom) grant types
For default 'password' Grant type the client should send next parameters:
clientId
clientSecret (depends of the client type. Not for single-page clients)
username
password
scope (if not explicitly set in auth server configuration for current client)
grantType
For 'refresh_token' Grant type the client should send next parameters:
clientId
clientSecret (depends of the client type. Not for single-page clients)
refresh_token
grantType
For 'facebook_social' Grant type the client should send next parameters:
clientId
facebook_social_token (custom field)
grantType
Based on the client design the way to send these requests will be different.
In my case with test Java based client which uses Spring OAuth2 library to obtain the social token I do the token exchange procedure with the redirect in controller(controller being invoked using url defined in facebook dev page configuration).
It can be handled in two stages: after obtaining facebook social token JavaScript can make a separate explicit call to my auth server to exchange tokens.
You can see Java client implementation examples here, but I doubt that you're going to use Java client in production:https://spring.io/guides/tutorials/spring-boot-oauth2/

Resources