JwtManager.java
/*
* Copyright © 2023-2025 The CTAN Team and individual authors
*
* This file is distributed under the 3-clause BSD license.
* See file LICENSE for details.
*/
package org.ctan.site.services.account;
import java.util.Date;
import java.util.UUID;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.JWTVerifier;
import lombok.NonNull;
/**
* The class <code>JwtManager</code> contains static utility methods for dealing
* with JWTs.
*
* @author <a href="mailto:gene@ctan.org">Gerd Neugebauer</a>
*/
public class JwtManager {
/**
* The field <code>SUBJECT</code> contains the expected subject of the JWT.
*/
private static final String SUBJECT = "ctan.org site";
/**
* The field <code>ISSUER</code> contains the issuer field of the JWT.
*/
private static final String ISSUER = "CTAN.org";
/**
* The field <code>SECRET</code> contains the dynamically generated random
* secret.
*/
private static final String SECRET = Double.toHexString(Math.random());
/**
* The field <code>ALGORITHM</code> contains the algorithm to sign the JWT.
*/
private static final Algorithm ALGORITHM = Algorithm.HMAC512(SECRET);
/**
* The field <code>VERIFIER</code> contains the verifier. It is visible
* outside for testing purpose.
*/
protected static final JWTVerifier VERIFIER_AUTH = JWT.require(ALGORITHM)
.withIssuer(ISSUER)
.withSubject(SUBJECT)
.withClaim("for", "A")
.build();
/**
* The field <code>VERIFIER</code> contains the verifier. It is visible
* outside for testing purpose.
*/
protected static final JWTVerifier VERIFIER_REFRESH = JWT.require(ALGORITHM)
.withIssuer(ISSUER)
.withSubject(SUBJECT)
.withClaim("for", "R")
.build();
/**
* Create a JWT with standard options set.
*
* @param account the user id;
* @param ttl time to live in ms
* @return a new JWT
*/
private static String create(String account, String type, long ttl) {
return JWT.create()
.withIssuer(ISSUER)
.withSubject(SUBJECT)
.withClaim("account", account)
.withClaim("for", type)
.withIssuedAt(new Date())
.withExpiresAt(new Date(System.currentTimeMillis() + ttl))
.withJWTId(UUID.randomUUID().toString())
.sign(ALGORITHM);
}
/**
* Create a JWT with standard options set for authentication.
*
* @param account the user id;
* @return a new JWT
*/
public static String createAuth(@NonNull String account) {
return create(account, "A", 1000L * 60 * 60 * 24);
}
/**
* Create a JWT with standard options set for refresh.
*
* @param account the user id;
* @return a new JWT
*/
public static String createRefresh(@NonNull String account) {
return create(account, "R", 1000L * 60 * 60 * 24 * 100);
}
/**
* The method <code>verifyAuth</code> provides means to verify a JWT and
* return the account.
*
* @param token the JWT
* @return the verified user id or {@code null}
*/
public static String verifyAuth(String token) {
try {
return VERIFIER_AUTH.verify(token)
.getClaim("account")
.asString();
} catch (JWTVerificationException e) {
return null;
}
}
/**
* The method <code>verify</code> provides means to verify a JWT and return
* the account.
*
* @param token the JWT
* @return the verified user id or {@code null}
*/
public static String verifyRefresh(String token) {
try {
return VERIFIER_REFRESH.verify(token)
.getClaim("account")
.asString();
} catch (JWTVerificationException e) {
return null;
}
}
/**
* This is the constructor for <code>JwtManager</code>.
*
* <p>
* Attention: It can not be used since this class exposes static methods
* only!
* </p>
*/
private JwtManager() {
}
}