Skip to content

Commit 512bcaa

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

File tree

4 files changed

+107
-45
lines changed

4 files changed

+107
-45
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public AbstractHTTPJwtAuthenticator(Settings settings, Path configPath) {
7373

7474
try {
7575
this.keyProvider = this.initKeyProvider(settings, configPath);
76-
jwtVerifier = new JwtVerifier(keyProvider, clockSkewToleranceSeconds );
76+
jwtVerifier = new JwtVerifier(keyProvider, clockSkewToleranceSeconds, requiredIssuer, requiredAudience);
7777

7878
} catch (Exception e) {
7979
log.error("Error creating JWT authenticator. JWT authentication will not work", e);

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

Lines changed: 20 additions & 2 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 {
@@ -112,6 +116,20 @@ private void validateClaims(JwtToken jwt) throws BadCredentialsException, JwtExc
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 (!audience.equals(requiredAudience)) {
128+
throw new JwtException("Invalid issuer");
129+
}
130+
131+
if (!issuer.equals(requiredIssuer)) {
132+
throw new JwtException("Invalid issuer");
115133
}
116134
}
117135
}

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

Lines changed: 81 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ public static void tearDown() {
4545

4646
@Test
4747
public void basicTest() {
48-
Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build();
48+
Settings settings = Settings.builder()
49+
.put("openid_connect_url", mockIdpServer.getDiscoverUri())
50+
.put("required_issuer", TestJwts.TEST_ISSUER)
51+
.put("required_audience", TestJwts.TEST_AUDIENCE)
52+
.build();
4953

5054
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
5155

@@ -56,74 +60,88 @@ public void basicTest() {
5660
Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername());
5761
Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud"));
5862
Assert.assertEquals(0, creds.getBackendRoles().size());
59-
Assert.assertEquals(3, creds.getAttributes().size());
63+
Assert.assertEquals(4, creds.getAttributes().size());
6064
}
6165

6266

6367
@Test
6468
public void jwksUriTest() {
65-
String requiredIssuer = "requiredIssuer";
66-
String requiredAudience = "requiredAudience";
6769
Settings settings = Settings.builder()
6870
.put("jwks_uri", mockIdpServer.getJwksUri())
69-
.put("required_issuer", requiredIssuer)
70-
.put("required_audience", requiredAudience)
71+
.put("required_issuer", TestJwts.TEST_ISSUER)
72+
.put("required_audience", TestJwts.TEST_AUDIENCE)
7173
.build();
7274

7375
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
7476

7577
AuthCredentials creds = jwtAuth.extractCredentials(new FakeRestRequest(
76-
ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_1), new HashMap<>()), null);
78+
ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_2), new HashMap<>()), null);
7779

7880
Assert.assertNotNull(creds);
7981
Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername());
8082
Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud"));
8183
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());
84+
Assert.assertEquals(4, creds.getAttributes().size());
8585
}
8686

8787
@Test
88-
public void jwksMatchingRequiredIssuerInClaimTest() {
89-
String requiredIssuer = "requiredIssuer";
88+
public void jwksMissingRequiredIssuerInClaimTest() {
9089
Settings settings = Settings.builder()
9190
.put("jwks_uri", mockIdpServer.getJwksUri())
92-
.put("required_issuer", requiredIssuer)
91+
.put("required_audience", TestJwts.TEST_AUDIENCE)
9392
.build();
9493

9594
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
9695

9796
AuthCredentials creds = jwtAuth.extractCredentials(new FakeRestRequest(
9897
ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_2), new HashMap<>()), null);
9998

100-
Assert.assertNotNull(creds);
101-
Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername());
102-
Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud"));
103-
Assert.assertEquals(0, creds.getBackendRoles().size());
104-
Assert.assertEquals(4, creds.getAttributes().size());
105-
Assert.assertFalse(creds.getAttributes().get("attr.jwt.iss").contains(jwtAuth.getRequiredIssuer()));
99+
Assert.assertNull(creds);
100+
}
101+
102+
@Test
103+
public void jwksNotMatchingRequiredIssuerInClaimTest() {
104+
Settings settings = Settings.builder()
105+
.put("jwks_uri", mockIdpServer.getJwksUri())
106+
.put("required_issuer", "Wrong Issuer")
107+
.build();
108+
109+
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
110+
111+
AuthCredentials creds = jwtAuth.extractCredentials(new FakeRestRequest(
112+
ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_2), new HashMap<>()), null);
113+
114+
Assert.assertNull(creds);
115+
}
116+
117+
@Test
118+
public void jwksMissingRequiredAudienceInClaimTest() {
119+
Settings settings = Settings.builder()
120+
.put("jwks_uri", mockIdpServer.getJwksUri())
121+
.put("required_issuer", TestJwts.TEST_ISSUER)
122+
.build();
123+
124+
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
125+
126+
AuthCredentials creds = jwtAuth.extractCredentials(new FakeRestRequest(
127+
ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_2), new HashMap<>()), null);
128+
129+
Assert.assertNull(creds);
106130
}
107131

