Accessing JWT token payload variable/object in Resource method from filter - jersey

I have a filter for token validation and I am using JWT token for authentication and authorization.
Suppose I am passing user_id in the JWT token payload and I want this user_id to be available for all my resource methods that will pass my filter so that I can do query on the basis of user_id in my resource method.
Is this possible to do so?
Or how can I pass this user_id as a parameter to my resource methods from my filter ?
And whether it is a good practice to do so ?
public void filter(ContainerRequestContext requestContext)
throws IOException, ConnectException {
UriInfo uriInfo = requestContext.getUriInfo();
UserModel user = new UserModel();
Claims claim =null;
String temp=null;
requestContext.setProperty("before","before");
if (uriInfo.getPath().equals("profile")) {
String authorizationHeader =
requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// Check if the HTTP Authorization header is present and formatted correctly
if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
throw new NotAuthorizedException("Authorization header must be provided");
}
// Extract the token from the HTTP Authorization header
String tokens = authorizationHeader.substring("Bearer".length()).trim();
try {
claim = user.validateToken(tokens);
} catch (SQLException ex) {
Logger.getLogger(AuthorizationRequestFilter.class.getName()).log(Level.SEVERE, null, ex);
}
requestContext.setProperty("id",claim.getId());
requestContext.setProperty("hello","hello");
}
}
I am able to get the value for "before" variable in my resource method.
but I am getting null for "hello" and "id". I want to get the value "id" in my resource method.
Thanks

Related

Spring Security:Why check if authentication token is set in authorization filter?

I'm trying to understand spring security. I came across following piece of code
public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
public JWTAuthorizationFilter(AuthenticationManager authManager) {
super(authManager);
}
#Override
protected void doFilterInternal(HttpServletRequest req,
HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
String header = req.getHeader(HEADER_STRING);
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(req, res);
}
// Reads the JWT from the Authorization header, and then uses JWT to validate the token
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
if (token != null) {
// parse the token.
String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes()))
.build()
.verify(token.replace(TOKEN_PREFIX, ""))
.getSubject();
if (user != null) {
// new arraylist means authorities
return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>());
}
return null;
}
return null;
}
What is the need of
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
Authorization will be done after authentication right ? In that case header will be set anyway.What am I missing?
And also what is chain.doFilter() doing exactly? It is used to proceed and hit the servlet eventually right? If user isn't authenticated why proceed with request?If it is to proceed to authentication filter then how come authorization filter invoked before authentication filter?
In spring security a filter chain is basically a linked list. So as you do a request, it will hit the first filter, that filter will then call the next chain in the link, then the next, the next, etc.
// Get a specific header (im guessing authorization header)
String header = req.getHeader(HEADER_STRING);
// If no such header was found, or the found header does not include a specific prefix
// (im guessing the string Baerer) which means that if this is not a
// authentication header containing a baerer token, then call the next filter in
// the chain.. The next filter will have the same call, etc.
if (header == null || !header.startsWith(TOKEN_PREFIX)) {
chain.doFilter(req, res);
return;
}
If a request is sent with no auth header and we proceed into the application, down the filter chain as we hit the actually endpoint, it will probably have an anotation saying that you need a specific role, which the user wont have, since it hasnt authenticated itself. And the result will be a 401 UNAUTHORIZED returned from the security framework.
If it on the other hand it finds an auth header with a bearer prefix it will try to extract out the token from the header, verify its integrity that no one has tampered with it, decode it, and get the subject field from the token. Using the subject it will create a UsernamePasswordAuthenticationToken and set this as the authenticated token in the security context. Basically setting the subject as the authenticated user.
If there is no subject in the token, filter will return null (which is sloppy coding and might crash the application, a 401 exception should be thrown).
Several bad things with this code:
String header = req.getHeader(HEADER_STRING); is called twice
default user case is to try to log in the user, should be the other way around, default case should be to throw a 401 unauthorised and all logic should try to prevent that.
if a authorization header containing the string bearer but no token is sent the parsing function will return a null value, and the filter will call setAuthentication with the value null which will probably result in a crash.
if a authorization header containing the string bearer the token does not contain a subject value the parsing function will return a null value, and the filter will call setAuthentication with the value null which will probably result in a crash.
Spring already includes support for Nimbus jwt library to encode and decode JWT's there is no need to pull in another library java-jwt as done in the example above.
Worth mentioning that from Spring 5 there is already a fully implemented JWT solution in spring security that only needs to be customised. So this entire custom filter is redundant.

