Spring security match Principal info with request param - spring

So I'm trying to validate my endpoint without IFs and such things, only with PreFilter and I basically have the following
#GetMapping(value = "/books/{userId}")
#PreFilter("hasRole('ADMIN') or principal.userId == pathVar.userId")
public List<Books> getBooks(#PathVariable("userId" String userId) {
//do sth
}
My problem is getting the PreFilter to work, or actually to match the userId from the authenticated principal with the path variable so that each user can request only his own books.
Yes I know that I can remove the path variable and just do principal.getUserId() inside the method, but that means that I will need to have an if user is admin.

Okay after quite a few tries I have finally found out how to do it. In case anyone is interested:
#PreAuthorize("hasRole('ADMIN') or #userId == principal.userId")
This way if you are the admin you can get the books of any user, and if you are a user you can get only your own.

Related

Unable to find the group information of the logged in user -JWT token OKTA

I am new to Okta so apologies if my questions are not clear.
So what I want to do is basically parse the JWT token generated by
okta and extract the group information of the logged in user
associated with it.
I am under the impression that this information should be there in the
OidcUser object. I do see user name/email id / token validity etc
information inside this object. Unfortunately I can't see group id
which I need for further processing.
#RequestMapping("/")
public String hello(#AuthenticationPrincipal OidcUser user){
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Object> entry : user.getClaims().entrySet()) {
sb.append(entry.getKey() + ":" + entry.getValue().toString());
sb.append("\n");
}
sb.append("|");
sb.append(user.getClaims());
return sb.toString();
}
Here is my okta plugin inside spring boot
okta.oauth2.issuer=https://dev-XXXXXXXX.okta.com/oauth2/default
okta.oauth2.client-id=XXXXXXXXXX
okta.oauth2.client-secret=XXXXXXXXXXXX
I am wondering if my approach is proper and what more I need to do to extract User group from Okta JWT token.
To get user groups you need to make an additional request to /userinfo endpoint, assuming you requested groups scope during /authorize call.
Please see a guide here
Not exactly spring-boot response, but it's always beneficial to know how things work under-the-hood

What is a good way to pass additional information to the http response when issuing access token with Owin middleware in Asp Net Web Api?

I am using Owin middleware to implement token-based security for my application. When issuing the access token to the client I would also like to pass User Id along with the token, so that, the client application will know the User Id and will be able to call GetUserById (one of the methods inside UserController) in order to show the user his starting page. The best solution I could come up with so far is just adding User Id to the response header. Take a look at my OAuthAuthorizationServerProvider class, in GrantResourceOwnerCredentialsmethod I am adding User Id to the header, using context.Response.Headers.Add("User-Id", new string[] { "1" })
Here is the implementation of my OAuthAuthorizationServerProviderclass
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
//The actual credential check will be added later
if (context.UserName=="user" && context.Password=="user")
{
identity.AddClaim(new Claim("Id", "1"));
context.Validated(identity);
//Here I am adding User Id to the response header
context.Response.Headers.Add("User-Id", new string[] { "1" });
}
else
{
context.SetError("invalid_grant","The credentials provided are not valid");
return;
}
}
}
Basically the client then will have to read User-Id from the header. Is this a good solution I came up with or there is a better one? Also what if I want to pass the whole User object with all its properties to the response is it possible and how to do this?
Since you store the ID already in the claims, why don't you just decode your token on the client and read out the user id like that? Passing it through the headers could allow tampering with it (security).
Have a look on how you could achieve to decode your token and read the claims. This is a c# example https://contos.io/peeking-inside-your-jwt-tokens-using-c-bf6a729d06c8 but this could also be done even through javascript.
This, assuming you use the JWT-format as token (was not specified in your initial question).
Bad way to store UserID as a response header back to client side. This is a huge security concern.
The best way would be to store it as a Claims.
It is very easy to achieve this and get back the claims in the client side.
In your controller, call this and remember to reference
using Microsoft.AspNet.Identity;
var userId = User.Identity.GetUserId();

How to store PreRequestFilter information in AuthUserSession

