In our app, a user’s role can change within a session. Our app uses JWT authentication based on the approach in this Vaadin blog. In versions up to 24.3.8, we accomplished this role change by writing a JWT with the updated role (code below). In v 24.3.9, we understand that JWT tokens started refreshing on every request, and our approach no longer works. We believe maybe our token is being intercepted and overwritten on the way out the door. Any ideas on how to tweak our current method, or try a new one? We suspect it’s just a matter of overriding a method in VaadinWebSecurity
, but not sure where to start. Many thanks!!
@marcoc_753 have you run into this before?
//Update a user's JWT token when they're granted a new role.
public void updateUserRoles(AppUser user, HttpServletRequest request, HttpServletResponse response) {
//Grab the current jwt.headerAndPayload cookie
String jwt = getJwtFromCookies(request);
if (jwt != null) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Jwt principal = (Jwt) auth.getPrincipal();
Map<String, Object> currentClaims = principal.getClaims();
Map<String, Object> updatedClaims = new HashMap<>();
//Copy the current claims (except for iat) to a new claims map
for (Map.Entry<String,Object> entry : currentClaims.entrySet()) {
if (!entry.getKey().equals("iat")) {
updatedClaims.put(entry.getKey(), entry.getValue());
}
}
String internalRole = user.getRoleAsString();
List<String> roleList = List.of(internalRole.replace("ROLE_", ""));
//Add the new role to the updated claims.
updatedClaims.put("roles", roleList);
//Wrap the claims up into a JWT and sign it.
String newJwt = Jwts.builder()
.setClaims(updatedClaims)
.signWith(secretKey)
.setExpiration(Date.from(Instant.ofEpochSecond(4622470422L)))
.compact();
//Update the user's cookies with their new role.
setJwtInCookies(newJwt, response);
//Update the current session with the new role
Authentication newAuth = new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(newAuth);
}
}
/**
* Parse a JWT token into two cookies (jwt.headerAndPayload and jwt.signature)
* Send the cookies in the servlet response
* @param jwt
* @param response
*/
private void setJwtInCookies(String jwt, HttpServletResponse response) {
String[] jwtParts = jwt.split("\\.");
if (jwtParts.length == 3) {
String headerAndPayload = jwtParts[0] + "." + jwtParts[1];
String signature = jwtParts[2];
Cookie headerAndPayloadCookie = new Cookie(JWT_HEADER_AND_PAYLOAD_COOKIE_NAME, headerAndPayload);
headerAndPayloadCookie.setPath("/");
headerAndPayloadCookie.setHttpOnly(true);
headerAndPayloadCookie.setMaxAge(60 * 60 * 24); // 1 day
Cookie signatureCookie = new Cookie(JWT_SIGNATURE_COOKIE_NAME, signature);
signatureCookie.setPath("/");
signatureCookie.setHttpOnly(true);
signatureCookie.setMaxAge(60 * 60 * 24); // 1 day
response.addCookie(headerAndPayloadCookie);
response.addCookie(signatureCookie);
}
}