public String validateJWTAndExtractUser(String signedJwt) throws ParseException, AuthenticationException {
    Preconditions.checkNotNull(jwksProvider);
    Preconditions.checkNotNull(signedJwt, "No token found");
    final SignedJWT parsedJwt = SignedJWT.parse(signedJwt);
    List<JWK> matchedJWKS = jwksProvider.getJWKs(parsedJwt.getHeader());
    if (matchedJWKS.isEmpty()) {
      throw new AuthenticationException("Failed to find matched JWKs with the JWT header: " + parsedJwt.getHeader());
    }

    // verify signature
    Exception lastException = null;
    for (JWK matchedJWK : matchedJWKS) {
      String keyID = matchedJWK.getKeyID() == null ? "null" : matchedJWK.getKeyID();
      try {
        JWSVerifier verifier = getVerifier(parsedJwt.getHeader(), matchedJWK);
        if (parsedJwt.verify(verifier)) {
          LOG.debug("Verified JWT {} by JWK {}", parsedJwt.getPayload(), keyID);
          break;
        }
      } catch (Exception e) {
        lastException = e;
        LOG.warn("Failed to verify JWT {} by JWK {}", parsedJwt.getPayload(), keyID, e);
      }
    }
    // We use only the last seven characters to let a user can differentiate exceptions for different JWT
    int startIndex = Math.max(0, signedJwt.length() - 7);
    String lastSevenChars = signedJwt.substring(startIndex);
    if (parsedJwt.getState() != JWSObject.State.VERIFIED) {
      throw new AuthenticationException("Failed to verify the JWT signature (ends with " + lastSevenChars + ")",
          lastException);
    }

    // verify claims
    JWTClaimsSet claimsSet = parsedJwt.getJWTClaimsSet();
    Date expirationTime = claimsSet.getExpirationTime();
    if (expirationTime != null) {
      Date now = new Date();
      if (now.after(expirationTime)) {
        LOG.warn("Rejecting an expired JWT: {}", parsedJwt.getPayload());
        throw new AuthenticationException("JWT (ends with " + lastSevenChars + ") has been expired");
      }
    }

    // We assume the subject of claims is the query user
    return claimsSet.getSubject();
  }
This class is used to validate JWT. JWKS is fetched during instantiation and kept in the memory.
We disallow JWT signature verification with symmetric key, because that means anyone can get the same key
and use it to sign a JWT.
This is cloned from JWTValidator in HS2 so as to NOT have any dependency on HS2 code.
standalone-metastore/metastore-server/src/main/java/org/apache/hadoop/hive/metastore/auth/jwt/JWTValidator.java