I am building a web service using ServiceStack which has to support multiple vendors. The web service provides largely the same functionality to all vendors with some exceptions here and there.
In order to re-use as much functionality as possible I have come up with the following URL scheme:
http://localhost/brand1/templates
http://localhost/brand2/templates
"brand1" and "brand2" are not services but "templates" is. The Templates service's request DTO's will have a property called "Brand" like so:
[Route("/{Brand}/templates", "GET")]
public class GetTemplates
{
public Brand Brand { get; set; }
}
So in the Templates service I know which brand I am dealing with. This scheme works well.
What I cannot figure out though is this. The user of the service has to be authenticated and I cannot figure out how to handle the redirection of the service after the user has been authenticated since I have to pass along the brand information. I have created my own CustomAuthProvider class that inherits CredentialsAuthProvider. In the TryAuthenticate method I can set the authService.GetSession().ReferrerUrl property to the correct brand if I know what it was.
The only way I have found so far to get this information is to register a PreRequestFilter. My thinking here was that since the URL (e.g. http://localhost/brand1/templates) contains the brand I can store it in my own AuthUserSession class. I can't figure out how to do this. I have a "SessionFactory" method that I pass to the AuthFeature constructor. But what should I do in there? How do I get to the brand that I've obtained in the PreRequestFilter? Is it safe to store it in a field of the AppHost? I think not because of concurrency issues. How do I tie the PreRequestFilter to the SessionFactory method?
I hope I am explaining my problem clearly enough?
I was overthinking the solution because I didn't realize that I had all the information I needed in the IServiceBase parameter of the TryAuthenticate method of the CredentialsAuthProvider class.
In the end I came to the following solution:
public class CustomCredentialsAuthProvider : CredentialsAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService,
string userName, string password)
{
var session = authService.GetSession();
var origQuery = authService.Request.UrlReferrer.Query;
session.ReferrerUrl = "/error";
var queryString = origQuery.Substring(10); // strip "redirect="
var decodedUrl = HttpUtility.UrlDecode(queryString);
if (!string.IsNullOrWhiteSpace(decodedUrl))
{
var query = new Uri(decodedUrl);
session.ReferrerUrl = query.AbsolutePath;
}
return DoAuthentication(userName, password);
}
}
The different places where you can set the Url to redirect to during ServiceStack Authentication, in order of precedence are:
The Session.ReferrerUrl Url if it's populated
The Continue QueryString, FormData param when making the request to /auth (i.e Authenticate.Continue property)
The HTTP Referer HTTP Header
The CallbackUrl of the current AuthProvider used

Handle url pattern that has user name as part of the url

Suppose I have 3 url patterns that needs to be handled by Spring MVC as follows:
1) www.example.com/login (to login page)
2) www.example.com/home (to my home page)
3) www.example.com/john (to user's home page)
I would like to know what is the best practice way of handling the url pattern that has username as part of the url (real world example is facebook fanpage www.faceboo.com/{fanpage-name})
I have come up with my own solution but not sure if this is the clean way or possible to do it.
In my approach, I need to intercept the request before it being passed to Spring MVC's dispatchservlet, then query the database to convert username to userid and change the request URI to the pattern that Spring MVC can recognize like www.example/user/userId=45.
But I am not sure if this is doable since the ServletAPI does not have the setter method
for requestURI(it does have the getter method for requestURI)
Or if you have a better solution please share with me. Thank in advance :-)
Spring MVC should be able to handle this just fine with PathVariables.
One handler for /login, one handler for /home, and one handler for /{userName}. Within the username handler you can do the lookup to get the user. Something like this:
#RequestMapping(value="/login", method=RequestMethod.GET)
public String getLoginPage() {
// Assuming your view resolver will resolve this to your jsp or whatever view
return "login";
}
#RequestMapping(value="/home", method=RequestMethod.GET)
public String getHomePage() {
return "home";
}
#RequestMapping(value="/{userName}", method=RequestMethod.GET)
public ModelAndView getUserPage( #PathVariable() String userName ) {
// do stuff here to look up the user and populate the model
// return the Model and View with the view pointing to your user page
}

Spring Security #PreAuthorize - Restrict certain roles by using Spring EL

Using Spring Security 3.1.3.RELEASE
So if there are a list of roles (over 10) and there is a need to block just ONE from accessing a Spring Controller method. Can this be done using Spring Expression Language, and avoid listing each and very accepted role?
For example, by including the Not sign.
#PreAuthorize("!hasRole('ROLE_FREE_USER')")
over listing all the roles like this
#PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_PAID_USER','ROLE_PREM_USER',...)
I've looked at the documentation over here: http://static.springsource.org/spring-security/site/docs/3.0.x/reference/el-access.html
But there seems to be nothing on the NOT EQUAL to cases. Anyone face similar issue?
I'm pretty sure that NOT-sign (!) is supported in Spring Expression Language (SPEL). Naturally, it returns a boolean result.
An Example from the official documentation:
// evaluates to false
boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);
// -- AND and NOT --
String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
Spring Expression Language didn't work for me in this case. Initially I tried with the following,
#RequestMapping("/post/edit")
#PreAuthorize("hasRole('ROLE_OWNER') AND !hasRole('ROLE_ADMIN')")
public String editPost(Model model, Principal principal, HttpServletRequest request, #RequestParam("postId") String postId) {
}
However, I had to recheck the Role from inside the method and redirect the page in case user has Admin Privileges.
#RequestMapping("/post/edit")
#PreAuthorize("hasRole('ROLE_OWNER')")
public String editPost(Model model, Principal principal, HttpServletRequest request{
//Admin Users does not have access to post edit page
if (request.isUserInRole("ROLE_ADMIN")) {
return TemplateNamesConstants.POST_WALL;
}
}
Do update this thread in case you found a better/alternate solution.

Resources