Dynamically changing JWT subject field - spring-boot

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.

Related

How to logout user when he changes password from all browsers (Django-rest-auth, JWT)?

First of all, i am new with django-rest-framework so please excuse me if I'm wrong.
I'm working with django-rest-auth and django-restframework-jwt to authenticate users. I'm saving the jwt token in localStorage everytime the user logs in.
The problem That I'm facing now is that when I log in with same credentials in two browsers and then I change password in one of them, the other account still valid and user still can navigate and see all pages even though the password has changed.
I wanted to make his JWT token invalid when he changes password so that he will be automatically logged out. But I couldn't find a way to expire his token in official documentation of Django REST framework JWT
I tried to track the moment of changing password by generating manually a new JWT token for user, but this is not working (maybe because the existing token is still valid)
#receiver(signals.pre_save, sender=User)
def revoke_tokens(sender, instance, **kwargs):
existing_user = User.objects.get(pk=instance.pk)
if getattr(settings, 'REST_USE_JWT', False):
if instance.password != existing_user.password:
# If user has changed his password, generate manually a new token for him
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(instance)
payload['orig_iat'] = timegm(datetime.utcnow().utctimetuple())
instance.token = jwt_encode_handler(payload)
After reading some documenations and posts, it seems that this is not quite easy with only jwt since it's stateless, But could somebody point me the direction where to go?
Should I remove JWT authentication?
Is there a work around that can help me on this ?
Thanks a lot.
EDIT:
I found a comment in a similar post on SO by #Travis stating that
A common approach for invalidating tokens when a user changes their
password is to sign the token with a hash of their password. Thus if
the password changes, any previous tokens automatically fail to
verify. You can extend this to logout by including a last-logout-time
in the user's record and using a combination of the last-logout-time
and password hash to sign the token. This requires a DB lookup each
time you need to verify the token signature, but presumably you're
looking up the user anyway
I'm trying to implement that ..I will update my post if it worked.
Otherwise, I still open to suggestions.
After days of work, I ended up by overriding the JWT_PAYLOAD_HANDLER and adding the last digits of the user's hash of password in the payload of JWT token (since adding all the hash of password in the payload is not a good practice)
and then creating a custom middleware that intercepts all requests.
in every request I check from jwt token if the hash of the password matches the existing user's hash (if not that means that the user has changed his password)
if they are different then I raise an error and logout the user with old hash of password.
in config file :
'JWT_PAYLOAD_HANDLER': 'your.path.jwt.jwt_payload_handler',
and in the root stated in the config file :
def jwt_payload_handler(user):
username_field = get_username_field()
username = get_username(user)
payload = {
'user_id': user.pk,
'username': username,
'pwd': user.password[-10:],
'exp': datetime.utcnow() + api_settings.JWT_EXPIRATION_DELTA
}
if hasattr(user, 'email'):
payload['email'] = user.email
if isinstance(user.pk, uuid.UUID):
payload['user_id'] = str(user.pk)
payload[username_field] = username
return payload
and then this is the custom middleware :
from django.http.response import HttpResponseForbidden
from django.utils.deprecation import MiddlewareMixin
from rest_framework_jwt.utils import jwt_decode_handler
from config.settings.base import JWT_AUTH
from trp.users.models import User
class JWTAuthenticationMiddleware(MiddlewareMixin):
def process_request(self, request):
jwt_user_pwd = self.get_jwt_user_pwd(request)
# check if last digits of password read from jwt token matches the hash of the current user in DB
if jwt_user_pwd is not None:
if jwt_user_pwd['pwd'] != jwt_user_pwd['user'].password[-10:]:
return HttpResponseForbidden()
#staticmethod
def get_jwt_user_pwd(request):
token = request.META.get('HTTP_AUTHORIZATION', None)
# Remove the prefix from token name so that decoding the token gives us correct credentials
token = str(token).replace(JWT_AUTH['JWT_AUTH_HEADER_PREFIX'] + ' ', '')
if token:
try:
payload = jwt_decode_handler(token)
authenticated_user = User.objects.get(id=payload['user_id'])
except Exception as e:
authenticated_user = None
payload = {}
if authenticated_user and payload:
return {'user': authenticated_user, 'pwd': payload.get('pwd')}
return None
To logout the user I have read the status code of the request 'in this case 403' from front end : (I'm using Angular in my case) and then logout the user
I hope it helps someone in the future .
Well,
It is all about token expiry time - If you keep this short (like 10-15 minutes) - you can no bother with invalidating it when a password or some permissions will change. Token will be invalidated always after some short period of time and a new one will be issued.
If you are using JWT as long living token (which is not good practice) - you will have problems.
Because actions like changing a password and invalidating other tokens (different session) (or force recreate) needs to be stored somewhere else (like some NoSQL store) - and checked for each session that some special action is required - and then you are losing the stateless advantage of JWT.

how expired OAuth2 Tokens are detected?

I'm a beginner in Spring Boot and I set up OAuth2 and all work well with inMemory () data.
I'm trying to store the Token in DB but I want the client to stay in memory because I will always use a single client for this application
so i created the necessary tables using schema.sql i see that only the oauth_access_token and oauth_refresh_token tables that will be used and when i make a request to request a token the system returns the old one if it is still valid and a new one if not(this is good but..). For this reason I have difficulty understanding how the system can know that a token is expired or not? (knowing that I do not define JWT token or any other specific type of token explicitly)
The token in this scenario is an instance of class DefaultOAuth2AccessToken which has field
expiration
which takes care of expiry of a token. Object of this class is serialized so that it can be stored in database.Upon deserialization values are populated in respective fields and below method is invoked to check for expiry.
public boolean isExpired() {
return this.expiration != null && this.expiration.before(new Date());
}
The class DefaultTokenServices has createAccessToken() method to create token.
just go and have a look at these classes to see the working.
Thanks for your answer
After some research in the documentation i can now understand how the token is validated.
the token (DefaultOAuth2AccessToken) is stored as a serialized object in the DB and it will be retrieved from the database to validate expiration date , and many other operation ...

which crud operation in rest to use post or delete?

i have created a bank application where i want to write a rest service to delete account . so for that we need an account no . for that i think for security reasons i cant pass account no in url . so i am passing it in request body . i think if i try using it with delete it runs fine but again that could be a security issue .
so in that case will i need to use post instead of delete so that i can pass account no in request body ?
#PostMapping("/account")
public void deleteAccount(#RequestBody #Valid final AccountNoDto accountNoDto) {
return accountService.deleteAccount(accountNoDto);
}
or
#DeleteMapping("/account/{accountNo}")
public void deleteAccount(#PathVariable Long accountNo) {
return accountService.deleteAccount(accountNo);
}
You should use #DeleteMapping because you are deleting a record.
The HTTP verbs should be compliant with what the function does.
But dont send the account Number along with the endPoint.
Write the endpoint as -
#DeleteMapping("/account")
The Account Number should be retrived at the backend from the token you will be sending along with the request.So All requests GET,POST,PUT,DELETE will have the same uri and the account number will be fetched from the token at the backend.
If you want to know how it is done in spring read about SecurityContextHolder class
Idealy we use #DeleteMapping for delete operation and we use #PostMapping for new creation and updation of data . I dont think account id is that much sensitive information to reveal in url. You can go for #DeleteMapping

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

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.

After changing ASPNETDB Username via sql, User.Identity not updating

I'm calling a stored procedure to change the username. This works and the username is changed.
After I change the username, Membership.GetUser() returns null. I check User.Identity and it still has the old username and is authenticated.
First I tried calling this function (which is also called when the user first logs in)
public void Authorize(string username)
{
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1, username, DateTime.Now, DateTime.Now.AddDays(5), true, string.Empty);
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
authCookie.Expires = authTicket.Expiration;
HttpContext.Current.Response.Cookies.Add(authCookie);
}
Then I tried adding Membership.ValidateUser(username,password) before calling the Authorize function (since it's a test account and I do know the password) but it didn't make any difference.
Then I tried this:
FormsAuthentication.SignOut();
FormsAuthentication.SetAuthCookie(txtUserName.Text, false);
I'm confused that after I call FormsAuthentication.SignOut(), the User.Identity.IsAuthenticated is still true. Is that not supposed to be updated until after the page reloads?
I read this http://forums.asp.net/t/939408.aspx/1 which makes me think my problem is User.Identity.Name never getting updated. How do I make that happen?
Membership.GetUser() will only work for an authenticated user. Otherwise, it's going to return null. To verify you're dealing with an authenticated request call "User.Identity.IsAuthenticated" on the page. If you've got an authenticated request, but Membership.GetUser() is still returning null, then that means the username associated with the authenticated user can't be found in the Membership datasource. Verify the username of the authenticated user with "User.Identity.Name".
If you're calling one of the Membership.GetUser() overloads which takes the username and it's returning null, then that user doesn't exist in the Membership datasource (or we've got a bug). One way to easily verify this is to try a Membership.CreateUser() with the same username. If this doesn't throw an error because of a duplicate user, then you know the user never existed in the first place.
Membership.GetUser() should have never worked for an anonymous user. No support was built into Membership for handling this case.
Any changes to the FormsCookie, user account, are not reflected in the User.Identity property until the next request. This property is set by the membership provider at the start of the request when the cookie is validated. Any changes you make will be seen by in any subsequent requests.
You can manually replace User.Identity with a principal of your own, but this requires implementing your own membership provider.

Resources