All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.entur.jwt.verifier.auth0.Auth0JwtVerifierFactory Maven / Gradle / Ivy
package org.entur.jwt.verifier.auth0;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.entur.jwt.jwk.JwkProvider;
import org.entur.jwt.jwk.JwksProvider;
import org.entur.jwt.jwk.UrlJwksProvider;
import org.entur.jwt.jwk.auth0.Auth0JwkProviderBuilder;
import org.entur.jwt.jwk.auth0.Auth0JwkReader;
import org.entur.jwt.verifier.JwtClaimExtractor;
import org.entur.jwt.verifier.JwtClaimVerifier;
import org.entur.jwt.verifier.JwtVerifier;
import org.entur.jwt.verifier.JwtVerifierFactory;
import org.entur.jwt.verifier.config.JwkProperties;
import org.entur.jwt.verifier.config.JwkCacheProperties;
import org.entur.jwt.verifier.config.JwtClaimConstraintProperties;
import org.entur.jwt.verifier.config.JwtClaimsProperties;
import org.entur.jwt.verifier.config.JwkLocationProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.entur.jwt.verifier.config.JwkOutageCacheProperties;
import org.entur.jwt.verifier.config.JwkPreemptiveCacheProperties;
import org.entur.jwt.verifier.config.JwkRateLimitProperties;
import org.entur.jwt.verifier.config.JwkRetryProperties;
import org.entur.jwt.verifier.config.JwtTenantProperties;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.auth0.jwt.interfaces.Verification;
import com.auth0.jwk.Jwk;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
public class Auth0JwtVerifierFactory implements JwtVerifierFactory {
private static Logger log = LoggerFactory.getLogger(Auth0JwtVerifierFactory.class);
private final JwtClaimExtractor extractor;
public Auth0JwtVerifierFactory(JwtClaimExtractor extractor) {
this.extractor = extractor;
}
@Override
public JwtVerifier getVerifier(Map tenants, JwkProperties jwkConfiguration, JwtClaimsProperties claims) {
Map verifiers = new HashMap<>(); // thread safe for read access
boolean healthIndicator = jwkConfiguration.getHealthIndicator().isEnabled();
List> statusProviders = new ArrayList<>();
List valueConstraints = getValueConstraints(claims);
for (Entry entry : tenants.entrySet()) {
JwtTenantProperties tenantConfiguration = entry.getValue();
if(!tenantConfiguration.isEnabled()) {
continue;
}
JwkLocationProperties tenantJwkConfiguration = tenantConfiguration.getJwk();
if (tenantJwkConfiguration == null) {
throw new IllegalStateException("Missing JWK location for " + entry.getKey());
}
log.info("Configure tenant '{}' with issuer '{}' and JWK location {}", entry.getKey(), tenantConfiguration.getIssuer(), tenantConfiguration.getJwk().getLocation());
String location = tenantJwkConfiguration.getLocation();
if (location == null) {
throw new IllegalStateException("Missing JWK location for " + entry.getKey());
}
URL url;
try {
url = new URL(location);
} catch (MalformedURLException e) {
throw new IllegalArgumentException("Invalid location " + tenantJwkConfiguration.getLocation() + " for " + entry.getKey());
}
UrlJwksProvider urlProvider = new UrlJwksProvider<>(url, new Auth0JwkReader(), jwkConfiguration.getConnectTimeout(), jwkConfiguration.getReadTimeout());
Auth0JwkProviderBuilder builder = new Auth0JwkProviderBuilder(urlProvider);
JwkRateLimitProperties rateLimiting = jwkConfiguration.getRateLimit();
if (rateLimiting != null && rateLimiting.isEnabled()) {
double tokensPerSecond = rateLimiting.getRefillRate();
double secondsPerToken = 1d / tokensPerSecond;
int millisecondsPerToken = (int)(secondsPerToken * 1000); // note quantization, ms precision is sufficient
builder.rateLimited(rateLimiting.getBucketSize(), 1, Duration.ofMillis(millisecondsPerToken));
} else {
builder.rateLimited(false);
}
JwkCacheProperties cache = jwkConfiguration.getCache();
if (cache != null && cache.isEnabled()) {
builder.cached(Duration.ofSeconds(cache.getTimeToLive()), Duration.ofSeconds(cache.getRefreshTimeout()));
JwkPreemptiveCacheProperties preemptive = cache.getPreemptive();
if (preemptive != null && preemptive.isEnabled()) {
builder.preemptiveCacheRefresh(Duration.ofSeconds(preemptive.getTimeToExpires()));
} else {
builder.preemptiveCacheRefresh(false);
}
} else {
builder.cached(false);
builder.preemptiveCacheRefresh(false);
}
JwkRetryProperties retrying = jwkConfiguration.getRetry();
builder.retrying(retrying != null && retrying.isEnabled());
JwkOutageCacheProperties outageCache = jwkConfiguration.getOutageCache();
if (outageCache != null && outageCache.isEnabled()) {
builder.outageCached(Duration.ofSeconds(outageCache.getTimeToLive()));
} else {
builder.outageCached(false);
}
builder.health(healthIndicator);
JwkProvider jwkProvider = builder.build();
if (healthIndicator) {
jwkProvider.getHealth(false); // verify that health is supported
statusProviders.add(jwkProvider);
}
JwtKeyProvider keyProvider = new JwtKeyProvider(jwkProvider);
Verification jwtBuilder = JWT.require(Algorithm.RSA256(keyProvider)).withIssuer(tenantConfiguration.getIssuer()) // strictly not necessary, but lets add it anyways
.acceptExpiresAt(claims.getExpiresAtLeeway()).acceptIssuedAt(claims.getIssuedAtLeeway());
// value verification directly supported by auth0
addValueConstraints(jwtBuilder, valueConstraints);
JWTVerifier verifier = new AudienceJWTVerifier(jwtBuilder.build(), claims.getAudiences());
verifiers.put(tenantConfiguration.getIssuer(), verifier);
}
JwtVerifier verifier;
if (!statusProviders.isEmpty()) {
verifier = new MultiTenantJwtVerifier(verifiers, statusProviders);
} else {
verifier = new MultiTenantJwtVerifier(verifiers);
}
List dataTypeConstraints = getDataTypeConstraints(claims);
if (!dataTypeConstraints.isEmpty()) {
return getVerifierForDataTypes(verifier, dataTypeConstraints);
}
return verifier;
}
private void addValueConstraints(Verification jwtBuilder, List valueConstraints) {
for (JwtClaimConstraintProperties r : valueConstraints) {
switch (r.getType()) {
case "integer": {
add(jwtBuilder, r);
break;
}
case "boolean": {
jwtBuilder.withClaim(r.getName(), Boolean.parseBoolean(r.getValue()));
break;
}
case "string": {
jwtBuilder.withClaim(r.getName(), r.getValue());
break;
}
case "double": {
jwtBuilder.withClaim(r.getName(), Double.parseDouble(r.getValue()));
break;
}
default: {
throw new IllegalArgumentException("Unexpected claim type '" + r.getType() + "'");
}
}
}
}
private JwtVerifier getVerifierForDataTypes(JwtVerifier verifier, List dataTypeConstraints) {
// add claim-verifying wrapper
Map> types = new HashMap<>(dataTypeConstraints.size() * 2);
for (JwtClaimConstraintProperties r : dataTypeConstraints) {
switch (r.getType()) {
case "integer": {
types.put(r.getName(), Long.class); // integers can be cast to long, so should not be a problem.
break;
}
case "boolean": {
types.put(r.getName(), Boolean.class);
break;
}
case "string": {
types.put(r.getName(), String.class);
break;
}
case "double": {
types.put(r.getName(), Double.class);
break;
}
default: {
throw new IllegalArgumentException("Unexpected claim type '" + r.getType() + "'");
}
}
}
return new JwtClaimVerifier<>(verifier, extractor, types, Collections.emptyMap());
}
private void add(Verification jwtBuilder, JwtClaimConstraintProperties r) {
Long l = Long.parseLong(r.getValue());
if (l <= Integer.MAX_VALUE) {
jwtBuilder.withClaim(r.getName(), l.intValue());
} else {
jwtBuilder.withClaim(r.getName(), l);
}
}
public List getValueConstraints(JwtClaimsProperties claims) {
List dataValueConstraints = new ArrayList<>();
for (JwtClaimConstraintProperties r : claims.getRequire()) {
if (r.getValue() != null) {
log.info("Require claim {} of type {}", r.getName(), r.getType());
dataValueConstraints.add(r);
}
}
return dataValueConstraints;
}
public List getDataTypeConstraints(JwtClaimsProperties claims) {
List dataTypeConstraints = new ArrayList<>();
for (JwtClaimConstraintProperties r : claims.getRequire()) {
if (r.getValue() == null) {
log.info("Require claim {} value {} of type {}", r.getName(), r.getValue(), r.getType());
dataTypeConstraints.add(r);
}
}
return dataTypeConstraints;
}
}