auth0 / java-jwt

Java implementation of JSON Web Token (JWT)

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

com.auth0.jwt.exceptions.JWTDecodeException: The input is not a valid base 64 encoded string.

StefanSchubert opened this issue · comments

Hi There!

First thanks a lot for the implementation! It's the only one I currently found which is using the packaged moved to jakarta, which are now required when moving on to spring6.

Describe the problem

I switched my solution to the new Auth0 API and created a Token by it:

Sabi.Service: TokenAuthenticationService could not parse JWT Token!
com.auth0.jwt.exceptions.JWTDecodeException: The input is not a valid base 64 encoded string.
Token was: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0c2VydmljZTFAYmx1ZXdoYWxlLmRlIiwiaXNzIjoiU0FCSS1zZXJ2ZXIgbW9kdWxlIiwiZXhwIjoxNjcwMTUwMzkyfQ.MXInXRP1JDcYwkl3LFz6ZBjGKW9fe3HrSgEew88yjPMR2RMbBHJflyiMbBwommvUZkWPBFtBn5Hv-FYQqLv3EA

When placing this token into http://jwt.io debugger it could parse the token without base64 issues.
However trying to verify this token with the Auth0 API results in the Decoded Exception.

What did I wrong? My Code to create and verify the Token can be seen here:

https://github.com/StefanSchubert/sabi/blob/feature/sabi-85/sabi-server/src/main/java/de/bluewhale/sabi/security/TokenAuthenticationService.java

What was the expected behavior?

If io.JWT could parse above token, I would expect the Auth0 Verfifier coud do it, too.

Reproduction

I placed the details for Reproduction here: StefanSchubert/sabi#134

Environment

See https://github.com/StefanSchubert/sabi/blob/feature/sabi-85/sabi-server/pom.xml

Thanks for the info @StefanSchubert. Are you able to reproduce using a simple JWT.decode("token"), or with a simple creation/validation? I just tried both cases and wasn't able to reproduce. e.g.,

String jwt = JWT.create().withIssuer("SABI-server module").sign(Algorithm.HMAC512("badsecret"));
DecodedJWT decoded = JWT.decode(jwt);
DecodedJWT verified = JWT.require(Algorithm.HMAC512("badsecret"))
    .withIssuer("SABI-server module")
    .build()
    .verify(jwt);

Hi, I placed this into a junit and it is green!
Strange I tried to replace my check code with above API usage, like this:

            // Make sure we have a valid signed token here
            /*
            JWTVerifier verifier = JWT.require(JWT_TOKEN_ALGORITHM)
                    .withIssuer(SABI_JWT_ISSUER)
                    .build();

            DecodedJWT jwt = verifier.verify(token);
            */

            DecodedJWT decoded = JWT.decode(token);

            DecodedJWT verified = JWT.require(JWT_TOKEN_ALGORITHM)
                    .withIssuer("SABI-server module")
                    .build()
                    .verify(token);

            userID = verified.getSubject();

but I still get the same error, at the JWT.decode() call.

@StefanSchubert can you clarify, what conditions/code passes and which fails?

OK,

this one here passes:

    @Test
    public void testJWTTokenParsing() throws Exception {

        String jwt = JWT.create().withIssuer("SABI-server module").sign(Algorithm.HMAC512("badsecret"));
        DecodedJWT decoded = JWT.decode(jwt);
        DecodedJWT verified = JWT.require(Algorithm.HMAC512("badsecret"))
                .withIssuer("SABI-server module")
                .build()
                .verify(jwt);
        assertThat("In case something went wrong we would have encountered an exception.", verified != null);
    }

Meanwhile I wrote a second one which reflects the same API calls as I have now in my Authentication Service:

@Test
public void testJWTTokenParsing_SabiUsage() throws Exception {
    /* statements used within sabi */

    long ACCESS_TOKEN_MAX_VALIDITY_PERIOD_IN_SECS = 60;
    Algorithm JWT_TOKEN_ALGORITHM = Algorithm.HMAC512("sampleSECRET");
    String SABI_JWT_ISSUER = "Sabi-Backend Module";
    String pUserID = "junit@bluewhale.de";

    Date expiresAt = new Date(System.currentTimeMillis() + ACCESS_TOKEN_MAX_VALIDITY_PERIOD_IN_SECS * 1000);

    String jwtToken = JWT.create()
            .withSubject(pUserID)
            .withExpiresAt(expiresAt)
            .withIssuer(SABI_JWT_ISSUER)
            .sign(JWT_TOKEN_ALGORITHM);

    DecodedJWT decoded = JWT.decode(jwtToken);

    DecodedJWT verified = JWT.require(JWT_TOKEN_ALGORITHM)
            .withIssuer(SABI_JWT_ISSUER)
            .build()
            .verify(jwtToken);

    String tokenUserID = verified.getSubject();

    assertThat("In case something went wrong we would have encountered an exception.", verified != null);
    assertThat(pUserID, Matchers.equalTo(tokenUserID));
}

Astonishingly this one passes also. While my controller test which used the Authentication service fails with the message base64 decode error initially described.

One of the failing controller tests with the token error is this one:

   @Test // REST-API
    public void testUserProfileUpdateWithNewMeasurementReminder() throws Exception {

        // given a successful update answer
        UserTo testuser = new UserTo(TestDataFactory.TESTUSER_EMAIL1, "testuser", "123");
        testuser.setId(1L);
        UserProfileTo updatedUserProfileTo = testDataFactory.getUserProfileToWithMeasurementReminderFor(testuser);

        Message info = Message.info(CommonMessageCodes.UPDATE_SUCCEEDED);
        final ResultTo<UserProfileTo> userProfileResultTo = new ResultTo<>(updatedUserProfileTo, info);
        given(this.userService.updateProfile(updatedUserProfileTo, TestDataFactory.TESTUSER_EMAIL1)).willReturn(userProfileResultTo);

        // and we need a valid authentication token for our mocked user
        String authToken = TokenAuthenticationService.createAuthorizationTokenFor(TestDataFactory.TESTUSER_EMAIL1);
        HttpHeaders headers = RestHelper.prepareAuthedHttpHeader(authToken);

        // when - sending an update request
        String requestJson = objectMapper.writeValueAsString(updatedUserProfileTo);
        HttpEntity<String> requestEntity = new HttpEntity<>(requestJson, headers);
        ResponseEntity<String> responseEntity = restTemplate.exchange(Endpoint.USER_PROFILE.getPath(), HttpMethod.PUT, requestEntity, String.class);

        // then we should get a 200 as result.
        assertThat(responseEntity.getStatusCode(), equalTo(HttpStatus.OK));
        UserProfileTo retrievedUserProfileTo = objectMapper.readValue(responseEntity.getBody(), UserProfileTo.class);

        assertThat("Missing reminder entry",
                retrievedUserProfileTo.getMeasurementReminderTos().containsAll(updatedUserProfileTo.getMeasurementReminderTos()));
    }

The TokenAuthservice I am using is this here: https://github.com/StefanSchubert/sabi/blob/feature/sabi-85/sabi-server/src/main/java/de/bluewhale/sabi/security/TokenAuthenticationService.java

I do believe that there must be an error somehow with my setup as the junits with the pure api calls are green.
I do also belive that the reported base64 decode error is missleading and shaddows the real cause as the printed token is accepted by jwt.io without base64 complains.

I will report when I managed to get the same result as the junits and discovered the setup flaw on my side.
If you are able to see anything obvious I'm doing wrong let me know.

Have a nice weekend!

@jimmyjames I'm ashamed, what a silly issue. I found my fault...as usual it was right in front of my eyes and I didn't see it!
Sorry for bothering you at all!

Heres the solution:

System.out.println("Sabi.Service: TokenAuthenticationService could not parse JWT Token!"); System.out.println("Token was: " + token);

This produced my error output. Now have a look at my problem description. Above...see it? The token I'm trying to parse contains the prefix "Bearer ". I need to cut this before calling the method. It worked before migrating from io.jsonwebtoken to the auth0 impl. Maybe the previous one was more resilient and recognized by error by passing Bearer as part of the token...hm.

Anyway. Found my error. Thanks a lot for help and sorry for bothering.
Issue closed.

@StefanSchubert ha, no worries, and thanks for following up with the details 🙏 ! I don't think v3 parsed out the possible "Bearer" prefix, but regardless, glad you figured it out - that was a tricky one!