com.nimbusds.jose.jwk.source.JWKSourceBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of nimbus-jose-jwt Show documentation
Show all versions of nimbus-jose-jwt Show documentation
Java library for Javascript Object Signing and Encryption (JOSE) and
JSON Web Tokens (JWT)
The newest version!
/*
* nimbus-jose-jwt
*
* Copyright 2012-2022, Connect2id Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
* this file except in compliance with the License. You may obtain a copy of the
* License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package com.nimbusds.jose.jwk.source;
import java.net.URL;
import java.util.Objects;
import com.nimbusds.jose.proc.SecurityContext;
import com.nimbusds.jose.util.DefaultResourceRetriever;
import com.nimbusds.jose.util.ResourceRetriever;
import com.nimbusds.jose.util.events.EventListener;
import com.nimbusds.jose.util.health.HealthReportListener;
/**
* {@linkplain JWKSource} builder.
*
* Supports wrapping of a JWK set source, typically a URL, with the
* following capabilities:
*
*
* - {@linkplain CachingJWKSetSource caching}
*
- {@linkplain RefreshAheadCachingJWKSetSource caching with refresh ahead}
*
- {@linkplain RateLimitedJWKSetSource rate limiting}
*
- {@linkplain RetryingJWKSetSource retrial}
*
- {@linkplain JWKSourceWithFailover fail-over}
*
- {@linkplain JWKSetSourceWithHealthStatusReporting health status reporting}
*
- {@linkplain OutageTolerantJWKSetSource outage tolerance}
*
*
* @author Thomas Rørvik Skjølberg
* @author Vladimir Dzhuvinov
* @version 2023-12-10
*/
public class JWKSourceBuilder {
/**
* The default HTTP connect timeout for JWK set retrieval, in
* milliseconds. Set to 500 milliseconds.
*/
public static final int DEFAULT_HTTP_CONNECT_TIMEOUT = RemoteJWKSet.DEFAULT_HTTP_CONNECT_TIMEOUT;
/**
* The default HTTP read timeout for JWK set retrieval, in
* milliseconds. Set to 500 milliseconds.
*/
public static final int DEFAULT_HTTP_READ_TIMEOUT = RemoteJWKSet.DEFAULT_HTTP_READ_TIMEOUT;
/**
* The default HTTP entity size limit for JWK set retrieval, in bytes.
* Set to 50 KBytes.
*/
public static final int DEFAULT_HTTP_SIZE_LIMIT = RemoteJWKSet.DEFAULT_HTTP_SIZE_LIMIT;
/**
* The default time to live of cached JWK sets, in milliseconds. Set to
* 5 minutes.
*/
public static final long DEFAULT_CACHE_TIME_TO_LIVE = 5 * 60 * 1000L;
/**
* The default refresh timeout of cached JWK sets, in milliseconds. Set
* to 15 seconds.
*/
public static final long DEFAULT_CACHE_REFRESH_TIMEOUT = 15 * 1000L;
/**
* The default afresh-ahead time of cached JWK sets, in milliseconds.
* Set to 30 seconds.
*/
public static final long DEFAULT_REFRESH_AHEAD_TIME = 30_000L;
/**
* The default rate limiting minimum allowed time interval between two
* JWK set retrievals, in milliseconds.
*/
public static final long DEFAULT_RATE_LIMIT_MIN_INTERVAL = 30_000L;
/**
* Creates a new JWK source builder using the specified JWK set URL
* and {@linkplain DefaultResourceRetriever} with default timeouts.
*
* @param jwkSetURL The JWK set URL. Must not be {@code null}.
*/
public static JWKSourceBuilder create(final URL jwkSetURL) {
DefaultResourceRetriever retriever = new DefaultResourceRetriever(
DEFAULT_HTTP_CONNECT_TIMEOUT,
DEFAULT_HTTP_READ_TIMEOUT,
DEFAULT_HTTP_SIZE_LIMIT);
JWKSetSource jwkSetSource = new URLBasedJWKSetSource<>(jwkSetURL, retriever);
return new JWKSourceBuilder<>(jwkSetSource);
}
/**
* Creates a new JWK source builder using the specified JWK set URL
* and resource retriever.
*
* @param jwkSetURL The JWK set URL. Must not be {@code null}.
* @param retriever The resource retriever. Must not be {@code null}.
*/
public static JWKSourceBuilder create(final URL jwkSetURL, final ResourceRetriever retriever) {
return new JWKSourceBuilder<>(new URLBasedJWKSetSource(jwkSetURL, retriever));
}
/**
* Creates a new JWK source builder wrapping an existing source.
*
* @param source The JWK source to wrap. Must not be {@code null}.
*/
public static JWKSourceBuilder create(final JWKSetSource source) {
return new JWKSourceBuilder<>(source);
}
// the wrapped source
private final JWKSetSource jwkSetSource;
// caching
private boolean caching = true;
private long cacheTimeToLive = DEFAULT_CACHE_TIME_TO_LIVE;
private long cacheRefreshTimeout = DEFAULT_CACHE_REFRESH_TIMEOUT;
private EventListener, C> cachingEventListener;
private boolean refreshAhead = true;
private long refreshAheadTime = DEFAULT_REFRESH_AHEAD_TIME;
private boolean refreshAheadScheduled = false;
// rate limiting (retry on network error will not count against this)
private boolean rateLimited = true;
private long minTimeInterval = DEFAULT_RATE_LIMIT_MIN_INTERVAL;
private EventListener, C> rateLimitedEventListener;
// retrying
private boolean retrying = false;
private EventListener, C> retryingEventListener;
// outage
private boolean outageTolerant = false;
private long outageCacheTimeToLive = -1L;
private EventListener, C> outageEventListener;
// health status reporting
private HealthReportListener, C> healthReportListener;
// failover
protected JWKSource failover;
/**
* Creates a new JWK set source.
*
* @param jwkSetSource The JWK set source to wrap. Must not be
* {@code null}.
*/
private JWKSourceBuilder(final JWKSetSource jwkSetSource) {
Objects.requireNonNull(jwkSetSource);
this.jwkSetSource = jwkSetSource;
}
/**
* Toggles caching of the JWK set.
*
* @param enable {@code true} to cache the JWK set.
*
* @return This builder.
*/
public JWKSourceBuilder cache(final boolean enable) {
this.caching = enable;
return this;
}
/**
* Enables caching of the retrieved JWK set.
*
* @param timeToLive The time to live of the cached JWK set,
* in milliseconds.
* @param cacheRefreshTimeout The cache refresh timeout, in
* milliseconds.
*
* @return This builder.
*/
public JWKSourceBuilder cache(final long timeToLive, final long cacheRefreshTimeout) {
this.caching = true;
this.cacheTimeToLive = timeToLive;
this.cacheRefreshTimeout = cacheRefreshTimeout;
return this;
}
/**
* Enables caching of the retrieved JWK set.
*
* @param timeToLive The time to live of the cached JWK set,
* in milliseconds.
* @param cacheRefreshTimeout The cache refresh timeout, in
* milliseconds.
* @param eventListener The event listener, {@code null} if not
* specified.
*
* @return This builder.
*/
public JWKSourceBuilder cache(final long timeToLive,
final long cacheRefreshTimeout,
final EventListener, C> eventListener) {
this.caching = true;
this.cacheTimeToLive = timeToLive;
this.cacheRefreshTimeout = cacheRefreshTimeout;
this.cachingEventListener = eventListener;
return this;
}
/**
* Enables caching of the JWK set forever (no expiration).
*
* @return This builder.
*/
public JWKSourceBuilder cacheForever() {
this.caching = true;
this.cacheTimeToLive = Long.MAX_VALUE;
this.refreshAhead = false; // refresh ahead not necessary
return this;
}
/**
* Toggles refresh-ahead caching of the JWK set.
*
* @param enable {@code true} to enable refresh-ahead caching of the
* JWK set.
*
* @return This builder.
*/
public JWKSourceBuilder refreshAheadCache(final boolean enable) {
if (enable) {
this.caching = true;
}
this.refreshAhead = enable;
return this;
}
/**
* Enables refresh-ahead caching of the JWK set.
*
* @param refreshAheadTime The refresh ahead time, in milliseconds.
* @param scheduled {@code true} to refresh in a scheduled
* manner, regardless of requests.
*
* @return This builder.
*/
public JWKSourceBuilder refreshAheadCache(final long refreshAheadTime, final boolean scheduled) {
this.caching = true;
this.refreshAhead = true;
this.refreshAheadTime = refreshAheadTime;
this.refreshAheadScheduled = scheduled;
return this;
}
/**
* Enables refresh-ahead caching of the JWK set.
*
* @param refreshAheadTime The refresh ahead time, in milliseconds.
* @param scheduled {@code true} to refresh in a scheduled
* manner, regardless of requests.
* @param eventListener The event listener, {@code null} if not
* specified.
*
* @return This builder.
*/
public JWKSourceBuilder refreshAheadCache(final long refreshAheadTime,
final boolean scheduled,
final EventListener, C> eventListener) {
this.caching = true;
this.refreshAhead = true;
this.refreshAheadTime = refreshAheadTime;
this.refreshAheadScheduled = scheduled;
this.cachingEventListener = eventListener;
return this;
}
/**
* Toggles rate limiting of the JWK set retrieval.
*
* @param enable {@code true} to rate limit the JWK set retrieval.
*
* @return This builder.
*/
public JWKSourceBuilder rateLimited(final boolean enable) {
this.rateLimited = enable;
return this;
}
/**
* Enables rate limiting of the JWK set retrieval.
*
* @param minTimeInterval The minimum allowed time interval between two
* JWK set retrievals, in milliseconds.
*
* @return This builder.
*/
public JWKSourceBuilder rateLimited(final long minTimeInterval) {
this.rateLimited = true;
this.minTimeInterval = minTimeInterval;
return this;
}
/**
* Enables rate limiting of the JWK set retrieval.
*
* @param minTimeInterval The minimum allowed time interval between two
* JWK set retrievals, in milliseconds.
* @param eventListener The event listener, {@code null} if not
* specified.
*
* @return This builder.
*/
public JWKSourceBuilder rateLimited(final long minTimeInterval,
final EventListener, C> eventListener) {
this.rateLimited = true;
this.minTimeInterval = minTimeInterval;
this.rateLimitedEventListener = eventListener;
return this;
}
/**
* Sets a failover JWK source.
*
* @param failover The failover JWK source, {@code null} if none.
*
* @return This builder.
*/
public JWKSourceBuilder failover(final JWKSource failover) {
this.failover = failover;
return this;
}
/**
* Enables single retrial to retrieve the JWK set to work around
* transient network issues.
*
* @param enable {@code true} to enable single retrial.
*
* @return This builder.
*/
public JWKSourceBuilder retrying(final boolean enable) {
this.retrying = enable;
return this;
}
/**
* Enables single retrial to retrieve the JWK set to work around
* transient network issues.
*
* @param eventListener The event listener, {@code null} if not
* specified.
*
* @return This builder.
*/
public JWKSourceBuilder retrying(final EventListener, C> eventListener) {
this.retrying = true;
this.retryingEventListener = eventListener;
return this;
}
/**
* Sets a health report listener.
*
* @param listener The health report listener, {@code null} if not
* specified.
*
* @return This builder.
*/
public JWKSourceBuilder healthReporting(final HealthReportListener, C> listener) {
this.healthReportListener = listener;
return this;
}
/**
* Toggles outage tolerance by serving a cached JWK set in case of
* outage.
*
* @param enable {@code true} to enable the outage cache.
*
* @return This builder.
*/
public JWKSourceBuilder outageTolerant(final boolean enable) {
this.outageTolerant = enable;
return this;
}
/**
* Enables outage tolerance by serving a non-expiring cached JWK set in
* case of outage.
*
* @return This builder.
*/
public JWKSourceBuilder outageTolerantForever() {
this.outageTolerant = true;
this.outageCacheTimeToLive = Long.MAX_VALUE;
return this;
}
/**
* Enables outage tolerance by serving a non-expiring cached JWK set in
* case of outage.
*
* @param timeToLive The time to live of the cached JWK set to cover
* outages, in milliseconds.
*
* @return This builder.
*/
public JWKSourceBuilder outageTolerant(final long timeToLive) {
this.outageTolerant = true;
this.outageCacheTimeToLive = timeToLive;
return this;
}
/**
* Enables outage tolerance by serving a non-expiring cached JWK set in
* case of outage.
*
* @param timeToLive The time to live of the cached JWK set to cover
* outages, in milliseconds.
* @param eventListener The event listener, {@code null} if not
* specified.
*
* @return This builder.
*/
public JWKSourceBuilder outageTolerant(final long timeToLive,
final EventListener, C> eventListener) {
this.outageTolerant = true;
this.outageCacheTimeToLive = timeToLive;
this.outageEventListener = eventListener;
return this;
}
/**
* Builds the final {@link JWKSource}.
*
* @return The final {@link JWKSource}.
*/
public JWKSource build() {
if (! caching && rateLimited) {
throw new IllegalStateException("Rate limiting requires caching");
} else if (! caching && refreshAhead) {
throw new IllegalStateException("Refresh-ahead caching requires general caching");
}
if (caching && rateLimited && cacheTimeToLive <= minTimeInterval) {
throw new IllegalStateException("The rate limiting min time interval between requests must be less than the cache time-to-live");
}
if (caching && outageTolerant && cacheTimeToLive == Long.MAX_VALUE && outageCacheTimeToLive == Long.MAX_VALUE) {
// TODO consider adjusting instead of exception
throw new IllegalStateException("Outage tolerance not necessary with a non-expiring cache");
}
if (caching && refreshAhead && cacheTimeToLive == Long.MAX_VALUE) {
// TODO consider adjusting instead of exception
throw new IllegalStateException("Refresh-ahead caching not necessary with a non-expiring cache");
}
JWKSetSource source = jwkSetSource;
if (retrying) {
source = new RetryingJWKSetSource<>(source, retryingEventListener);
}
if (outageTolerant) {
if (outageCacheTimeToLive == -1L) {
if (caching) {
outageCacheTimeToLive = cacheTimeToLive * 10;
} else {
outageCacheTimeToLive = DEFAULT_CACHE_TIME_TO_LIVE * 10;
}
}
source = new OutageTolerantJWKSetSource<>(source, outageCacheTimeToLive, outageEventListener);
}
if (healthReportListener != null) {
source = new JWKSetSourceWithHealthStatusReporting<>(source, healthReportListener);
}
if (rateLimited) {
source = new RateLimitedJWKSetSource<>(source, minTimeInterval, rateLimitedEventListener);
}
if (refreshAhead) {
source = new RefreshAheadCachingJWKSetSource<>(source, cacheTimeToLive, cacheRefreshTimeout, refreshAheadTime, refreshAheadScheduled, cachingEventListener);
} else if (caching) {
source = new CachingJWKSetSource<>(source, cacheTimeToLive, cacheRefreshTimeout, cachingEventListener);
}
JWKSource jwkSource = new JWKSetBasedJWKSource<>(source);
if (failover != null) {
return new JWKSourceWithFailover<>(jwkSource, failover);
}
return jwkSource;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy