Hw to configure spring vault to authenticate with a JWT token - spring

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?

Related

How to proof that a JWT is truly valid?

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

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

How are users authenticated and retrieved?

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.

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)

Resources