Skip to content

Commit ce5b295

Browse files
[FEATURE] usage of JWKS with JWT (w/o OpenID connect) (#2808) (#2815)
* [FEATURE] usage of JWKS with JWT (w/o OpenID connect) --------- Signed-off-by: Sebastian Michalski <shekerama@gmail.com> Signed-off-by: Craig Perkins <cwperx@amazon.com> Co-authored-by: Sebastian Michalski <shekerama@gmail.com> (cherry picked from commit 4b38671) Co-authored-by: Craig Perkins <cwperx@amazon.com>
1 parent 2ebcfa7 commit ce5b295

File tree

8 files changed

+238
-34
lines changed

8 files changed

+238
-34
lines changed

src/main/java/com/amazon/dlic/auth/http/jwt/AbstractHTTPJwtAuthenticator.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ public abstract class AbstractHTTPJwtAuthenticator implements HTTPAuthenticator
5555
private final String jwtUrlParameter;
5656
private final String subjectKey;
5757
private final String rolesKey;
58+
private final String requiredAudience;
59+
private final String requiredIssuer;
5860

5961
public static final int DEFAULT_CLOCK_SKEW_TOLERANCE_SECONDS = 30;
6062
private final int clockSkewToleranceSeconds ;
@@ -66,10 +68,12 @@ public AbstractHTTPJwtAuthenticator(Settings settings, Path configPath) {
6668
rolesKey = settings.get("roles_key");
6769
subjectKey = settings.get("subject_key");
6870
clockSkewToleranceSeconds = settings.getAsInt("jwt_clock_skew_tolerance_seconds", DEFAULT_CLOCK_SKEW_TOLERANCE_SECONDS);
71+
requiredAudience = settings.get("required_audience");
72+
requiredIssuer = settings.get("required_issuer");
6973

7074
try {
7175
this.keyProvider = this.initKeyProvider(settings, configPath);
72-
jwtVerifier = new JwtVerifier(keyProvider, clockSkewToleranceSeconds );
76+
jwtVerifier = new JwtVerifier(keyProvider, clockSkewToleranceSeconds, requiredIssuer, requiredAudience);
7377

7478
} catch (Exception e) {
7579
log.error("Error creating JWT authenticator. JWT authentication will not work", e);
@@ -233,4 +237,12 @@ public boolean reRequestAuthentication(RestChannel channel, AuthCredentials auth
233237
return true;
234238
}
235239

240+
public String getRequiredAudience() {
241+
return requiredAudience;
242+
}
243+
244+
public String getRequiredIssuer() {
245+
return requiredIssuer;
246+
}
247+
236248
}

src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPJwtKeyByOpenIdConnectAuthenticator.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,15 @@ protected KeyProvider initKeyProvider(Settings settings, Path configPath) throws
3232

3333
int refreshRateLimitTimeWindowMs = settings.getAsInt("refresh_rate_limit_time_window_ms", 10000);
3434
int refreshRateLimitCount = settings.getAsInt("refresh_rate_limit_count", 10);
35-
36-
KeySetRetriever keySetRetriever = new KeySetRetriever(settings.get("openid_connect_url"),
37-
getSSLConfig(settings, configPath), settings.getAsBoolean("cache_jwks_endpoint", false));
35+
String jwksUri = settings.get("jwks_uri");
36+
37+
KeySetRetriever keySetRetriever;
38+
if(jwksUri != null && !jwksUri.isBlank()) {
39+
keySetRetriever =
40+
new KeySetRetriever(getSSLConfig(settings, configPath), settings.getAsBoolean("cache_jwks_endpoint", false), jwksUri);
41+
} else {
42+
keySetRetriever = new KeySetRetriever(settings.get("openid_connect_url"), getSSLConfig(settings, configPath), settings.getAsBoolean("cache_jwks_endpoint", false));
43+
}
3844

3945
keySetRetriever.setRequestTimeoutMs(idpRequestTimeoutMs);
4046

src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/JwtVerifier.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,14 @@ public class JwtVerifier {
3333

3434
private final KeyProvider keyProvider;
3535
private final int clockSkewToleranceSeconds;
36-
37-
public JwtVerifier(KeyProvider keyProvider, int clockSkewToleranceSeconds ) {
36+
private final String requiredIssuer;
37+
private final String requiredAudience;
38+
39+
public JwtVerifier(KeyProvider keyProvider, int clockSkewToleranceSeconds, String requiredIssuer, String requiredAudience) {
3840
this.keyProvider = keyProvider;
3941
this.clockSkewToleranceSeconds = clockSkewToleranceSeconds;
42+
this.requiredIssuer = requiredIssuer;
43+
this.requiredAudience = requiredAudience;
4044
}
4145

4246
public JwtToken getVerifiedJwtToken(String encodedJwt) throws BadCredentialsException {
@@ -106,12 +110,26 @@ private JwsSignatureVerifier getInitializedSignatureVerifier(JsonWebKey key, Jwt
106110
}
107111
}
108112

109-
private void validateClaims(JwtToken jwt) throws BadCredentialsException, JwtException {
113+
private void validateClaims(JwtToken jwt) throws JwtException {
110114
JwtClaims claims = jwt.getClaims();
111115

112116
if (claims != null) {
113117
JwtUtils.validateJwtExpiry(claims, clockSkewToleranceSeconds, false);
114118
JwtUtils.validateJwtNotBefore(claims, clockSkewToleranceSeconds, false);
119+
validateRequiredAudienceAndIssuer(claims);
120+
}
121+
}
122+
123+
private void validateRequiredAudienceAndIssuer(JwtClaims claims) {
124+
String audience = claims.getAudience();
125+
String issuer = claims.getIssuer();
126+
127+
if (!Strings.isNullOrEmpty(requiredAudience) && !requiredAudience.equals(audience)) {
128+
throw new JwtException("Invalid audience");
129+
}
130+
131+
if (!Strings.isNullOrEmpty(requiredIssuer) && !requiredIssuer.equals(issuer)) {
132+
throw new JwtException("Invalid issuer");
115133
}
116134
}
117135
}

src/main/java/com/amazon/dlic/auth/http/jwt/keybyoidc/KeySetRetriever.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import java.io.IOException;
1515

16+
import joptsimple.internal.Strings;
1617
import org.apache.cxf.rs.security.jose.jwk.JsonWebKeys;
1718
import org.apache.cxf.rs.security.jose.jwk.JwkUtils;
1819
import org.apache.http.HttpEntity;
@@ -52,15 +53,20 @@ public class KeySetRetriever implements KeySetProvider {
5253
private int oidcCacheModuleResponses = 0;
5354
private long oidcRequests = 0;
5455
private long lastCacheStatusLog = 0;
56+
private String jwksUri;
5557

5658
KeySetRetriever(String openIdConnectEndpoint, SSLConfig sslConfig, boolean useCacheForOidConnectEndpoint) {
5759
this.openIdConnectEndpoint = openIdConnectEndpoint;
5860
this.sslConfig = sslConfig;
5961

60-
if (useCacheForOidConnectEndpoint) {
61-
cacheConfig = CacheConfig.custom().setMaxCacheEntries(10).setMaxObjectSize(1024L * 1024L).build();
62-
oidcHttpCacheStorage = new BasicHttpCacheStorage(cacheConfig);
63-
}
62+
configureCache(useCacheForOidConnectEndpoint);
63+
}
64+
65+
KeySetRetriever(SSLConfig sslConfig, boolean useCacheForOidConnectEndpoint, String jwksUri) {
66+
this.jwksUri = jwksUri;
67+
this.sslConfig = sslConfig;
68+
69+
configureCache(useCacheForOidConnectEndpoint);
6470
}
6571

6672
public JsonWebKeys get() throws AuthenticatorUnavailableException {
@@ -101,6 +107,14 @@ public JsonWebKeys get() throws AuthenticatorUnavailableException {
101107

102108
String getJwksUri() throws AuthenticatorUnavailableException {
103109

110+
if (!Strings.isNullOrEmpty(jwksUri)) {
111+
return jwksUri;
112+
}
113+
114+
if (Strings.isNullOrEmpty(openIdConnectEndpoint)) {
115+
throw new AuthenticatorUnavailableException("Either openid_connect_url or jwks_uri must be configured for OIDC Authentication backend");
116+
}
117+
104118
try (CloseableHttpClient httpClient = createHttpClient(oidcHttpCacheStorage)) {
105119

106120
HttpGet httpGet = new HttpGet(openIdConnectEndpoint);
@@ -202,6 +216,13 @@ private CloseableHttpClient createHttpClient(HttpCacheStorage httpCacheStorage)
202216
return builder.build();
203217
}
204218

219+
private void configureCache(boolean useCacheForOidConnectEndpoint) {
220+
if (useCacheForOidConnectEndpoint) {
221+
cacheConfig = CacheConfig.custom().setMaxCacheEntries(10).setMaxObjectSize(1024L * 1024L).build();
222+
oidcHttpCacheStorage = new BasicHttpCacheStorage(cacheConfig);
223+
}
224+
}
225+
205226
public int getOidcCacheHits() {
206227
return oidcCacheHits;
207228
}

0 commit comments

Comments
 (0)