class JwtService {

    public String generateSignedToken(final LoginAuthenticationToken authenticationToken) throws JwtException {
        if (authenticationToken == null) {
            throw new IllegalArgumentException("Cannot generate a JWT for a null authentication token");
        }

        // Set expiration from the token
        final Calendar expiration = Calendar.getInstance();
        expiration.setTimeInMillis(authenticationToken.getExpiration());

        final Object principal = authenticationToken.getPrincipal();
        if (principal == null || StringUtils.isEmpty(principal.toString())) {
            final String errorMessage = "Cannot generate a JWT for a token with an empty identity issued by " + authenticationToken.getIssuer();
            logger.error(errorMessage);
            throw new JwtException(errorMessage);
        }

        // Create a JWT with the specified authentication
        final String identity = principal.toString();
        final String username = authenticationToken.getName();

        try {
            // Get/create the key for this user
            final Key key = keyService.getOrCreateKey(identity);
            final byte[] keyBytes = key.getKey().getBytes(StandardCharsets.UTF_8);

            logger.trace("Generating JWT for " + authenticationToken);

            // TODO: Implement "jti" claim with nonce to prevent replay attacks and allow blacklisting of revoked tokens
            // Build the token
            return Jwts.builder().setSubject(identity)
                    .setIssuer(authenticationToken.getIssuer())
                    .setAudience(authenticationToken.getIssuer())
                    .claim(USERNAME_CLAIM, username)
                    .claim(KEY_ID_CLAIM, key.getId())
                    .setExpiration(expiration.getTime())
                    .setIssuedAt(Calendar.getInstance().getTime())
                    .signWith(SIGNATURE_ALGORITHM, keyBytes).compact();
        } catch (NullPointerException | AdministrationException e) {
            final String errorMessage = "Could not retrieve the signing key for JWT for " + identity;
            logger.error(errorMessage, e);
            throw new JwtException(errorMessage, e);
        }
    }

    public  JwtService(final KeyService keyService);

}