How can I refresh tokens in Spring security

This line:
Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
Throws an error like this when my jwt token expires:
JWT expired at 2020-05-13T07:50:39Z. Current time:
2020-05-16T21:29:41Z.
More specifically, it is this function that throws the "ExpiredJwtException" exception :
How do I go about handling these exceptions? Should I catch them and send back to the client an error message and force them to re-login?
How can I implement a refresh tokens feature? I'm using Spring and mysql in the backend and vuejs in the front end.
I generate the initial token like this:
#Override
public JSONObject login(AuthenticationRequest authreq) {
JSONObject json = new JSONObject();
try {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authreq.getUsername(), authreq.getPassword()));
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
List<String> roles = userDetails.getAuthorities().stream().map(item -> item.getAuthority())
.collect(Collectors.toList());
if (userDetails != null) {
final String jwt = jwtTokenUtil.generateToken(userDetails);
JwtResponse jwtres = new JwtResponse(jwt, userDetails.getId(), userDetails.getUsername(),
userDetails.getEmail(), roles, jwtTokenUtil.extractExpiration(jwt).toString());
return json.put("jwtresponse", jwtres);
}
} catch (BadCredentialsException ex) {
json.put("status", "badcredentials");
} catch (LockedException ex) {
json.put("status", "LockedException");
} catch (DisabledException ex) {
json.put("status", "DisabledException");
}
return json;
}
And then in the JwtUtil class:
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return createToken(claims, userDetails.getUsername());
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + EXPIRESIN))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
}
For more info, here is my doFilterInternal function that filters every request:
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException, ExpiredJwtException, MalformedJwtException {
try {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userService.loadUserByUsername(username);
boolean correct = jwtUtil.validateToken(jwt, userDetails);
if (correct) {
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
} catch (ExpiredJwtException ex) {
resolver.resolveException(request, response, null, ex);
}
}
There are 2 main approaches to deal with such situations:
Manage access and refresh tokens
In this case, the flow is the following one:
User logins into the application (including username and password)
Your backend application returns any required credentials information and:
2.1 Access JWT token with an expired time usually "low" (15, 30 minutes, etc).
2.2 Refresh JWT token with an expired time greater than access one.
From now, your frontend application will use access token in the Authorization header for every request.
When backend returns 401, the frontend application will try to use refresh token (using an specific endpoint) to get new credentials, without forcing the user to login again.
Refresh token flow
(This is only an example, usually only the refresh token is sent)
If there is no problem, then the user will be able to continue using the application. If backend returns a new 401 => frontend should redirect to login page.
Manage only one Jwt token
In this case, the flow is similar to the previous one and you can create your own endpoint to deal with such situations: /auth/token/extend (for example), including the expired Jwt as parameter of the request.
Now it's up to you manage:
How much time an expired Jwt token will be "valid" to extend it?
The new endpoint will have a similar behaviour of refresh one in the previous section, I mean, will return a new Jwt token or 401 so, from the point of view of frontend the flow will be the same.
One important thing, independently of the approach you want to follow, the "new endpoint" should be excluded from the required Spring authenticated endpoints, because you will manage the security by yourself:
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
..
#Override
protected void configure(HttpSecurity http) throws Exception {
http.
..
.authorizeRequests()
// List of services do not require authentication
.antMatchers(Rest Operator, "MyEndpointToRefreshOrExtendToken").permitAll()
// Any other request must be authenticated
.anyRequest().authenticated()
..
}
}
You can call the API for getting the refresh token as below
POST https://yourdomain.com/oauth/token
Header
"Authorization": "Basic [base64encode(clientId:clientSecret)]"
Parameters
"grant_type": "refresh_token"
"refresh_token": "[yourRefreshToken]"
Please be noticed that, the
base64encode is the method to encrypt the client authorization. You can use online at https://www.base64encode.org/
the refresh_token is the String value of the grant_type
yourRefreshToken is the refresh token received with JWT access token
The result can be seen as
{
"token_type":"bearer",
"access_token":"eyJ0eXAiOiJK.iLCJpYXQiO.Dww7TC9xu_2s",
"expires_in":20,
"refresh_token":"7fd15938c823cf58e78019bea2af142f9449696a"
}
Good luck.