108132
@Test
109133
public void jwksNotMatchingRequiredAudienceInClaimTest() {
110-
String requiredAudience = "requiredAudience";
111134
Settings settings = Settings.builder()
112135
.put("jwks_uri", mockIdpServer.getJwksUri())
113-
.put("required_audience", requiredAudience)
136+
.put("required_audience", "Wrong Audience")
114137
.build();
115138

116139
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
117140

118141
AuthCredentials creds = jwtAuth.extractCredentials(new FakeRestRequest(
119142
ImmutableMap.of("Authorization", TestJwts.MC_COY_SIGNED_OCT_2), new HashMap<>()), null);
120143

121-
Assert.assertNotNull(creds);
122-
Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername());
123-
Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud"));
124-
Assert.assertEquals(0, creds.getBackendRoles().size());
125-
Assert.assertEquals(4, creds.getAttributes().size());
126-
Assert.assertFalse(creds.getAttributes().get("attr.jwt.aud").contains(jwtAuth.getRequiredAudience()));
144+
Assert.assertNull(creds);
127145
}
128146

129147
@Test
@@ -141,7 +159,11 @@ public void jwksUriMissingTest() {
141159

142160
@Test
143161
public void testEscapeKid() {
144-
Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build();
162+
Settings settings = Settings.builder()
163+
.put("openid_connect_url", mockIdpServer.getDiscoverUri())
164+
.put("required_issuer", TestJwts.TEST_ISSUER)
165+
.put("required_audience", TestJwts.TEST_AUDIENCE)
166+
.build();
145167

146168
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
147169

@@ -152,12 +174,16 @@ public void testEscapeKid() {
152174
Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername());
153175
Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud"));
154176
Assert.assertEquals(0, creds.getBackendRoles().size());
155-
Assert.assertEquals(3, creds.getAttributes().size());
177+
Assert.assertEquals(4, creds.getAttributes().size());
156178
}
157179

158180
@Test
159181
public void bearerTest() {
160-
Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build();
182+
Settings settings = Settings.builder()
183+
.put("openid_connect_url", mockIdpServer.getDiscoverUri())
184+
.put("required_issuer", TestJwts.TEST_ISSUER)
185+
.put("required_audience", TestJwts.TEST_AUDIENCE)
186+
.build();
161187

162188
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
163189

@@ -170,13 +196,17 @@ public void bearerTest() {
170196
Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername());
171197
Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud"));
172198
Assert.assertEquals(0, creds.getBackendRoles().size());
173-
Assert.assertEquals(3, creds.getAttributes().size());
199+
Assert.assertEquals(4, creds.getAttributes().size());
174200
}
175201

176202
@Test
177203
public void testRoles() throws Exception {
178-
Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri())
179-
.put("roles_key", TestJwts.ROLES_CLAIM).build();
204+
Settings settings = Settings.builder()
205+
.put("openid_connect_url", mockIdpServer.getDiscoverUri())
206+
.put("roles_key", TestJwts.ROLES_CLAIM)
207+
.put("required_issuer", TestJwts.TEST_ISSUER)
208+
.put("required_audience", TestJwts.TEST_AUDIENCE)
209+
.build();
180210

181211
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
182212

@@ -207,6 +237,8 @@ public void testExpInSkew() throws Exception {
207237
Settings settings = Settings.builder()
208238
.put("openid_connect_url", mockIdpServer.getDiscoverUri())
209239
.put("jwt_clock_skew_tolerance_seconds", "10")
240+
.put("required_issuer", TestJwts.TEST_ISSUER)
241+
.put("required_audience", TestJwts.TEST_AUDIENCE)
210242
.build();
211243

212244
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
@@ -230,6 +262,8 @@ public void testNbf() throws Exception {
230262
Settings settings = Settings.builder()
231263
.put("openid_connect_url", mockIdpServer.getDiscoverUri())
232264
.put("jwt_clock_skew_tolerance_seconds", "0")
265+
.put("required_issuer", TestJwts.TEST_ISSUER)
266+
.put("required_audience", TestJwts.TEST_AUDIENCE)
233267
.build();
234268

235269
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
@@ -253,6 +287,8 @@ public void testNbfInSkew() throws Exception {
253287
Settings settings = Settings.builder()
254288
.put("openid_connect_url", mockIdpServer.getDiscoverUri())
255289
.put("jwt_clock_skew_tolerance_seconds", "10")
290+
.put("required_issuer", TestJwts.TEST_ISSUER)
291+
.put("required_audience", TestJwts.TEST_AUDIENCE)
256292
.build();
257293

258294
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
@@ -273,7 +309,11 @@ public void testNbfInSkew() throws Exception {
273309
@Test
274310
public void testRS256() throws Exception {
275311

276-
Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build();
312+
Settings settings = Settings.builder()
313+
.put("openid_connect_url", mockIdpServer.getDiscoverUri())
314+
.put("required_issuer", TestJwts.TEST_ISSUER)
315+
.put("required_audience", TestJwts.TEST_AUDIENCE)
316+
.build();
277317

278318
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
279319

@@ -284,7 +324,7 @@ public void testRS256() throws Exception {
284324
Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername());
285325
Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud"));
286326
Assert.assertEquals(0, creds.getBackendRoles().size());
287-
Assert.assertEquals(3, creds.getAttributes().size());
327+
Assert.assertEquals(4, creds.getAttributes().size());
288328
}
289329

290330
@Test
@@ -302,7 +342,11 @@ public void testBadSignature() throws Exception {
302342

303343
@Test
304344
public void testPeculiarJsonEscaping() {
305-
Settings settings = Settings.builder().put("openid_connect_url", mockIdpServer.getDiscoverUri()).build();
345+
Settings settings = Settings.builder()
346+
.put("openid_connect_url", mockIdpServer.getDiscoverUri())
347+
.put("required_issuer", TestJwts.TEST_ISSUER)
348+
.put("required_audience", TestJwts.TEST_AUDIENCE)
349+
.build();
306350

307351
HTTPJwtKeyByOpenIdConnectAuthenticator jwtAuth = new HTTPJwtKeyByOpenIdConnectAuthenticator(settings, null);
308352

@@ -314,7 +358,7 @@ public void testPeculiarJsonEscaping() {
314358
Assert.assertEquals(TestJwts.MCCOY_SUBJECT, creds.getUsername());
315359
Assert.assertEquals(TestJwts.TEST_AUDIENCE, creds.getAttributes().get("attr.jwt.aud"));
316360
Assert.assertEquals(0, creds.getBackendRoles().size());
317-
Assert.assertEquals(3, creds.getAttributes().size());
361+
Assert.assertEquals(4, creds.getAttributes().size());
318362
}
319363

320364
}

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,13 @@ class TestJwts {
3333

3434
static final String MCCOY_SUBJECT = "Leonard McCoy";
3535

36-
static final String ISS = "ISSUER";
36+
static final String TEST_ISSUER = "TestIssuer";
3737

38-
static final JwtToken MC_COY = create(MCCOY_SUBJECT, TEST_AUDIENCE, null, ROLES_CLAIM, TEST_ROLES_STRING);
38+
static final JwtToken MC_COY = create(MCCOY_SUBJECT, TEST_AUDIENCE, TEST_ISSUER, ROLES_CLAIM, TEST_ROLES_STRING);
3939

40-
static final JwtToken MC_COY_2 = create(MCCOY_SUBJECT, TEST_AUDIENCE, ISS, ROLES_CLAIM, TEST_ROLES_STRING);
40+
static final JwtToken MC_COY_2 = create(MCCOY_SUBJECT, TEST_AUDIENCE, TEST_ISSUER, ROLES_CLAIM, TEST_ROLES_STRING);
4141

42-
static final JwtToken MC_COY_EXPIRED = create(MCCOY_SUBJECT, TEST_AUDIENCE, null, ROLES_CLAIM, TEST_ROLES_STRING, JwtConstants.CLAIM_EXPIRY, 10);
42+
static final JwtToken MC_COY_EXPIRED = create(MCCOY_SUBJECT, TEST_AUDIENCE, TEST_ISSUER, ROLES_CLAIM, TEST_ROLES_STRING, JwtConstants.CLAIM_EXPIRY, 10);
4343

4444
static final String MC_COY_SIGNED_OCT_1 = createSigned(MC_COY, TestJwk.OCT_1);
4545

@@ -116,7 +116,7 @@ static String createMcCoySignedOct1(long nbf, long exp)
116116
{
117117
JwtToken jwt_token = create(
118118
MCCOY_SUBJECT, TEST_AUDIENCE,
119-
null, ROLES_CLAIM, TEST_ROLES_STRING,
119+
TEST_ISSUER, ROLES_CLAIM, TEST_ROLES_STRING,
120120
JwtConstants.CLAIM_NOT_BEFORE, nbf,
121121
JwtConstants.CLAIM_EXPIRY, exp);
122122

0 commit comments

Comments
 (0)