tomekn
tomekn

Reputation: 31

JWT authentication without fetching user details on each request

I'm implementing JWT authentication in Spring Security. I have predefined roles, eg. regular user, admin etc.

I have following payload of token:

{
  "sub": "nick",
  "iat": "<some_date>",
  "exp": "<some_date+1h>",
  "scopes": [
    "ROLE_USER",
    "ROLE_ADMIN"
  ]
}

Most of the implementations I saw so far retrieve user details from database based on id/username/email and then use this data to create authentication (eg. by authenticating UsernamePasswordAuthenticationToken). For me it's actually a preferrable way to go, because I always have up to date privileges and restrictions (eg. whether user was banned) and time overhead is not so big compared to benefits.

I'm just curious how could I implement authorization with Spring Security based only on roles included in incoming request (in its authorization header token). I want just to be able to access user identifier in controller after the request is routed appropriately. Would token validation based only on checking expiration time and roles validity be sufficient?

Upvotes: 3

Views: 2357

Answers (1)

Markus Pscheidt
Markus Pscheidt

Reputation: 7331

Since the information about the user's roles are part of your token (in the scopesclaim), it's possible to create the authentication object based on the token alone, without further access to the database.

In the following example, the createAuthentication() method transforms the token into a Spring Security Authentication object.

public class JWTFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
        throws IOException, ServletException {

        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        String jwt = // resolveToken(httpServletRequest);

        this.createAuthentication(jwt).ifPresent(authentication -> {
            SecurityContextHolder.getContext().setAuthentication(authentication);
        });

        filterChain.doFilter(servletRequest, servletResponse);
    }


    public Optional<Authentication> createAuthentication(String token) {

        Jws<Claims> jwsClaims = validateToken(token);
        if (jwsClaims == null) {
            return Optional.empty();
        }

        Claims claims = jwsClaims.getBody();

        String scopesString = claims.get("scopes").toString();
        String[] authStrings = scopesString.split(",");

        Collection<? extends GrantedAuthority> authorities =
            Arrays.stream(authStrings)
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());

        String subject = claims.getSubject();
        org.springframework.security.core.userdetails.User principal = new User(subject, "", authorities);

        return Optional.of(new UsernamePasswordAuthenticationToken(principal, token, authorities));
    }

    private Jws<Claims> validateToken(String authToken) {
        try {
            Jws<Claims> claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(authToken);
            return claims;
        } catch ...
    }

}

Upvotes: 7

Related Questions