OAuth2Authentication.getPrincipal() returns client_id instead of username

I am using spring-security-oauth2-autoconfigure version 2.1.6.RELEASE to secure a resource server and am trying to get the username that is listed in my jwt. The payload of my jwt contains the client_id and username. When I try to get the Principal from the OAuth2Authentication object, it returns the client_id instead of the username.
For example, if the jwt payload contains:
client_id":"2v3r098kgipu053lph0cb9nfjb","username":"7e953975-0df2-49ff-9b23-cae0864384b7"
And my code is:
#GetMapping("/whoami")
public String whoami(#CurrentSecurityContext OAuth2Authentication auth) {
logger.debug("auth.name: {}", auth.getName() );
logger.debug("auth.principal: {}", auth.getPrincipal());
OAuth2Request req = auth.getOAuth2Request();
logger.debug("req.clientid: {}", req.getClientId() );
return "success";
}
I see the following in the log:
auth.name: 2v3r098kgipu053lph0cb9nfjb
auth.principal: 2v3r098kgipu053lph0cb9nfjb
req.clientid: 2v3r098kgipu053lph0cb9nfjb
What do I need to do to get the username from the jwt?
I was able to figure out the issue.
This is the OAuth2Authentication.getPrincipal() method:
public Object getPrincipal() {
return this.userAuthentication == null ? this.storedRequest.getClientId() : this.userAuthentication
.getPrincipal();
}
auth.getUserAuthentication() returns null so this explains the results I'm seeing.
The reason why this.userAuthentication is null is because the DefaultUserAuthenticationConverter.extractAuthentication(Map<String, ?> map) method tries to set the principal to map.get(USERNAME) where the map is the jwt and USERNAME = "user_name" instead of "username".
This question had a similar issue: Override UserAuthenticationConverter for JWT OAuth Tokens. I was able to use their solution and alter it for my needs. I also decided to use the sub from the jwt instead of username.

Error Response while getting jwt access token for google user with Google Credential Object

I am trying to get the jwt access tokens for each user of my gsuite domain using the GoogleCredential and JacksonFactory libraries.
Code sample -
GoogleCredential credential = new GoogleCredential.Builder().setTransport(httpTransport)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(clientEmail)
.setServiceAccountScopes(scopes)
.setServiceAccountPrivateKeyFromP12File(privateKeyFile)
.setServiceAccountUser(userEmail)
.build();
credential.refreshToken();
String accessToken = credential.getAccessToken();
All fields - clientEmail, scopes, key and userEmail are neither null nor empty
For a few number of users I am not able to get the access token and am getting this error
com.google.api.client.repackaged.com.google.common.base.Preconditions.checkNotNull(Preconditions.java:191) com.google.api.client.util.Preconditions.checkNotNull(Preconditions.java:127) com.google.api.client.json.jackson2.JacksonFactory.createJsonParser(JacksonFactory.java:96) com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:85) com.google.api.client.json.JsonObjectParser.parseAndClose(JsonObjectParser.java:81) com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:88) com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:287) com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307) com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:269) com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489) com.hubble.hubbleEngine.policyTypes.OAuth.getJWTAccessToken(OAuth.java:815)
This is happening only the first time, I am trying to get the access tokens. When I try to get the access tokens again for all users, I am able to get the access tokens for the users which were throwing the error the first time.
I debugged a bit and saw that the error gets generated from the following function present in com.google.api.client.auth.oauth2.TokenRequest
public final HttpResponse executeUnparsed() throws IOException {
// must set clientAuthentication as last execute interceptor in case it needs to sign request
HttpRequestFactory requestFactory =
transport.createRequestFactory(new HttpRequestInitializer() {
public void initialize(HttpRequest request) throws IOException {
if (requestInitializer != null) {
requestInitializer.initialize(request);
}
final HttpExecuteInterceptor interceptor = request.getInterceptor();
request.setInterceptor(new HttpExecuteInterceptor() {
public void intercept(HttpRequest request) throws IOException {
if (interceptor != null) {
interceptor.intercept(request);
}
if (clientAuthentication != null) {
clientAuthentication.intercept(request);
}
}
});
}
});
// make request
HttpRequest request =
requestFactory.buildPostRequest(tokenServerUrl, new UrlEncodedContent(this));
request.setParser(new JsonObjectParser(jsonFactory));
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
if (response.isSuccessStatusCode()) {
return response;
}
throw TokenResponseException.from(jsonFactory, response);
}
The request.execute() hits the "https://accounts.google.com/o/oauth2/token" to get the token but it is throwing some error response. Due to this it throws the TokenResponseException mentioned at last. Here, the response.getContent() is null due to which the whole null exception is occuring.
Is there a way to know, which kind of error response is thrown by the call. (>300 or <200)? Or why such a case is happening ?

