JWT Spring - user based access - java

I'm implementing JWT based authentication in my Spring boot application. I have an Accounts table which contains user's bank account info. Now, the user signs in using Account number and pin from that table. The problem is that after logging in, user can access anything with the token assigned to it by JWT. He can even change someone else's account info. How can I restrict the access only to the user for which the token is created?
Every user should be able to access info associated with that user only, so creating roles is not an option. Does JWT provides any such feature or do i have to check the tokens manually? I can parse the token and retrieve the account number out of it and compare it with the account number passed in controller methods, but it doesn't seem like a neat solution as this will require changing every Controller method.

As security in your case depends on business logic I guess there is no way to perform such verification on the Auth provider side.
What you can do is to implement it with the help of the Spring in AOP way quite elegant. You could use spring method security with custom securiry resolver
#PreAuthorize("#securityResolver.isOwner(#userId)")
void changeAccount(UUID userId, Request body);
#Component("securityResolver")
public class CustomSecurityResolver {
public boolean isOwner(final String userId) {
//TODO business check here
}
}
You could even pass JWT token to the security resolver method and implement custom check. In this case you can avoid changing business logic of your service and just add couple of annotations with custom resolver.
I've always implemented such checks as user could only change its own info or tenant isolation with the help of custom method security

Related

Different response based on logged user role

In my RestController I have POST method which returns different object based on user role:
#PostMapping("getlocal")
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')")
public ResponseEntity<LocalDto> getLocal(#RequestBody LocalRequest localRequest){
return status(OK).body(localService.findLocalBylocalId(localRequest));
}
Service method:
public LocalDto findLocalBylocalId(LocalRequest localRequest) {
Role role = userRoleRepository.findByUsername(localRequest.getUsername());
if(role == Role.ROLE_ADMIN) //return localDto infromation object for ADMIN
else if(role == Role.ROLE_USER) //return localDto information for USER
}
LocalRequest contains username of current logged in user.
The problem is when user will authenticate and hit this endpoint he can pass to RequestBody admin's username. In this case he will get access to admin resources even if he is logged as USER.
How to avoid this situation and modify the code?
Should I create two different endpoints - one secured only for USER, and one secured only for ADMIN?
Should I pass Principal object instead passing username in POST method as parameter? Can I pass Princpal object if I am using jwt mechanism?
You can access the currently authenticated username by specifying Principal as an argument. For example:
#PostMapping("getlocal")
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')")
public ResponseEntity<LocalDto> getLocal(Principal principal){
return status(OK).body(localService.findLocalBylocalId(principal.getName()));
}
This works because Spring MVC will resolve the Principal argument from the HttpServletRequest.getUserPrincipal() method and Spring Security overrides the method to align with the currently logged in user. You would then need to update your LocalDto to accept a username instead of a Localrequest.
Alternatively, you can also resolve the entire Spring Security Authentication in the same way since the HttpServletRequest.getUserPrincipal() will be an Authentication.
#PostMapping("getlocal")
#PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_USER')")
public ResponseEntity<LocalDto> getLocal(Authentication authentication){
return status(OK).body(localService.findLocalBylocalId(principal.getName()));
}
This gives you access to the roles without needing to look them up again. The disadvantage of this approach is that you are now relying on Spring Security's API directly.
You can also consider using the #AuthenticationPrincipal annotation to decouple yourself from Spring Security. This approach is the best if you need access to more than just the username because you can still be decoupled from Spring Security, but it also involves more work (i.e for username/password authentication you need a custom UserDetailsService, custom UserDetails). Because the amount of work/variables here, it is difficult to provide more guidance than a link to the documentation without further details.

REST service to return only current user-related resources

I have a REST service implemented using Spring MVC (RestControllers) with token based security (using Spring Security). How can i filter resources depending on user identity? Let's say user has some reports. How can I let authorized user by performing a call to /reports to see only his reports?
Obviously i can make userId to be a request parameter or path variable, but something tells me that this is a bad practice.
I assume i can achieve that using Spring Security features, but how exactly could i do that and, more important, where is the most appropriate place to apply such filtering? Should controllers perform calls to services passing user identity or should it be somehow retrieved at repositories level (I use Spring Data JPA)?
Thanks in advance
You have Authentication object whenever a user is successfully logged in.
It contains Object principal Object credentials and Set authorities.
All you need to do is override UserDetailsService to add new parameters for your authenticated user. Add your userId in authentication as shown in blog
Now when you do
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
this will return you the User object of the spring security.
You can get the user id from here and use this in controller to do necessary actions.

