Skip to content

Optimise TokenUtils parsing #611

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions lib/src/main/java/com/auth0/jwt/TokenUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,33 @@ static String[] splitToken(String token) throws JWTDecodeException {
if (token == null) {
throw new JWTDecodeException("The token is null.");
}
String[] parts = token.split("\\.");
if (parts.length == 2 && token.endsWith(".")) {
//Tokens with alg='none' have empty String as Signature.
parts = new String[]{parts[0], parts[1], ""};

char delimiter = '.';

int firstPeriodIndex = token.indexOf(delimiter);
if (firstPeriodIndex == -1) {
throw wrongNumberOfParts(0);
}
if (parts.length != 3) {
throw new JWTDecodeException(
String.format("The token was expected to have 3 parts, but got %s.", parts.length));

int secondPeriodIndex = token.indexOf(delimiter, firstPeriodIndex + 1);
if (secondPeriodIndex == -1) {
throw wrongNumberOfParts(2);
}

// too many ?
if (token.indexOf(delimiter, secondPeriodIndex + 1) != -1) {
throw wrongNumberOfParts("> 3");
}

String[] parts = new String[3];
parts[0] = token.substring(0, firstPeriodIndex);
parts[1] = token.substring(firstPeriodIndex + 1, secondPeriodIndex);
parts[2] = token.substring(secondPeriodIndex + 1);

return parts;
}

private static JWTDecodeException wrongNumberOfParts(Object partCount) {
return new JWTDecodeException(String.format("The token was expected to have 3 parts, but got %s.", partCount));
}
}
2 changes: 1 addition & 1 deletion lib/src/test/java/com/auth0/jwt/JWTDecoderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void shouldThrowIfLessThan3Parts() {
@Test
public void shouldThrowIfMoreThan3Parts() {
exception.expect(JWTDecodeException.class);
exception.expectMessage("The token was expected to have 3 parts, but got 4.");
exception.expectMessage("The token was expected to have 3 parts, but got > 3.");
JWT.decode("this.has.four.parts");
}

Expand Down
38 changes: 35 additions & 3 deletions lib/src/test/java/com/auth0/jwt/TokenUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,30 @@ public class TokenUtilsTest {
@Rule
public ExpectedException exception = ExpectedException.none();

@Test
public void toleratesEmptyFirstPart() {
String token = ".eyJpc3MiOiJhdXRoMCJ9.W1mx_Y0hbAMbPmfW9whT605AAcxB7REFuJiDAHk2Sdc";
String[] parts = TokenUtils.splitToken(token);

assertThat(parts, is(notNullValue()));
assertThat(parts, is(arrayWithSize(3)));
assertThat(parts[0], is(""));
assertThat(parts[1], is("eyJpc3MiOiJhdXRoMCJ9"));
assertThat(parts[2], is("W1mx_Y0hbAMbPmfW9whT605AAcxB7REFuJiDAHk2Sdc"));
}

@Test
public void toleratesEmptySecondPart() {
String token = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0..W1mx_Y0hbAMbPmfW9whT605AAcxB7REFuJiDAHk2Sdc";
String[] parts = TokenUtils.splitToken(token);

assertThat(parts, is(notNullValue()));
assertThat(parts, is(arrayWithSize(3)));
assertThat(parts[0], is("eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0"));
assertThat(parts[1], is(""));
assertThat(parts[2], is("W1mx_Y0hbAMbPmfW9whT605AAcxB7REFuJiDAHk2Sdc"));
}

@Test
public void shouldSplitToken() {
String token = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJhdXRoMCJ9.W1mx_Y0hbAMbPmfW9whT605AAcxB7REFuJiDAHk2Sdc";
Expand All @@ -34,19 +58,27 @@ public void shouldSplitTokenWithEmptySignature() {
assertThat(parts, is(arrayWithSize(3)));
assertThat(parts[0], is("eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0"));
assertThat(parts[1], is("eyJpc3MiOiJhdXRoMCJ9"));
assertThat(parts[2], is(isEmptyString()));
assertThat(parts[2], is(emptyString()));
}

@Test
public void shouldThrowOnSplitTokenWithMoreThan3Parts() {
exception.expect(JWTDecodeException.class);
exception.expectMessage("The token was expected to have 3 parts, but got 4.");
exception.expectMessage("The token was expected to have 3 parts, but got > 3.");
String token = "this.has.four.parts";
TokenUtils.splitToken(token);
}

@Test
public void shouldThrowOnSplitTokenWithLessThan3Parts() {
public void shouldThrowOnSplitTokenWithNoParts() {
exception.expect(JWTDecodeException.class);
exception.expectMessage("The token was expected to have 3 parts, but got 0.");
String token = "notajwt";
TokenUtils.splitToken(token);
}

@Test
public void shouldThrowOnSplitTokenWith2Parts() {
exception.expect(JWTDecodeException.class);
exception.expectMessage("The token was expected to have 3 parts, but got 2.");
String token = "two.parts";
Expand Down