Skip to content

Commit e3bee23

Browse files
[FEATURE] usage of JWKS with JWT (w/o OpenID connect)
Signed-off-by: Sebastian Michalski <shekerama@gmail.com>
1 parent 33aebb9 commit e3bee23

File tree

5 files changed

+84
-7
lines changed

5 files changed

+84
-7
lines changed

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

Lines changed: 12 additions & 0 deletions
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,6 +68,8 @@ 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);
@@ -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+
var 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/KeySetRetriever.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,20 @@ public class KeySetRetriever implements KeySetProvider {
5454
private int oidcCacheModuleResponses = 0;
5555
private long oidcRequests = 0;
5656
private long lastCacheStatusLog = 0;
57+
private String jwksUri;
5758

5859
KeySetRetriever(String openIdConnectEndpoint, SSLConfig sslConfig, boolean useCacheForOidConnectEndpoint) {
5960
this.openIdConnectEndpoint = openIdConnectEndpoint;
6061
this.sslConfig = sslConfig;
6162

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

6873
public JsonWebKeys get() throws AuthenticatorUnavailableException {
@@ -101,6 +106,10 @@ public JsonWebKeys get() throws AuthenticatorUnavailableException {
101106

102107
String getJwksUri() throws AuthenticatorUnavailableException {
103108

109+
if (jwksUri != null && !jwksUri.isBlank()) {
110+
return jwksUri;
111+
}
112+
104113
try (CloseableHttpClient httpClient = createHttpClient(oidcHttpCacheStorage)) {
105114

106115
HttpGet httpGet = new HttpGet(openIdConnectEndpoint);
@@ -204,6 +213,13 @@ private CloseableHttpClient createHttpClient(HttpCacheStorage httpCacheStorage)
204213
return builder.build();
205214
}
206215

216+
private void configureCache(boolean useCacheForOidConnectEndpoint) {
217+
if (useCacheForOidConnectEndpoint) {
218+
cacheConfig = CacheConfig.custom().setMaxCacheEntries(10).setMaxObjectSize(1024L * 1024L).build();
219+
oidcHttpCacheStorage = new BasicHttpCacheStorage(cacheConfig);
220+
}
221+
}
222+
207223
public int getOidcCacheHits() {
208224
return oidcCacheHits;
209225
}

src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/HTTPJwtKeyByOpenIdConnectAuthenticatorTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.junit.BeforeClass;
1919
import org.junit.Test;
2020

21+
import org.opensearch.OpenSearchSecurityException;
2122
import org.opensearch.common.settings.Settings;
2223
import org.opensearch.security.user.AuthCredentials;
2324
import org.opensearch.security.util.FakeRestRequest;
@@ -58,6 +59,44 @@ public void basicTest() {
5859
Assert.assertEquals(3, creds.getAttributes().size());
5960
}
6061

62+
63+
@Test
64+
public void jwksUriTest() {
65+
String requiredIssuer = "requiredIssuer";
66+
String requiredAudience = "requiredAudience";
67+
Settings settings = Settings.builder()
68+
.put("jwks_uri", mockIdpServer.getJwksUri())
69+
.put("required_issuer", requiredIssuer)
70+
.put("required_audience", requiredAudience)
71+
.build();
72+
73+
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
74+
75+
AuthCredentials creds = jwtAuth.extractCredentials(new FakeRestRequest(
76+
ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap<>()), null);
77+
78+
Assert.assertNotNull(creds);
79+
Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername());
80+
Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud"));
81+
Assert.assertEquals(0, creds.getBackendRoles().size());
82+
Assert.assertEquals(3, creds.getAttributes().size());
83+
Assert.assertEquals(requiredAudience, jwtAuth.getRequiredAudience());
84+
Assert.assertEquals(requiredIssuer, jwtAuth.getRequiredIssuer());
85+
}
86+
87+
@Test
88+
public void jwksUriMissingTest() {
89+
var exception = Assert.assertThrows(Exception.class, () -> {
90+
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(Settings.builder().build(), null);
91+
jwtAuth.extractCredentials(
92+
new FakeRestRequest(ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap<>()),
93+
null);
94+
});
95+
96+
Assert.assertEquals("Authentication backend failed", exception.getMessage());
97+
Assert.assertEquals(OpenSearchSecurityException.class, exception.getClass());
98+
}
99+
61100
@Test
62101
public void testEscapeKid() {
63102
Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build();

src/test/java/com/amazon/dlic/auth/http/jwt/keybyoidc/MockIpdServer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ public String getDiscoverUri() {
118118
return uri + CTX_DISCOVER;
119119
}
120120

121+
public String getJwksUri() {
122+
return uri + CTX_KEYS;
123+
}
124+
121125
public int getPort() {
122126
return port;
123127
}

0 commit comments

Comments
 (0)