Shiro handling user attributes with principals or as session attribute(s) - session

I'm using Shiro in a JSF application and look for guidance on how to handle user attributes like: Id (from the database), email etc. as part of the information held by the Shiro Subject.
Having read the Shiro manual, it seems to me that there's no reason for me to have a SessionScoped bean to hold the user information, as I already have the Subject. The question is how to best store the user information that is not part of the Subject by default.
I've seen different examples where some use Principals and other put a separate custom made User object in the current Session like:
User user = userDAO.findByName(user.getUsername());
subject.getSession().setAttribute("user", user);
It would be elegant to get hold of - for example - the user id like this:
userId = subject.getUserId();
or email:
email = subject.getEmail();
But I guess that is not likely to be possible - so my question is: What is best practice for this? And I'd also like to ask: is there any good reason to keep a separate SessionScoped bean alive for the purpose of user sessions?

For my understanding the principal is the pure object for identification, authentication or remember me function. So put additional info to session (setAttribute).
When a user logs in, load the data and put it to the session with setAttribute. Or when a user comes back with remember me, use the principal to identify the user, and load the necessary things back to session.
It's up to you to decide how much data you want to hold in session for every user. If you want to keep memory consumption small on server, just store data to identify the user (maybe it's already in the principal) and load the data when needed in every function. This way is typically more cpu and database heavy.
If you don't have that much users, just put it to session and make your life easier.
// Do login
Subject currentUser = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(email, password);
currentUser.login(token);
// load attribues
Long uuid = (Long) currentUser.getPrincipal();
Long ucid = // load other ids;
String email = // maybe load email and other stuff
// set attributes
currentUser.getSession().setAttribute("UUID", uuid);
currentUser.getSession().setAttribute("UCID", ucid);
currentUser.getSession().setAttribute("EMAIL", email);
...
// some function
public void doSomething() {
Long ucid = (Long)SecurityUtils.getSubject().getSession().getAttribute("UCID");
// do something
}
In my example, my principal is just the Unique User Id. That's enough to identify every user. Some Users are also customers, so i save this id too (saves me database access everytime).
You can also save more stuff like email or whatever you need.
I duplicated the UUID from the principal to the session, just for comfort. I don't like mixing up principal casts and session access. This keeps my code clean.

Related

Dynamically changing JWT subject field

I successfully implemented JWT as a authentication filter in my web application. When user's login is successful, I am creating a new JWT and assigning userName in the sub field of JWT.
In the subsequent request's I am using userName in the JWT sub field to identify the user. But what if the user changes his userName in the update section of the application. Is there way, I can update the value of sub field in JWT ?
What I am thinking!
I am thinking of getting the existing JWT in the RestController and after updating the userName, I will update the JWT with new userName and again send back to the client. Is this fine or is there a better approach?
I think I should refresh the token after update is done and send back the refreshed token back to client.
#RequestMapping( value = "/account", method = RequestMethod.POST )
public ResponseEntity<?> updateAccount( #RequestBody UserDetailsBean userDetailsBean, HttpServletRequest request,
HttpServletResponse response )
{
try
{
UserAccessDetails accessDetails = getLoggedInUser();
UserDetailsBean updatedUserBean = userService.updateAccount(userDetailsBean, accessDetails);
// send updated jwt incase of mobile number update by user
response.addHeader(SecurityConstants.HEADER_STRING,
SecurityConstants.TOKEN_PREFIX + refreshJWT(updatedUserBean.getMobileNumber()));
return buildResponse(updatedUserBean);
}
catch( DataException e )
{
return buildError(e);
}
}
private String refreshJWT( String subject )
{
return Jwts.builder().setSubject((subject))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SecurityConstants.SECRET).compact();
}
This is working. If anyone has a cleaner and industry standard approach please specify.
If you allow your users to change their usernames, they should also have an immutable user id that can be used to identify any data or activity associated with a given user. Otherwise, any time a user changes his or her name, you will either lose the ability to audit the user's past actions or you will have to update all references to that username in the database. What's worse is if there are references to an old username in the database and another user takes that username -- now you have data from one user now being associated with another due to incorrect handling of user identification.
Now with that said, the sub claim should contain this immutable user id. You can create a separate claim for the mutable username. When a username is changed, you now only need to change a single field in the database (assuming that only the users table references this mutable username). You could then use the refresh token retrieve a new token that would contain the latest username that could then be used by your API as needed.
Using this approach, you should be careful to only use the username claim for display purposes, not for identifying the logged in user due to the fact that it is mutable. The sub claim containing the user id would serve the purpose of identifying a user.
It is also important to note that this solution requires no special logic for "updating the sub claim." You would be using the same logic that you're already using to generate a token for a supplied refresh token.

