Skip to content

Commit 89b4f54

Browse files
authored
Merge pull request #2 from avaje/feature/exceptions-and-javadoc
Add JwtKeyException, all exceptions throws extend JwtVerifyException,…
2 parents 988e492 + 268459f commit 89b4f54

File tree

9 files changed

+124
-23
lines changed

9 files changed

+124
-23
lines changed

avaje-oauth2-core/src/main/java/io/avaje/oauth2/core/jwt/DJwtVerifier.java

+10-10
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ final class DJwtVerifier implements JwtVerifier {
2626
private final Duration clockSkew;
2727
private final Clock clock;
2828

29-
private DJwtVerifier(Map<String, AlgorithmVerifier> map,
30-
JwtKeySource keySource,
31-
JsonDataMapper mapper,
32-
String expectedIssuer,
33-
Duration clockSkew,
34-
Clock clock) {
29+
private DJwtVerifier(
30+
Map<String, AlgorithmVerifier> map,
31+
JwtKeySource keySource,
32+
JsonDataMapper mapper,
33+
String expectedIssuer,
34+
Duration clockSkew,
35+
Clock clock) {
3536
this.map = map;
3637
this.keySource = keySource;
3738
this.mapper = mapper;
@@ -45,7 +46,7 @@ static JwtVerifier.Builder builder() {
4546
}
4647

4748
@Override
48-
public AccessToken verifyAccessToken(String accessToken) {
49+
public AccessToken verifyAccessToken(String accessToken) throws JwtVerifyException {
4950
SignedJwt accessTokenJwt = SignedJwt.parse(accessToken);
5051
verify(accessTokenJwt);
5152

@@ -172,7 +173,6 @@ public JwtVerifier.Builder clockSkew(Duration clockSkew) {
172173
return this;
173174
}
174175

175-
176176
@Override
177177
public JwtVerifier build() {
178178
if (mapper == null) {
@@ -208,7 +208,7 @@ public Signature get() {
208208
try {
209209
return Signature.getInstance(algorithm);
210210
} catch (NoSuchAlgorithmException e) {
211-
throw new RuntimeException(e);
211+
throw new JwtVerifyException("Unsupported algorithm", e);
212212
}
213213
}
214214
}
@@ -228,7 +228,7 @@ boolean verify(PublicKey publicKey, final byte[] content, final byte[] signature
228228
verifier.update(content);
229229
return verifier.verify(signature);
230230
} catch (InvalidKeyException e) {
231-
throw new IllegalArgumentException("Invalid public key", e);
231+
throw new JwtVerifyException("Invalid public key", e);
232232
} catch (SignatureException e) {
233233
return false;
234234
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package io.avaje.oauth2.core.jwt;
2+
3+
/**
4+
* Exception when obtaining or building key used to verify a JWT.
5+
*/
6+
public class JwtKeyException extends JwtVerifyException {
7+
8+
public JwtKeyException(String message) {
9+
super(message);
10+
}
11+
12+
public JwtKeyException(String message, Exception e) {
13+
super(message, e);
14+
}
15+
}

avaje-oauth2-core/src/main/java/io/avaje/oauth2/core/jwt/JwtKeySource.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public interface JwtKeySource {
1212
/**
1313
* Return the public key for the given key id.
1414
*/
15-
PublicKey key(String kid);
15+
PublicKey key(String kid) throws JwtKeyException;
1616

1717
/**
1818
* Build an immutable KeySource from the KeySet.

avaje-oauth2-core/src/main/java/io/avaje/oauth2/core/jwt/JwtVerifier.java

+67
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,103 @@
77
import java.time.Clock;
88
import java.time.Duration;
99

10+
/**
11+
* Verify that a JWT is valid.
12+
*
13+
* <pre>{@code
14+
*
15+
* String issuer = "https://cognito-idp.<region>.amazonaws.com/<endpoint>"
16+
*
17+
* JwtVerifier verifier =
18+
* JwtVerifier.builder()
19+
* .issuer(issuer)
20+
* .build()
21+
*
22+
* }</pre>
23+
*/
1024
public interface JwtVerifier {
1125

26+
/**
27+
* Create and return a builder for JwtVerifier.
28+
*/
1229
static Builder builder() {
1330
return DJwtVerifier.builder();
1431
}
1532

33+
/**
34+
* Verify that the SignedJwt is valid.
35+
*/
1636
void verify(SignedJwt jwt) throws JwtVerifyException;
1737

38+
/**
39+
* Parse and verify the accessToken.
40+
*
41+
* @param accessToken The raw JWT access token.
42+
* @return The verified AccessToken.
43+
* @throws JwtVerifyException When the access token is not valid.
44+
*/
1845
AccessToken verifyAccessToken(String accessToken) throws JwtVerifyException;
1946

47+
/**
48+
* Builder for JwtVerifier.
49+
*/
2050
interface Builder {
2151

52+
/**
53+
* Add RSA 256 algorithm signing.
54+
*/
2255
Builder addRS256();
2356

57+
/**
58+
* Add an algorithm that this verifier will support.
59+
*
60+
* @param key The key for the algorithm
61+
* @param algorithm The algorithm
62+
*/
2463
Builder add(String key, String algorithm);
2564

65+
/**
66+
* Add a key source.
67+
*/
2668
Builder keySource(JwtKeySource keySource);
2769

70+
/**
71+
* Add a URI for remote JSON Web Key Set.
72+
*/
2873
Builder jwksUri(String jwksUri);
2974

75+
/**
76+
* Add a JsonDataMapper to parse the various json payloads.
77+
* <p>
78+
* A default is provided when a mapper is not explicitly specified.
79+
*/
3080
Builder jsonMapper(JsonDataMapper mapper);
3181

82+
/**
83+
* Add the HttpClient to use. Defaults to creating a new HttpClient.
84+
*/
3285
Builder httpClient(HttpClient httpClient);
3386

87+
/**
88+
* Specify the Issuer.
89+
* <p>
90+
* Cognito example: <code>https://cognito-idp.{region}.amazonaws.com/{endpoint}</code>
91+
*/
3492
Builder issuer(String expectedIssuer);
3593

94+
/**
95+
* Specify the Clock to use. Defaults to <code>Clock.systemDefaultZone()</code>
96+
*/
3697
Builder clock(Clock clock);
3798

99+
/**
100+
* Specify the Clock skew to use. Defaults to 60 seconds.
101+
*/
38102
Builder clockSkew(Duration clockSkew);
39103

104+
/**
105+
* Build and return the JwtVerifier.
106+
*/
40107
JwtVerifier build();
41108
}
42109

Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
package io.avaje.oauth2.core.jwt;
22

3+
/**
4+
* Verification of JWT failed.
5+
*/
36
public class JwtVerifyException extends RuntimeException {
7+
48
public JwtVerifyException(String message) {
59
super(message);
610
}
11+
12+
public JwtVerifyException(String message, Exception cause) {
13+
super(message, cause);
14+
}
715
}

avaje-oauth2-core/src/main/java/io/avaje/oauth2/core/jwt/RemoteKeySetSource.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ private void reloadKeys() {
3737
}
3838

3939
@Override
40-
public PublicKey key(String kid) {
40+
public PublicKey key(String kid) throws JwtKeyException {
4141
final PublicKey publicKey = publicKeys.get(kid);
4242
if (publicKey != null) {
4343
return publicKey;
@@ -46,7 +46,7 @@ public PublicKey key(String kid) {
4646
reloadKeys();
4747
final PublicKey refreshedKey = publicKeys.get(kid);
4848
if (refreshedKey == null) {
49-
throw new IllegalArgumentException("Unable to provide key for " + kid);
49+
throw new JwtKeyException("Unable to provide key for " + kid);
5050
}
5151
return refreshedKey;
5252
}
@@ -64,13 +64,13 @@ private String readBody() {
6464
if (statusCode < 400) {
6565
return res.body();
6666
}
67-
throw new RuntimeException("Unexpected status code " + res.statusCode() + " obtaining jwks json from " + jwksUri);
67+
throw new JwtKeyException("Unexpected status code " + res.statusCode() + " obtaining jwks json from " + jwksUri);
6868

6969
} catch (InterruptedException e) {
7070
Thread.currentThread().interrupt();
71-
throw new RuntimeException(e);
71+
throw new JwtKeyException("Error obtaining remote Key", e);
7272
} catch (IOException e) {
73-
throw new RuntimeException(e);
73+
throw new JwtKeyException("Error obtaining remote Key", e);
7474
}
7575
}
7676

avaje-oauth2-core/src/main/java/io/avaje/oauth2/core/jwt/SignedJwt.java

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
1-
21
package io.avaje.oauth2.core.jwt;
32

3+
/**
4+
* Signed JWT.
5+
*
6+
* @param header The raw header content.
7+
* @param payload The raw payload content.
8+
* @param contentBytes The content bytes used for verifying.
9+
* @param signatureBytes The signature bytes for verifying.
10+
*/
411
public record SignedJwt(
512
String header,
613
String payload,
714
byte[] contentBytes,
815
byte[] signatureBytes) {
916

10-
public static SignedJwt parse(String rawToken) {
17+
/**
18+
* Parse the raw JWT content and return as SignedJwt.
19+
*
20+
* @throws JwtVerifyException When the token is not SignedJwt
21+
*/
22+
public static SignedJwt parse(String rawToken) throws JwtVerifyException {
1123
return SignedJwtParser.parse(rawToken);
1224
}
1325

avaje-oauth2-core/src/main/java/io/avaje/oauth2/core/jwt/SignedJwtParser.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,16 @@
77

88
final class SignedJwtParser {
99

10-
static SignedJwt parse(String rawToken) {
10+
static SignedJwt parse(String rawToken) throws JwtVerifyException {
1111
int first = rawToken.indexOf('.');
1212
int second = rawToken.indexOf('.', first + 1);
1313
int third = rawToken.indexOf('.', second + 1);
1414
if (third != -1) {
15-
throw new IllegalArgumentException("Only support signed jwt token");
15+
throw new JwtVerifyException("Only signed jwt token supported");
1616
}
1717

1818
String headerDecoded = decodeString(rawToken.substring(0, first));
1919
String payloadDecoded = decodeString(rawToken.substring(first + 1, second));
20-
2120
byte[] contentBytes = rawToken.substring(0, second).getBytes(StandardCharsets.UTF_8);
2221
byte[] signatureBytes = decode(rawToken.substring(second + 1));
2322

avaje-oauth2-core/src/main/java/io/avaje/oauth2/core/jwt/UtilRSA.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ final class UtilRSA {
1212

1313
static PublicKey createRsaKey(KeySet.KeyInfo key) {
1414
if (!"RS256".equals(key.algorithm())) {
15-
throw new IllegalArgumentException("Unsupported key algorithm: " + key.algorithm());
15+
throw new JwtKeyException("Unsupported key algorithm: " + key.algorithm());
1616
}
1717
return createRsaKey(key.exponent(), key.modulus());
1818
}
@@ -25,7 +25,7 @@ static PublicKey createRsaKey(String exponentBase64, String modulusBase64) {
2525
KeyFactory factory = KeyFactory.getInstance("RSA");
2626
return factory.generatePublic(new RSAPublicKeySpec(modulus, exponent));
2727
} catch (Exception e) {
28-
throw new RuntimeException(e);
28+
throw new JwtKeyException("Unable to create PublicKey", e);
2929
}
3030
}
3131

0 commit comments

Comments
 (0)