Why OAuth2AccessTokenSupport always send POST request ??

I'm working with a Spring Boot + Spring Security OAuth2 to consume the Restful Oauth2 service.
Our Oauth2 service is always expects HTTP GET But OAuth2AccessTokenSupport always sending HTTP POST.
Result:
resulted in 405 (Method Not Allowed); invoking error handler
protected OAuth2AccessToken retrieveToken(AccessTokenRequest request, OAuth2ProtectedResourceDetails resource,
MultiValueMap<String, String> form, HttpHeaders headers) throws OAuth2AccessDeniedException {
try {
this.authenticationHandler.authenticateTokenRequest(resource, form, headers);
this.tokenRequestEnhancer.enhance(request, resource, form, headers);
AccessTokenRequest copy = request;
ResponseExtractor delegate = getResponseExtractor();
ResponseExtractor extractor = new ResponseExtractor(copy, delegate) {
public OAuth2AccessToken extractData(ClientHttpResponse response) throws IOException {
if (response.getHeaders().containsKey("Set-Cookie")) {
this.val$copy.setCookie(response.getHeaders().getFirst("Set-Cookie"));
}
return ((OAuth2AccessToken) this.val$delegate.extractData(response));
}
};
return ((OAuth2AccessToken) getRestTemplate().execute(getAccessTokenUri(resource, form), getHttpMethod(),
getRequestCallback(resource, form, headers), extractor, form.toSingleValueMap()));
} catch (OAuth2Exception oe) {
throw new OAuth2AccessDeniedException("Access token denied.", resource, oe);
} catch (RestClientException rce) {
throw new OAuth2AccessDeniedException("Error requesting access token.", resource, rce);
}
}
<b>protected HttpMethod getHttpMethod() {
return HttpMethod.POST;
}</b>
protected String getAccessTokenUri(OAuth2ProtectedResourceDetails resource, MultiValueMap<String, String> form) {
String accessTokenUri = resource.getAccessTokenUri();
if (this.logger.isDebugEnabled()) {
this.logger.debug(new StringBuilder().append("Retrieving token from ").append(accessTokenUri).toString());
}
StringBuilder builder = new StringBuilder(accessTokenUri);
String separator;
if (getHttpMethod() == HttpMethod.GET) {
separator = "?";
if (accessTokenUri.contains("?")) {
separator = "&";
}
for (String key : form.keySet()) {
builder.append(separator);
builder.append(new StringBuilder().append(key).append("={").append(key).append("}").toString());
separator = "&";
}
}
return builder.toString();
}
Can Anyone explain me why OAuth2AccessTokenSupport always returns POST and
How to send HTTP GET request
To enable GET requests for the token endpoint, you need to add the following in your AuthorizationServerConfigurerAdapter:
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
As for why only POST by default: I think that is due to GET requests potentially sending username and password information as request params (this is certainly the case for password grant). These may well be visible in web server logs, while POST body data is not.
Indeed the RFC for OAuth2 declares that the client must use HTTP POST when requesting an access token (https://www.rfc-editor.org/rfc/rfc6749#section-3.2)

Resources