Spring MVC - Keeping object state between ajax requests

Spring MVC 4.1
Hi,
I have a situation where, on a single page, there are several input fields. As the users enters numbers into these fields, a bunch of calculations will occur and update various other fields on the page.
I want this whole calculation model to take place in Java on the server-side. I really want to avoid replicating this logic in Javascript on the client.
What I envision is...
User opens the page, the object that does the calculations (let's call it Calculator) is created and its initial state is set (many of its fields are pre-populated with values).
This Calculator instance is stored and available for the duration of the user's time on that page.
Whenever the user changes a value in an input field, that new value is sent to the server via ajax and plugged into our Calculator object. The Calculator, re-calculates the other fields based on the new state and returns the results to the page.
The other fields on the page are updated accordingly.
The key here is that I'm not sending the state of all fields with each ajax request. I'm only sending the current value that was updated. Essentially, I'm trying to ensure that the form state and the Calculator state on the back-end are always synchronized.
I have looked into #SessionAttributes and #ModelAttribute.
The problem with #ModelAttribute, as I understand it, is that it will be re-created with each ajax request.
The problem with #SessionAttributes is that it is a session variable. What if the user has two of these windows open? And how do I ensure the object is removed from the session when they leave the page? etc...
Maybe there's no magic Spring bullet and I just have to figure out the session variable thing. But any pointers on dealing with this would be much appreciated.
Thanks!
You have a couple of options:
.1. Like you have said using the #SessionAttributes, however yes it suffers from the issue that you have mentioned, multiple instances of the same session will see the same variable.
.2. Store state somewhere else and re-hydrate the state using #ModelAttribute annotated method. I would personally prefer this approach, essentially when you create the form, create it with a identifier for the current state:
#RequestMapping(params = "form")
public String createForm(Model uiModel) {
uiModel.addAttribute("calculationId", UUID.randomUUID().toString());
return "calculationpage/create";
}
Then for subsequent ajax requests, ensure your previous calculationId is sent across:
#ModelAttribute("calculationState")
public CalculationState rehydrateState(String calculationId) {
//retrieve current state of calculation from some persistent store..
}
#RequestMapping("/calculate")
public String handleCalculation(#ModelAttribute("calculationState") CalculationState c) {
//you will get a calculationstate with the delta's at this point..
}
.3. Another potential approach may be to use session but disambiguate different instances within the session with a custom id:
public String handleCalculation(HttpSession session, #RequestParam("calcId") String calcId) {
CalculationState calcState = (CalculationState) session.getAttribute("calculation" + calcId);
}
You need any sort of persistent store outside session to store and retrieve the state of your calculator model. Like Biju said, I will go for solutions like No 2.

Can CakePHP offer stricter user authentication (continuous throughout session)?

I am trying to create a suitable authentication check for a CakePHP service. Currently it appears that the user session is created initially during login, but never checked beyond this during a single session.
eg. Renamed the username, changing the password or ID in the user's database entry has no effect on the session.
Is there a preferred method for this type of, constantly checked, authentication? Essentially the user should be confirmed access at every request.
My current solution would involve extending the AuthComponent and storing a hash of the user data (including the encrypted password) and checking this at every request. I also considered storing the session ID in this same token, but noticed that CakePHP does not even use the session_start() function.
This functionality appears necessary for me, and I would have thought others would also require such a solution. I have yet to find Cake documentation or community solutions similar to what I need.
Well, you can use isAuthorized() function from AuthComponent. It's being called with every request.
public function isAuthorized($user){
return true; //allow the user to see the page
}
You can debug($user) to see the actual data and if you want "new" information from your database, you can always get them like this:
public function isAuthorized($user){
$current_user_from_database = $this->User->findById($user['id']);
if($current_user_from_database['User']['username'] != $user['username']){
$this->Session->setFlash('You\'ve changed the username. Please, login again.');
$this->redirect($this->Auth->logout);
return false;
}
return true;
}
Look at the API for more info and from the PDF book. You can look at this video about AuthComponent too. It's great.
If you need any more information or help, feel free to ask.
Btw. you have to configure AuthComponent in your Controller if you want isAuthorized() function to get called with every request.
If Session.timeout will work correctly with a setting of zero minutes, you're set. http://book.cakephp.org/2.0/en/development/sessions.html

Simple Authorization in MVC3 with Forms Authentication

I'm trying to do what should be a simple thing in MVC3.
I've got an application that uses forms authentication to authenticate users with a 3rd party SSO. The SSO, on successful login, posts back to a specific controller action on my application. I then call FormsAuthentication.SetAuthCookie(user,false);.
I'm trying to implement some level of authorization. Simply, a user can exist in a number of different roles, e.g. Admin and Developer. Some controller actions should only be available to certain roles. Details of which roles a user belongs to is obtained by making a call to another external API, which returns a simple JSON response indicating.
In theory, this should be as simple as doing something like this after I set the FormsAuthentication cookie:
string[] rolelist = GetRoleListForUserFromAPI(User.Identity.Name);
HttpContext.User = new GenericPrincipal(User.Identity, rolelist);
However, I can't call this directly after calling SetAuthCookie, because HttpContext.User isn't anything meaningful at this point.
I could try setting this on every request, but ever request to my app would mean a roundtrip API call.
The most promising approach I've seen so far is to create a custom Authorization attribute and override OnAuthorization to do something like this:
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (<some way of checking if roles have already been set for this user, or role cache has timed out>)
{
string[] rolelist = GetRoleListForUserFromAPI(filterContext.HttpContext.User.Identity.Name);
filterContext.HttpContext.User = new GenericPrincipal(filterContext.HttpContext.User.Identity,rolelist);
}
}
I could then use [MyCustomAuthorization(Roles="Admin")] in front of controller actions to make the magic happen.
However, I've no idea how to detect whether or not the current HttpContext.User object has had its roles set, or whether it was set over a certain time ago and another API trip is needed.
What's the best approach for this?
Another way would be to store the roles in the UserData property of the FormsAuthentcationTicket. This could be done with comma delimited string.
http://msdn.microsoft.com/en-us/library/system.web.security.formsauthenticationticket.formsauthenticationticket
Then on AuthenticateRequest method, you could pull the ticket back, grab the roles data and assign it to the current user using a generic principal.
You should override PostAuthenticateRequest
protected void Application_OnPostAuthenticateRequest(object sender, EventArgs e)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
string[] rolelist = GetRoleListForUserFromAPI(User.Identity.Name);
HttpContext.User = new GenericPrincipal(User.Identity, rolelist);
}
}
It's invoked after forms authentication is finished with it's processing.
http://msdn.microsoft.com/en-us/library/ff647070.aspx
Update
I had the wrong method signature (just checked in one of my own applications).
My first thought is that you should investigate implementing a custom role provider. This might be overkill but seems to fit in with the role-based plumbing.
More info from MSDN here.
Much to the aghast of some, the session object ISNT a bad idea here.
If you use temp data, you already take a hit for the session.
Storing this data in the cookie, well - Forms auth tokens have already been exploited in the POET vulnerability from a year and a half ago, so in that case someone could've simply formed their own cookie with the "admin" string in it using that vulnerability.
You can do this in post authenticate as #jgauffin mentioned.
If the session state isn't available there you can use it then in Application_PreRequestHandlerExecute and check it there.
If you want to check if session state is available in either see my code at:
How can I handle forms authentication timeout exceptions in ASP.NET?
Also whenever using forms auth and sessions, you always want to make sure the timeouts are in sync with each other (again the above code)

