Skip to content

Commit 37fba62

Browse files
author
Tomasz Borowiec
committed
Throwing exception on all other JWT types than SignedJWT
1 parent c38b9d7 commit 37fba62

File tree

2 files changed

+77
-141
lines changed

2 files changed

+77
-141
lines changed

openid-connect-server/src/main/java/org/mitre/openid/connect/assertion/JWTBearerAuthenticationProvider.java

Lines changed: 47 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
import com.nimbusds.jose.JWSAlgorithm;
4747
import com.nimbusds.jwt.JWT;
4848
import com.nimbusds.jwt.JWTClaimsSet;
49-
import com.nimbusds.jwt.PlainJWT;
5049
import com.nimbusds.jwt.SignedJWT;
5150

5251
/**
@@ -92,64 +91,60 @@ public Authentication authenticate(Authentication authentication) throws Authent
9291
JWT jwt = jwtAuth.getJwt();
9392
JWTClaimsSet jwtClaims = jwt.getJWTClaimsSet();
9493

95-
if (jwt instanceof PlainJWT) {
96-
if (!AuthMethod.NONE.equals(client.getTokenEndpointAuthMethod())) {
97-
throw new AuthenticationServiceException("Client does not support this authentication method.");
94+
if (!(jwt instanceof SignedJWT)) {
95+
throw new AuthenticationServiceException("Unsupported JWT type: " + jwt.getClass().getName());
96+
}
97+
98+
// check the signature with nimbus
99+
SignedJWT jws = (SignedJWT) jwt;
100+
101+
JWSAlgorithm alg = jws.getHeader().getAlgorithm();
102+
103+
if (client.getTokenEndpointAuthSigningAlg() != null &&
104+
!client.getTokenEndpointAuthSigningAlg().equals(alg)) {
105+
throw new AuthenticationServiceException("Client's registered token endpoint signing algorithm (" + client.getTokenEndpointAuthSigningAlg()
106+
+ ") does not match token's actual algorithm (" + alg.getName() + ")");
107+
}
108+
109+
if (client.getTokenEndpointAuthMethod() == null ||
110+
client.getTokenEndpointAuthMethod().equals(AuthMethod.NONE) ||
111+
client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_BASIC) ||
112+
client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_POST)) {
113+
114+
// this client doesn't support this type of authentication
115+
throw new AuthenticationServiceException("Client does not support this authentication method.");
116+
117+
} else if ((client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY) &&
118+
(alg.equals(JWSAlgorithm.RS256)
119+
|| alg.equals(JWSAlgorithm.RS384)
120+
|| alg.equals(JWSAlgorithm.RS512)
121+
|| alg.equals(JWSAlgorithm.ES256)
122+
|| alg.equals(JWSAlgorithm.ES384)
123+
|| alg.equals(JWSAlgorithm.ES512)
124+
|| alg.equals(JWSAlgorithm.PS256)
125+
|| alg.equals(JWSAlgorithm.PS384)
126+
|| alg.equals(JWSAlgorithm.PS512)))
127+
|| (client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_JWT) &&
128+
(alg.equals(JWSAlgorithm.HS256)
129+
|| alg.equals(JWSAlgorithm.HS384)
130+
|| alg.equals(JWSAlgorithm.HS512)))) {
131+
132+
// double-check the method is asymmetrical if we're in HEART mode
133+
if (config.isHeartMode() && !client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY)) {
134+
throw new AuthenticationServiceException("[HEART mode] Invalid authentication method");
98135
}
99-
} else if (jwt instanceof SignedJWT) {
100-
// check the signature with nimbus
101-
SignedJWT jws = (SignedJWT)jwt;
102136

103-
JWSAlgorithm alg = jws.getHeader().getAlgorithm();
137+
JWTSigningAndValidationService validator = validators.getValidator(client, alg);
104138

105-
if (client.getTokenEndpointAuthSigningAlg() != null &&
106-
!client.getTokenEndpointAuthSigningAlg().equals(alg)) {
107-
throw new AuthenticationServiceException("Client's registered token endpoint signing algorithm (" + client.getTokenEndpointAuthSigningAlg()
108-
+ ") does not match token's actual algorithm (" + alg.getName() + ")");
139+
if (validator == null) {
140+
throw new AuthenticationServiceException("Unable to create signature validator for client " + client + " and algorithm " + alg);
109141
}
110142

111-
if (client.getTokenEndpointAuthMethod() == null ||
112-
client.getTokenEndpointAuthMethod().equals(AuthMethod.NONE) ||
113-
client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_BASIC) ||
114-
client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_POST)) {
115-
116-
// this client doesn't support this type of authentication
117-
throw new AuthenticationServiceException("Client does not support this authentication method.");
118-
119-
} else if ((client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY) &&
120-
(alg.equals(JWSAlgorithm.RS256)
121-
|| alg.equals(JWSAlgorithm.RS384)
122-
|| alg.equals(JWSAlgorithm.RS512)
123-
|| alg.equals(JWSAlgorithm.ES256)
124-
|| alg.equals(JWSAlgorithm.ES384)
125-
|| alg.equals(JWSAlgorithm.ES512)
126-
|| alg.equals(JWSAlgorithm.PS256)
127-
|| alg.equals(JWSAlgorithm.PS384)
128-
|| alg.equals(JWSAlgorithm.PS512)))
129-
|| (client.getTokenEndpointAuthMethod().equals(AuthMethod.SECRET_JWT) &&
130-
(alg.equals(JWSAlgorithm.HS256)
131-
|| alg.equals(JWSAlgorithm.HS384)
132-
|| alg.equals(JWSAlgorithm.HS512)))) {
133-
134-
// double-check the method is asymmetrical if we're in HEART mode
135-
if (config.isHeartMode() && !client.getTokenEndpointAuthMethod().equals(AuthMethod.PRIVATE_KEY)) {
136-
throw new AuthenticationServiceException("[HEART mode] Invalid authentication method");
137-
}
138-
139-
JWTSigningAndValidationService validator = validators.getValidator(client, alg);
140-
141-
if (validator == null) {
142-
throw new AuthenticationServiceException("Unable to create signature validator for client " + client + " and algorithm " + alg);
143-
}
144-
145-
if (!validator.validateSignature(jws)) {
146-
throw new AuthenticationServiceException("Signature did not validate for presented JWT authentication.");
147-
}
148-
} else {
149-
throw new AuthenticationServiceException("Unable to create signature validator for method " + client.getTokenEndpointAuthMethod() + " and algorithm " + alg);
143+
if (!validator.validateSignature(jws)) {
144+
throw new AuthenticationServiceException("Signature did not validate for presented JWT authentication.");
150145
}
151146
} else {
152-
throw new AuthenticationServiceException("Unsupported JWT type: " + jwt.getClass().getName());
147+
throw new AuthenticationServiceException("Unable to create signature validator for method " + client.getTokenEndpointAuthMethod() + " and algorithm " + alg);
153148
}
154149

155150
// check the issuer

openid-connect-server/src/test/java/org/mitre/openid/connect/assertion/TestJWTBearerAuthenticationProvider.java

Lines changed: 30 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import static org.hamcrest.CoreMatchers.hasItems;
44
import static org.hamcrest.CoreMatchers.instanceOf;
5+
import static org.hamcrest.CoreMatchers.is;
56
import static org.hamcrest.CoreMatchers.startsWith;
6-
import static org.hamcrest.core.Is.is;
77
import static org.junit.Assert.assertThat;
88
import static org.mockito.Matchers.any;
99
import static org.mockito.Mockito.when;
@@ -110,20 +110,23 @@ public void should_throw_UsernameNotFoundException_when_clientService_throws_Inv
110110
}
111111

112112
@Test
113-
public void should_throw_AuthenticationServiceException_for_PlainJWT_when_AuthMethod_is_different_than_NONE() {
113+
public void should_throw_AuthenticationServiceException_for_PlainJWT() {
114114
mockPlainJWTAuthAttempt();
115-
List<AuthMethod> unsupportedAuthMethods = Arrays.asList(
116-
null, AuthMethod.PRIVATE_KEY, AuthMethod.PRIVATE_KEY, AuthMethod.SECRET_BASIC, AuthMethod.SECRET_JWT, AuthMethod.SECRET_POST
117-
);
118115

119-
for (AuthMethod authMethod : unsupportedAuthMethods) {
120-
when(client.getTokenEndpointAuthMethod()).thenReturn(authMethod);
116+
Throwable thrown = authenticateAndReturnThrownException();
121117

122-
Throwable thrown = authenticateAndReturnThrownException();
118+
assertThat(thrown, instanceOf(AuthenticationServiceException.class));
119+
assertThat(thrown.getMessage(), is("Unsupported JWT type: " + PlainJWT.class.getName()));
120+
}
123121

124-
assertThat(thrown, instanceOf(AuthenticationServiceException.class));
125-
assertThat(thrown.getMessage(), is("Client does not support this authentication method."));
126-
}
122+
@Test
123+
public void should_throw_AuthenticationServiceException_for_EncryptedJWT() {
124+
mockEncryptedJWTAuthAttempt();
125+
126+
Throwable thrown = authenticateAndReturnThrownException();
127+
128+
assertThat(thrown, instanceOf(AuthenticationServiceException.class));
129+
assertThat(thrown.getMessage(), is("Unsupported JWT type: " + EncryptedJWT.class.getName()));
127130
}
128131

129132
@Test
@@ -228,21 +231,10 @@ public void should_throw_AuthenticationServiceException_for_SignedJWT_when_inval
228231
assertThat(thrown.getMessage(), is("Signature did not validate for presented JWT authentication."));
229232
}
230233

231-
@Test
232-
public void should_throw_AuthenticationServiceException_for_EncryptedJWT() {
233-
EncryptedJWT encryptedJWT = createEncryptedJWT();
234-
when(token.getJwt()).thenReturn(encryptedJWT);
235-
236-
Throwable thrown = authenticateAndReturnThrownException();
237-
238-
assertThat(thrown, instanceOf(AuthenticationServiceException.class));
239-
assertThat(thrown.getMessage(), is("Unsupported JWT type: " + EncryptedJWT.class.getName()));
240-
}
241-
242234
@Test
243235
public void should_throw_AuthenticationServiceException_when_null_issuer() {
244236
JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder().issuer(null).build();
245-
mockPlainJWTAuthAttempt(jwtClaimsSet);
237+
mockSignedJWTAuthAttempt(jwtClaimsSet);
246238

247239
Throwable thrown = authenticateAndReturnThrownException();
248240

@@ -253,7 +245,7 @@ public void should_throw_AuthenticationServiceException_when_null_issuer() {
253245
@Test
254246
public void should_throw_AuthenticationServiceException_when_not_matching_issuer() {
255247
JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder().issuer("not matching").build();
256-
mockPlainJWTAuthAttempt(jwtClaimsSet);
248+
mockSignedJWTAuthAttempt(jwtClaimsSet);
257249

258250
Throwable thrown = authenticateAndReturnThrownException();
259251

@@ -264,7 +256,7 @@ public void should_throw_AuthenticationServiceException_when_not_matching_issuer
264256
@Test
265257
public void should_throw_AuthenticationServiceException_when_null_expiration_time() {
266258
JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder().issuer(CLIENT_ID).expirationTime(null).build();
267-
mockPlainJWTAuthAttempt(jwtClaimsSet);
259+
mockSignedJWTAuthAttempt(jwtClaimsSet);
268260

269261
Throwable thrown = authenticateAndReturnThrownException();
270262

@@ -276,7 +268,7 @@ public void should_throw_AuthenticationServiceException_when_null_expiration_tim
276268
public void should_throw_AuthenticationServiceException_when_expired_jwt() {
277269
Date expiredDate = new Date(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(500));
278270
JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder().issuer(CLIENT_ID).expirationTime(expiredDate).build();
279-
mockPlainJWTAuthAttempt(jwtClaimsSet);
271+
mockSignedJWTAuthAttempt(jwtClaimsSet);
280272

281273
Throwable thrown = authenticateAndReturnThrownException();
282274

@@ -288,7 +280,7 @@ public void should_throw_AuthenticationServiceException_when_expired_jwt() {
288280
public void should_throw_AuthenticationServiceException_when_jwt_valid_in_future() {
289281
Date futureDate = new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(500));
290282
JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder().issuer(CLIENT_ID).expirationTime(futureDate).notBeforeTime(futureDate).build();
291-
mockPlainJWTAuthAttempt(jwtClaimsSet);
283+
mockSignedJWTAuthAttempt(jwtClaimsSet);
292284

293285
Throwable thrown = authenticateAndReturnThrownException();
294286

@@ -300,7 +292,7 @@ public void should_throw_AuthenticationServiceException_when_jwt_valid_in_future
300292
public void should_throw_AuthenticationServiceException_when_jwt_issued_in_future() {
301293
Date futureDate = new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(500));
302294
JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder().issuer(CLIENT_ID).expirationTime(futureDate).issueTime(futureDate).build();
303-
mockPlainJWTAuthAttempt(jwtClaimsSet);
295+
mockSignedJWTAuthAttempt(jwtClaimsSet);
304296

305297
Throwable thrown = authenticateAndReturnThrownException();
306298

@@ -311,7 +303,7 @@ public void should_throw_AuthenticationServiceException_when_jwt_issued_in_futur
311303
@Test
312304
public void should_throw_AuthenticationServiceException_when_unmatching_audience() {
313305
JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder().issuer(CLIENT_ID).expirationTime(new Date()).audience("invalid").build();
314-
mockPlainJWTAuthAttempt(jwtClaimsSet);
306+
mockSignedJWTAuthAttempt(jwtClaimsSet);
315307

316308
Throwable thrown = authenticateAndReturnThrownException();
317309

@@ -320,28 +312,7 @@ public void should_throw_AuthenticationServiceException_when_unmatching_audience
320312
}
321313

322314
@Test
323-
public void should_return_valid_token_for_PlainJWT_when_audience_contains_token_endpoint() {
324-
JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder()
325-
.issuer(CLIENT_ID)
326-
.subject(SUBJECT)
327-
.expirationTime(new Date())
328-
.audience(ImmutableList.of("http://issuer.com/token", "invalid"))
329-
.build();
330-
PlainJWT jwt = mockPlainJWTAuthAttempt(jwtClaimsSet);
331-
332-
Authentication authentication = jwtBearerAuthenticationProvider.authenticate(token);
333-
334-
assertThat(authentication, instanceOf(JWTBearerAssertionAuthenticationToken.class));
335-
336-
JWTBearerAssertionAuthenticationToken token = (JWTBearerAssertionAuthenticationToken) authentication;
337-
assertThat(token.getName(), is(SUBJECT));
338-
assertThat(token.getJwt(), is(jwt));
339-
assertThat(token.getAuthorities(), hasItems(authority1, authority2, authority3));
340-
assertThat(token.getAuthorities().size(), is(4));
341-
}
342-
343-
@Test
344-
public void should_return_valid_token_for_SignedJWT_when_audience_contains_token_endpoint() {
315+
public void should_return_valid_token_when_audience_contains_token_endpoint() {
345316
JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder()
346317
.issuer(CLIENT_ID)
347318
.subject(SUBJECT)
@@ -362,29 +333,7 @@ public void should_return_valid_token_for_SignedJWT_when_audience_contains_token
362333
}
363334

364335
@Test
365-
public void should_return_valid_token_for_PlainJWT_when_issuer_does_not_end_with_slash_and_audience_contains_token_endpoint() {
366-
JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder()
367-
.issuer(CLIENT_ID)
368-
.subject(SUBJECT)
369-
.expirationTime(new Date())
370-
.audience(ImmutableList.of("http://issuer.com/token"))
371-
.build();
372-
PlainJWT jwt = mockPlainJWTAuthAttempt(jwtClaimsSet);
373-
when(config.getIssuer()).thenReturn("http://issuer.com/");
374-
375-
Authentication authentication = jwtBearerAuthenticationProvider.authenticate(token);
376-
377-
assertThat(authentication, instanceOf(JWTBearerAssertionAuthenticationToken.class));
378-
379-
JWTBearerAssertionAuthenticationToken token = (JWTBearerAssertionAuthenticationToken) authentication;
380-
assertThat(token.getName(), is(SUBJECT));
381-
assertThat(token.getJwt(), is(jwt));
382-
assertThat(token.getAuthorities(), hasItems(authority1, authority2, authority3));
383-
assertThat(token.getAuthorities().size(), is(4));
384-
}
385-
386-
@Test
387-
public void should_return_valid_token_for_SignedJWT_when_issuer_does_not_end_with_slash_and_audience_contains_token_endpoint() {
336+
public void should_return_valid_token_when_issuer_does_not_end_with_slash_and_audience_contains_token_endpoint() {
388337
JWTClaimsSet jwtClaimsSet = new JWTClaimsSet.Builder()
389338
.issuer(CLIENT_ID)
390339
.subject(SUBJECT)
@@ -405,14 +354,15 @@ public void should_return_valid_token_for_SignedJWT_when_issuer_does_not_end_wit
405354
assertThat(token.getAuthorities().size(), is(4));
406355
}
407356

408-
private PlainJWT mockPlainJWTAuthAttempt() {
409-
return mockPlainJWTAuthAttempt(createJwtClaimsSet());
357+
private void mockPlainJWTAuthAttempt() {
358+
PlainJWT plainJWT = new PlainJWT(createJwtClaimsSet());
359+
when(token.getJwt()).thenReturn(plainJWT);
410360
}
411361

412-
private PlainJWT mockPlainJWTAuthAttempt(JWTClaimsSet jwtClaimsSet) {
413-
PlainJWT plainJWT = createPlainJWT(jwtClaimsSet);
414-
when(token.getJwt()).thenReturn(plainJWT);
415-
return plainJWT;
362+
private void mockEncryptedJWTAuthAttempt() {
363+
JWEHeader jweHeader = new JWEHeader.Builder(JWEAlgorithm.A128GCMKW, EncryptionMethod.A256GCM).build();
364+
EncryptedJWT encryptedJWT = new EncryptedJWT(jweHeader, createJwtClaimsSet());
365+
when(token.getJwt()).thenReturn(encryptedJWT);
416366
}
417367

418368
private SignedJWT mockSignedJWTAuthAttempt() {
@@ -436,10 +386,6 @@ private Throwable authenticateAndReturnThrownException() {
436386
throw new AssertionError("No exception thrown when expected");
437387
}
438388

439-
private PlainJWT createPlainJWT(JWTClaimsSet jwtClaimsSet) {
440-
return new PlainJWT(jwtClaimsSet);
441-
}
442-
443389
private SignedJWT createSignedJWT() {
444390
return createSignedJWT(JWSAlgorithm.RS256);
445391
}
@@ -457,11 +403,6 @@ private SignedJWT createSignedJWT(JWSAlgorithm jwsAlgorithm, JWTClaimsSet jwtCla
457403
return new SignedJWT(jwsHeader, jwtClaimsSet);
458404
}
459405

460-
private EncryptedJWT createEncryptedJWT() {
461-
JWEHeader jweHeader = new JWEHeader.Builder(JWEAlgorithm.A128GCMKW, EncryptionMethod.A256GCM).build();
462-
return new EncryptedJWT(jweHeader, createJwtClaimsSet());
463-
}
464-
465406
private JWTClaimsSet createJwtClaimsSet() {
466407
return new JWTClaimsSet.Builder()
467408
.issuer(CLIENT_ID)

0 commit comments

Comments
 (0)