JWTValidator.java
package com.hypixel.hytale.server.core.auth;
import com.hypixel.hytale.logger.HytaleLogger;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.crypto.Ed25519Verifier;
import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.OctetKeyPair;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class JWTValidator {
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
private static final long CLOCK_SKEW_SECONDS = ;
JWSAlgorithm SUPPORTED_ALGORITHM;
SessionServiceClient sessionServiceClient;
String expectedIssuer;
String expectedAudience;
JWKSet cachedJwkSet;
jwksCacheExpiry;
jwksCacheDurationMs;
ReentrantLock jwksFetchLock;
CompletableFuture<JWKSet> pendingFetch;
{
.jwksCacheDurationMs = TimeUnit.HOURS.toMillis();
.jwksFetchLock = ();
.pendingFetch = ;
.sessionServiceClient = sessionServiceClient;
.expectedIssuer = expectedIssuer;
.expectedAudience = expectedAudience;
}
JWTClaims {
(accessToken.isEmpty()) {
LOGGER.at(Level.WARNING).log();
;
} {
{
SignedJWT.parse(accessToken);
signedJWT.getHeader().getAlgorithm();
(!SUPPORTED_ALGORITHM.equals(algorithm)) {
LOGGER.at(Level.WARNING).log(, algorithm);
;
} (!.verifySignatureWithRetry(signedJWT)) {
LOGGER.at(Level.WARNING).log();
;
} {
signedJWT.getJWTClaimsSet();
();
claims.issuer = claimsSet.getIssuer();
claims.audience = claimsSet.getAudience() != && !claimsSet.getAudience().isEmpty() ? (String)claimsSet.getAudience().get() : ;
claims.subject = claimsSet.getSubject();
claims.username = claimsSet.getStringClaim();
claims.ipAddress = claimsSet.getStringClaim();
claims.issuedAt = claimsSet.getIssueTime() != ? claimsSet.getIssueTime().toInstant().getEpochSecond() : ;
claims.expiresAt = claimsSet.getExpirationTime() != ? claimsSet.getExpirationTime().toInstant().getEpochSecond() : ;
claims.notBefore = claimsSet.getNotBeforeTime() != ? claimsSet.getNotBeforeTime().toInstant().getEpochSecond() : ;
Map<String, Object> cnfClaim = claimsSet.getJSONObjectClaim();
(cnfClaim != ) {
claims.certificateFingerprint = (String)cnfClaim.get();
}
(!.expectedIssuer.equals(claims.issuer)) {
LOGGER.at(Level.WARNING).log(, .expectedIssuer, claims.issuer);
;
} (!.expectedAudience.equals(claims.audience)) {
LOGGER.at(Level.WARNING).log(, .expectedAudience, claims.audience);
;
} {
Instant.now().getEpochSecond();
(claims.expiresAt != && nowSeconds >= claims.expiresAt + ) {
LOGGER.at(Level.WARNING).log(, claims.expiresAt, nowSeconds);
;
} (claims.notBefore != && nowSeconds < claims.notBefore - ) {
LOGGER.at(Level.WARNING).log(, claims.notBefore, nowSeconds);
;
} (claims.issuedAt != && claims.issuedAt > nowSeconds + ) {
LOGGER.at(Level.WARNING).log(, claims.issuedAt, nowSeconds);
;
} (!CertificateUtil.validateCertificateBinding(claims.certificateFingerprint, clientCert)) {
LOGGER.at(Level.WARNING).log();
;
} {
LOGGER.at(Level.INFO).log(, claims.username, claims.subject);
claims;
}
}
}
} (ParseException e) {
((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(e)).log();
;
} (Exception e) {
((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(e)).log();
;
}
}
}
{
{
signedJWT.getHeader().getKeyID();
;
(JWK jwk : jwkSet.getKeys()) {
(jwk OctetKeyPair okp) {
(keyId == || keyId.equals(jwk.getKeyID())) {
ed25519Key = okp;
;
}
}
}
(ed25519Key == ) {
LOGGER.at(Level.WARNING).log(, keyId);
;
} {
(ed25519Key);
signedJWT.verify(verifier);
(valid) {
LOGGER.at(Level.FINE).log(, keyId);
}
valid;
}
} (Exception e) {
((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(e)).log();
;
}
}
JWKSet {
.getJwkSet();
}
JWKSet {
System.currentTimeMillis();
(!forceRefresh && .cachedJwkSet != && now < .jwksCacheExpiry) {
.cachedJwkSet;
} {
.jwksFetchLock.lock();
JWKSet var4;
{
(forceRefresh || .cachedJwkSet == || now >= .jwksCacheExpiry) {
CompletableFuture<JWKSet> existing = .pendingFetch;
(existing != && !existing.isDone()) {
.jwksFetchLock.unlock();
{
(JWKSet)existing.join();
var5;
} {
.jwksFetchLock.lock();
}
}
(forceRefresh) {
LOGGER.at(Level.INFO).log();
}
.pendingFetch = CompletableFuture.supplyAsync(::fetchJwksFromService);
(JWKSet).pendingFetch.join();
}
var4 = .cachedJwkSet;
} {
.jwksFetchLock.unlock();
}
var4;
}
}
JWKSet {
SessionServiceClient. .sessionServiceClient.getJwks();
(jwksResponse != && jwksResponse.keys != && jwksResponse.keys.length != ) {
{
ArrayList<JWK> jwkList = ();
(SessionServiceClient.JwkKey key : jwksResponse.keys) {
.convertToJWK(key);
(jwk != ) {
jwkList.add(jwk);
}
}
(jwkList.isEmpty()) {
LOGGER.at(Level.WARNING).log();
.cachedJwkSet;
} {
(jwkList);
.cachedJwkSet = newSet;
.jwksCacheExpiry = System.currentTimeMillis() + .jwksCacheDurationMs;
LOGGER.at(Level.INFO).log(, jwkList.size());
newSet;
}
} (Exception e) {
((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(e)).log();
.cachedJwkSet;
}
} {
LOGGER.at(Level.WARNING).log();
.cachedJwkSet;
}
}
{
.getJwkSet();
(jwkSet == ) {
;
} (.verifySignature(signedJWT, jwkSet)) {
;
} {
LOGGER.at(Level.INFO).log();
.getJwkSet();
freshJwkSet != && freshJwkSet != jwkSet ? .verifySignature(signedJWT, freshJwkSet) : ;
}
}
JWK {
(!.equals(key.kty)) {
LOGGER.at(Level.WARNING).log(, key.kty);
;
} {
{
String.format(, key.crv, key.x, key.kid);
JWK.parse(json);
} (Exception e) {
((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(e)).log();
;
}
}
}
{
.jwksFetchLock.lock();
{
.cachedJwkSet = ;
.jwksCacheExpiry = ;
.pendingFetch = ;
} {
.jwksFetchLock.unlock();
}
}
IdentityTokenClaims {
(identityToken.isEmpty()) {
LOGGER.at(Level.WARNING).log();
;
} {
{
SignedJWT.parse(identityToken);
signedJWT.getHeader().getAlgorithm();
(!SUPPORTED_ALGORITHM.equals(algorithm)) {
LOGGER.at(Level.WARNING).log(, algorithm);
;
} (!.verifySignatureWithRetry(signedJWT)) {
LOGGER.at(Level.WARNING).log();
;
} {
signedJWT.getJWTClaimsSet();
();
claims.issuer = claimsSet.getIssuer();
claims.subject = claimsSet.getSubject();
claims.username = claimsSet.getStringClaim();
claims.issuedAt = claimsSet.getIssueTime() != ? claimsSet.getIssueTime().toInstant().getEpochSecond() : ;
claims.expiresAt = claimsSet.getExpirationTime() != ? claimsSet.getExpirationTime().toInstant().getEpochSecond() : ;
claims.notBefore = claimsSet.getNotBeforeTime() != ? claimsSet.getNotBeforeTime().toInstant().getEpochSecond() : ;
claims.scope = claimsSet.getStringClaim();
(!.expectedIssuer.equals(claims.issuer)) {
LOGGER.at(Level.WARNING).log(, .expectedIssuer, claims.issuer);
;
} {
Instant.now().getEpochSecond();
(claims.expiresAt == ) {
LOGGER.at(Level.WARNING).log();
;
} (nowSeconds >= claims.expiresAt + ) {
LOGGER.at(Level.WARNING).log(, claims.expiresAt, nowSeconds);
;
} (claims.notBefore != && nowSeconds < claims.notBefore - ) {
LOGGER.at(Level.WARNING).log(, claims.notBefore, nowSeconds);
;
} (claims.issuedAt != && claims.issuedAt > nowSeconds + ) {
LOGGER.at(Level.WARNING).log(, claims.issuedAt, nowSeconds);
;
} (claims.getSubjectAsUUID() == ) {
LOGGER.at(Level.WARNING).log();
;
} {
LOGGER.at(Level.INFO).log(, claims.username, claims.subject);
claims;
}
}
}
} (ParseException e) {
((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(e)).log();
;
} (Exception e) {
((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(e)).log();
;
}
}
}
SessionTokenClaims {
(sessionToken.isEmpty()) {
LOGGER.at(Level.WARNING).log();
;
} {
{
SignedJWT.parse(sessionToken);
signedJWT.getHeader().getAlgorithm();
(!SUPPORTED_ALGORITHM.equals(algorithm)) {
LOGGER.at(Level.WARNING).log(, algorithm);
;
} (!.verifySignatureWithRetry(signedJWT)) {
LOGGER.at(Level.WARNING).log();
;
} {
signedJWT.getJWTClaimsSet();
();
claims.issuer = claimsSet.getIssuer();
claims.subject = claimsSet.getSubject();
claims.issuedAt = claimsSet.getIssueTime() != ? claimsSet.getIssueTime().toInstant().getEpochSecond() : ;
claims.expiresAt = claimsSet.getExpirationTime() != ? claimsSet.getExpirationTime().toInstant().getEpochSecond() : ;
claims.notBefore = claimsSet.getNotBeforeTime() != ? claimsSet.getNotBeforeTime().toInstant().getEpochSecond() : ;
(!.expectedIssuer.equals(claims.issuer)) {
LOGGER.at(Level.WARNING).log(, .expectedIssuer, claims.issuer);
;
} {
Instant.now().getEpochSecond();
(claims.expiresAt == ) {
LOGGER.at(Level.WARNING).log();
;
} (nowSeconds >= claims.expiresAt + ) {
LOGGER.at(Level.WARNING).log(, claims.expiresAt, nowSeconds);
;
} (claims.notBefore != && nowSeconds < claims.notBefore - ) {
LOGGER.at(Level.WARNING).log(, claims.notBefore, nowSeconds);
;
} (claims.issuedAt != && claims.issuedAt > nowSeconds + ) {
LOGGER.at(Level.WARNING).log(, claims.issuedAt, nowSeconds);
;
} {
LOGGER.at(Level.INFO).log();
claims;
}
}
}
} (ParseException e) {
((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(e)).log();
;
} (Exception e) {
((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(e)).log();
;
}
}
}
{
SUPPORTED_ALGORITHM = JWSAlgorithm.EdDSA;
}
{
String issuer;
String subject;
Long issuedAt;
Long expiresAt;
Long notBefore;
{
}
}
{
String issuer;
String subject;
String username;
Long issuedAt;
Long expiresAt;
Long notBefore;
String scope;
{
}
UUID {
(.subject == ) {
;
} {
{
UUID.fromString(.subject);
} (IllegalArgumentException var2) {
;
}
}
}
String[] getScopes() {
.scope != && !.scope.isEmpty() ? .scope.split() : [];
}
{
(String s : .getScopes()) {
(s.equals(targetScope)) {
;
}
}
;
}
}
{
String issuer;
String audience;
String subject;
String username;
String ipAddress;
Long issuedAt;
Long expiresAt;
Long notBefore;
String certificateFingerprint;
{
}
UUID {
(.subject == ) {
;
} {
{
UUID.fromString(.subject);
} (IllegalArgumentException var2) {
;
}
}
}
}
}