Storing custom user information in an authentication ticket instead of in Membership provider

I've been looking at implementing a custom SQL Server-based membership provider and one of my problems is that the membershipUserObject is keyed on a GUID. Since I'm using an existing ID as a key to roles and user data, this presents an interesting problem.
I'd like your opinion on which option -- or whether there's another option I haven't considered -- to use if I want to persist my own ID in a web session without round-tripping to the database constantly. I know that, by default, the login control creates a Forms Authentication cookie with the username of the membership object. So -- I could:
Implement the Logging_In method of the login control to add my field to the authentication cookie manually
if (Membership.ValidateUser(Login1.UserName, Login1.Password))
{
FormsAuthenticationTicket ticket = new
FormsAuthenticationTicket(
1,
Login1.UserName,
DateTime.Now,
DateTime.Now.AddMinutes(30),
Login1.RememberMeSet,
"some custom data want to store in ticket....", // User-data,
in this case the roles
FormsAuthentication.FormsCookiePath);
string hash = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(
FormsAuthentication.FormsCookieName,
hash);
if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;
Response.Cookies.Add(cookie);
Create a custom MembershipUser Object that inherits from MembershipUser and provides my extra property. I'll still have to persist it somehow (in Session? eww..)
Create a custom Profile Provider that contains the extra field and cache that in session. This seems a bit like overkill, though, for the few fields I would actually cache.
What's the best practice here? I've read countless articles and the extra data in the forms ticket seems like the best so far.
The MembershipUser.ProviderUserKey is of type object, using the default provider we cast this to a Guid in order to use it.
Since you are using a custom membership provider, when you retrieve a user from the database couldn't you populate MembershipUser.ProviderUserKey with an int (or whatever type your id is)? Of course, you would have to cast MembershipUser.ProviderUserKey to an int every time you wanted to use it.

Resources