Skip to content

Commit 1e9072d

Browse files
noetrojimmyjames
andauthored
Optimise TokenUtils parsing (#611)
* Optimise parsing of token for well-defined JWT format * Update error message in test to match new code * Fixing checkstyle issues * Added missing test case for no parts * Return new JWTDecodeException Return a new JWTDecodeException from private utility method `wrongNumberOfParts`, instead of throwing, since we throw from `splitToken()`. Co-authored-by: Jim Anderson <[email protected]> Co-authored-by: Jim Anderson <[email protected]>
1 parent 83eeb3b commit 1e9072d

File tree

3 files changed

+61
-11
lines changed

3 files changed

+61
-11
lines changed

lib/src/main/java/com/auth0/jwt/TokenUtils.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,33 @@ static String[] splitToken(String token) throws JWTDecodeException {
1515
if (token == null) {
1616
throw new JWTDecodeException("The token is null.");
1717
}
18-
String[] parts = token.split("\\.");
19-
if (parts.length == 2 && token.endsWith(".")) {
20-
//Tokens with alg='none' have empty String as Signature.
21-
parts = new String[]{parts[0], parts[1], ""};
18+
19+
char delimiter = '.';
20+
21+
int firstPeriodIndex = token.indexOf(delimiter);
22+
if (firstPeriodIndex == -1) {
23+
throw wrongNumberOfParts(0);
2224
}
23-
if (parts.length != 3) {
24-
throw new JWTDecodeException(
25-
String.format("The token was expected to have 3 parts, but got %s.", parts.length));
25+
26+
int secondPeriodIndex = token.indexOf(delimiter, firstPeriodIndex + 1);
27+
if (secondPeriodIndex == -1) {
28+
throw wrongNumberOfParts(2);
29+
}
30+
31+
// too many ?
32+
if (token.indexOf(delimiter, secondPeriodIndex + 1) != -1) {
33+
throw wrongNumberOfParts("> 3");
2634
}
35+
36+
String[] parts = new String[3];
37+
parts[0] = token.substring(0, firstPeriodIndex);
38+
parts[1] = token.substring(firstPeriodIndex + 1, secondPeriodIndex);
39+
parts[2] = token.substring(secondPeriodIndex + 1);
40+
2741
return parts;
2842
}
43+
44+
private static JWTDecodeException wrongNumberOfParts(Object partCount) {
45+
return new JWTDecodeException(String.format("The token was expected to have 3 parts, but got %s.", partCount));
46+
}
2947
}

lib/src/test/java/com/auth0/jwt/JWTDecoderTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public void shouldThrowIfLessThan3Parts() {
5050
@Test
5151
public void shouldThrowIfMoreThan3Parts() {
5252
exception.expect(JWTDecodeException.class);
53-
exception.expectMessage("The token was expected to have 3 parts, but got 4.");
53+
exception.expectMessage("The token was expected to have 3 parts, but got > 3.");
5454
JWT.decode("this.has.four.parts");
5555
}
5656

lib/src/test/java/com/auth0/jwt/TokenUtilsTest.java

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,30 @@ public class TokenUtilsTest {
1313
@Rule
1414
public ExpectedException exception = ExpectedException.none();
1515

16+
@Test
17+
public void toleratesEmptyFirstPart() {
18+
String token = ".eyJpc3MiOiJhdXRoMCJ9.W1mx_Y0hbAMbPmfW9whT605AAcxB7REFuJiDAHk2Sdc";
19+
String[] parts = TokenUtils.splitToken(token);
20+
21+
assertThat(parts, is(notNullValue()));
22+
assertThat(parts, is(arrayWithSize(3)));
23+
assertThat(parts[0], is(""));
24+
assertThat(parts[1], is("eyJpc3MiOiJhdXRoMCJ9"));
25+
assertThat(parts[2], is("W1mx_Y0hbAMbPmfW9whT605AAcxB7REFuJiDAHk2Sdc"));
26+
}
27+
28+
@Test
29+
public void toleratesEmptySecondPart() {
30+
String token = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0..W1mx_Y0hbAMbPmfW9whT605AAcxB7REFuJiDAHk2Sdc";
31+
String[] parts = TokenUtils.splitToken(token);
32+
33+
assertThat(parts, is(notNullValue()));
34+
assertThat(parts, is(arrayWithSize(3)));
35+
assertThat(parts[0], is("eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0"));
36+
assertThat(parts[1], is(""));
37+
assertThat(parts[2], is("W1mx_Y0hbAMbPmfW9whT605AAcxB7REFuJiDAHk2Sdc"));
38+
}
39+
1640
@Test
1741
public void shouldSplitToken() {
1842
String token = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJhdXRoMCJ9.W1mx_Y0hbAMbPmfW9whT605AAcxB7REFuJiDAHk2Sdc";
@@ -34,19 +58,27 @@ public void shouldSplitTokenWithEmptySignature() {
3458
assertThat(parts, is(arrayWithSize(3)));
3559
assertThat(parts[0], is("eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0"));
3660
assertThat(parts[1], is("eyJpc3MiOiJhdXRoMCJ9"));
37-
assertThat(parts[2], is(isEmptyString()));
61+
assertThat(parts[2], is(emptyString()));
3862
}
3963

4064
@Test
4165
public void shouldThrowOnSplitTokenWithMoreThan3Parts() {
4266
exception.expect(JWTDecodeException.class);
43-
exception.expectMessage("The token was expected to have 3 parts, but got 4.");
67+
exception.expectMessage("The token was expected to have 3 parts, but got > 3.");
4468
String token = "this.has.four.parts";
4569
TokenUtils.splitToken(token);
4670
}
4771

4872
@Test
49-
public void shouldThrowOnSplitTokenWithLessThan3Parts() {
73+
public void shouldThrowOnSplitTokenWithNoParts() {
74+
exception.expect(JWTDecodeException.class);
75+
exception.expectMessage("The token was expected to have 3 parts, but got 0.");
76+
String token = "notajwt";
77+
TokenUtils.splitToken(token);
78+
}
79+
80+
@Test
81+
public void shouldThrowOnSplitTokenWith2Parts() {
5082
exception.expect(JWTDecodeException.class);
5183
exception.expectMessage("The token was expected to have 3 parts, but got 2.");
5284
String token = "two.parts";

0 commit comments

Comments
 (0)