REST - sending user ID vs picking up user ID from Principal

I would like to know the right approach to defining a RESTful service for a generic use case.
I am writing a RESTful API to update the current user's profile. Should we send current user's ID within the request? This would require validation on server side such that user can only edit his own details. So I would need to add check on user ID and Principal object. Also, the client side has to maintain current user ID somewhere.
POST /user/{id}
Alternatively, I can just skip sending the user ID and fetch the user details from Principal object. As we know there is nothing like stateless secured API, would it be the right approach?
I am not aware of any feature in Spring that would validate the current user for me, as required in first approach. If it is present then please let me know.
Consider the case where an administrator, who is authorized to update other users, wants to send the update. In this case, it would be to POST /user/{id}. There's no reason that this shouldn't also be the case for an ordinary user.
With Spring Security, you can use an #PreAuthorize expression on your controller method. It would look something like this:
#PostMapping("/user/{id}")
#PreAuthorize("hasRole('ADMIN') || (principal.id == #id)")
public User updateUser(#RequestBody User newInfo, #PathVariable Long id) {
...
}

Spring Security allowing alternative to username

I'm using Spring security 3.x. In my login page, there is an additional field that the user would scan an ID card to populate. If they do this, the username is not required (it is looked up against the ID scanned), but the password still is.
The problem is that the username is required by my custom AuthenticationProvider. The ID is captured in a filter before this (UsernamePasswordAuthenticationFilter). I don't know how to connect them so my AuthProvider knows it doesn't require username (and also, how does it get the ID at this point since it is passed an Authentication object?).
You might need a special kind of Authentication interface that is not a UsernamePasswordAuthenticationToken and hence doesn't require the username and password both.
Then your AuthenticationProcessingFilter/AuthenticationProvider may create one of them.
Have a look at spring-cas-client and CasAuthenticationToken as an example.
The filter I was extending was FORM_LOGIN_FILTER, which is correct, however I needed to perform all the retrievals (j_username, j_password) here and call the authentication manager manually, instead of calling the super() method which passes the retrievals and auth manager calls to Spring. This also required extending the UsernamePasswordAuthenticationToken. Once I do this, they are connected.

How to add a role filter on request combined with OAuth

I would like to develop a portal which contains some modules
The portal and each module consume data provided by a webservice based on Jersey and secured with OAuth 1.0
For the moment I have almost implement the OAuth provider
A user can connect to the portal and access to a module
Each app or module has a specific access token to consume resource
What I want to do is to add a role implementation
For example for the module1, the user can have 2 roles (role1 and role2) but can't use the 2 roles in parallel
First the user uses the access (module1 / user1 / role1) and he will have a token and later the user uses the access (module1 / user1 / role2) and he will have an other token
Depending on the role, I would like to filter the request with a RolesAllowed annotation for example
I have read this article: http://objecthunter.congrace.de/tinybo/blog/articles/89
When the user is authenticated to the web service I could persist in a database the username, and the role used for the module and the RolesAllowedResourceFilterFactory could use the realm to check if the user is in the role and can access to the resource
But can I by-passed the auth method?
Anyway I really need your help to implement this role filter thing
I will try to give you more details if you need
Thanks
The Jersey oauth filter sets the security context based on what access token was used. You just have to make sure your custom implementation of the oauth provider assigns a token with the right return values from the isInRole() method when called with various roles. The role for a given token can be established during the token authorization flow (e.g. the client can request a particular role using a custom parameter that it passes to the server when requesting a request token (this gets passed in the parameters parameter to the provider.newRequestToken() method).
The security context that the oauth filter sets will delegate to the token isInRole() method when determining the roles - and the RolesAllowedResourceFilterFactory relies on the security context. So, everything should work as expected if OAuthToken.isInRole() returns the right value. Are you facing any issues?
I know it is an old post but I was facing similar issue. In my case I solved it exactly the same way as Martin described. During token authorisation I set allowed roles:
String verifier = provider.authorizeToken(token, sContext.getUserPrincipal(), roles);
where provider is #Context DefaultOAuthProvider, token is DefaultOAuthProvider.Token and roles is a Set of roles I want to allow the access by this token:
Set<String> roles = new HashSet<String>();
roles.add("someRole");
Then in my service I just use a #Context SecurityContext method isUserInRole("someRole") which gives me true in case the user is in specified role and false if not:
if (sContext.isUserInRole("someRole")) {
....
}
Hope it will help somebody.

Categories