This is my Service for generating JWTs in my Project. This, works, a user can login, retrieve a JWT and do operations with this token until its expires.
#Singleton
public class JwtService {
public String generateUserJwt() {
return generateJsonWebToken(Set.of(Role.USER));
}
public String generateAdminJwt() {
return generateJsonWebToken(Set.of(Role.ADMIN));
}
public String generateSuperAdminJwt() {
return generateJsonWebToken(Set.of(Role.USER, Role.ADMIN));
}
private String generateJsonWebToken(Set<Role> roles) {
Set<String> groups = Set.of(roles.stream().map(Role::getValue).toArray(String[]::new));
return Jwt.issuer("https://example.com")
.subject("myproject-2022-jwt")
.upn("myproject-2022-jwt")
.claim(Claims.birthdate.name(), "1985-10-25")
.groups(groups)
.expiresAt(System.currentTimeMillis() + 3600)
.sign();
}
}
Problem
Let say a User login, 10 seconds later I ban/delete his account. His JWT is still valid and he can do requests with postman using last JWT.
How I can handle this?
You can't.
The entire purpose of a JWT is to be self sustained and independent. This is why its never recommended to expose JWTs to external clients. Even though people do this all the time.
The rfc for JWTs states the following:
JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. [...] enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted.
In this context, "claim" can be something like a 'command', a one-time authorization, or basically any other scenario that you can word as:
Hello Server B, Server A told me that I could , and here's the (cryptographic) proof.
This is why you need to have short lived JWT that are only valid for minutes. Because if one gets compromised you can't block/logout that user.
OWASP mentions this in their cheat sheet series, and their solution for mitigation is to implement a block list, but remember that once you do that, the entire solution is not stateless anymore, and you need to share this block list over multiple servers.
Which basically means you can use cookie sessions, as they have more protection and fulfills the same purpose.
Using JWTs as session bearers has always been a bad idea and has been mentioned over and over:
Even back in 2016 it was not advised:
Stop using JWT for sessions part 1
Stop using JWT for sessions part 2
Redis blog:
JSON Web Tokens (JWT) are Dangerous for User Sessions
Okta blog:
Why JWTs suck as user sessions
Related
I'm trying to use spring-vault-core and (optionally) spring-cloud-starter-vault-config to connect to a vault using a JWT token as the auth mechanism.
From the documentation, you can request a token from /auth/jwt/login. Currently, the code I've written for that looks like this:
public void deleteSecrets(String path) {
getVaultOperations().delete(path);
}
public void writeSecret(String path, ObjectNode jsonObject) {
getVaultOperations().write(path, jsonObject);
}
private VaultOperations getVaultOperations() {
return new VaultTemplate(vaultEndpoint, new TokenAuthentication(vaultTokenFactory.getToken()));
}
It means I'm constructing a new VaultOperations every time, and am having to just use a rest template in my VaultTokenFactory to get the token from the endpoint.
I'm a little bit surprised there's no built in support from Spring for this, but I can't find any mention of JWT authentication in their docs at all.
I considered making my VaultTokenFactory implement ClientAuthentication and wiring it in like that, but from what I can see a token is only generated once through that mechanism, whereas this will need to be refreshed.
Has anyone implemented something similar in a cleaner way that doesn't involve constructing a VaultTemplate every time?
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.
A couple of tutorials on oAuth use the Flask session to store state parameters and access tokens in the flask session. (Brendan McCollam's very useful presentation from Pycon is an example)
I understand that Flask stores the session in cookies on the client side and that they are fairly easy to expose (see Michael Grinberg's how-secure-is-the-flask-user-session). I tried this myself and was able to see the token the expiration, etc.
Is it correct to store the state and tokens in the flask session or they should be stored somewhere else?
Code example:
#app.route('/login', methods=['GET'])
def login():
provider = OAuth2Session(
client_id=CONFIG['client_id'],
scope=CONFIG['scope'],
redirect_uri=CONFIG['redirect_uri'])
url, state = provider.authorization_url(CONFIG['auth_url'])
session['oauth2_state'] = state
return redirect(url)
#app.route('/callback', methods=['GET'])
def callback():
provider = OAuth2Session(CONFIG['client_id'],
redirect_uri=CONFIG['redirect_uri'],
state=session['oauth2_state'])
token_response = provider.fetch_token(
token_url=CONFIG['token_url'],
client_secret=CONFIG['client_secret'],
authorization_response=request.url)
session['access_token'] = token_response['access_token']
session['access_token_expires'] = token_response['expires_at']
transfers = provider.get('https://transfer.api.globusonline.org/v0.10/task_list?limit=1')
return redirect(url_for('index'))
#app.route('/')
def index():
if 'access_token' not in session:
return redirect(url_for('login'))
transfers = requests.get('https://transfer.api.globusonline.org/v0.10/task_list?limit=1',
headers={'Authorization': 'Bearer ' + session['access_token']})
return render_template('index.html.jinja2',
transfers=transfers.json())
I think some tutorials over-simplify in order to show simpler code. A good rule of thumb is to use session cookies only for information that MUST be known by your application and your user's browser, and is not private. That normally translates into a Session ID and possibly other non sensitive information such as a language selection.
Applying that rule of thumb, I'd suggest the next to each of the tokens:
Authorization Token: this data is by definition known to both the user and the application, so it shouldn't be a security concern to expose it in the cookie. However, there really is no need to keep this token once you're given an access code, so I advice against keeping it locally or in your cookies.
Access Code: this data must be considered secret, and must only be known by your application and the provider. There is no reason to make it know to any other parties, including the user, therefore it should NOT be included in cookies. If you need to store it, keep it locally in your servers (perhaps in your database, referencing your users session ID).
CSRF State Token: this data is ideally included as a hidden form field and validated against a server side variable, so cookies seem like an unnecessary complication. But I wouldn't be concerned about this data being in a cookie, since it's part of the response anyways.
Keep in mind there are extensions such as flask-sessions, with which practically the same code uses server side variables instead of cookie variables.
Having worked my way through this tutorial:
http://bitoftech.net/2015/02/16/implement-oauth-json-web-tokens-authentication-in-asp-net-web-api-and-identity-2/
I now have the solution standing upright and I can issue JWT tokens (what I think of as 'login') and authenticate requests by passing in those tokens during subsequent calls.
What I'm not clear on is how the [Authorize] attribute is:
Recognising a user as authenticated
Retrieving a user from the database
Making that user available to my code
How I would add to the authentication process if I wanted to (perhaps including extra authentication logic after the exiting logic)
[EDIT] I understand that JWT tokens are being used to identify the user but I don't understand 'how' this is taking place. I also understand the middleware is doing it, but the workings of this are not clear.
with the [Authorize] attribute an AuthorizationFilter will added to the filter chain before the controller is called. This article illustrates that.
With the call to ConfigureOAuthTokenConsumption (Step 6 in the tutorial) you give the middleware the information it needs to validate and process tokens.
the authentication, i.e. check username and password, happens only before the token is issued in
public override async Task
GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) {
...
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
...
}
the AuthorizationFilter will only see the token and rely on the information in the token itself
this blog post gives you an example how you can access the username:
var currentPrincipal = ClaimsPrincipal.Current;
string userName = "Anonymous";
if (currentPrincipal.Identity.IsAuthenticated)
{
userName = currentPrincipal.Identity.Name;
}
the middleware gets the information from the token
you can add you own logic either before the token is issued in GrantResourceOwnerCredentials or add your own AuthorizationFilter if you need additonal logic when you receive the token. The blog post linked under 3. shows an example for that.
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)