'Illegal base64url character: ' ' when getting claims/decode from token Java JWT Spring Boot

When I get some claims from a JWT Token to validate user authentication I get the following error:

Illegal base64url character: ' '

Creating a JWT goes completely fine but "decoding" seems to have some issues... I also tried a base64url decoder to decode the token before getting the claims but then the token is unvalid.

My JWToken class where I encode and "decode" the token:

@Component
public class JWToken {

    private static final String JWT_USERNAME_CLAIM = "sub";
    private static final String JWT_ADMIN_CLAIM = "admin";

    @Value("${jwt.issuer}")
    private String issuer;

    @Value("${jwt.passPhrase}")
    private String passPhrase;

    @Value("${jwt.duration-of-validity}")
    private int expiration;


   public String encode(String name, boolean admin) {
       String token = Jwts.builder()
               .claim(JWT_USERNAME_CLAIM, name)
               .claim(JWT_ADMIN_CLAIM, admin)
               .setSubject(name)
               .setIssuer(issuer)
               .setIssuedAt(new Date(System.currentTimeMillis()))
               .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
               .signWith(SignatureAlgorithm.HS512, passPhrase).compact();

       return token;
   }


    //for retrieving any information from token we will need the secret key
    private Claims getAllClaimsFromToken(String token) {
        return Jwts.parser().setSigningKey(passPhrase).parseClaimsJws(token).getBody();
    }

    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    //retrieve username from jwt token
    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }

    //retrieve expiration date from jwt token
    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }

    //check if the token has expired
    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    //validate token
    public Boolean validateToken(String token, String name) {
        final String username = getUsernameFromToken(token);
        return (username.equals(name) && !isTokenExpired(token));
    }
    


}

My request filter:

@Component
public class JWTRequestFilter extends OncePerRequestFilter {
    private static final Set<String> SECURED_PATHS =
            Set.of("/api/offers", "/api/bids");

    private final JWToken jwToken;

    @Autowired
    public JWTRequestFilter(JWToken jwToken) {
        this.jwToken = jwToken;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {



        final String requestTokenHeader = request.getHeader("Authorization");
        String username = null;
        String jwtToken = null;

        String servletPath = request.getServletPath();

        if (HttpMethod.OPTIONS.matches(request.getMethod()) || SECURED_PATHS.stream().noneMatch(servletPath::startsWith)) {

            filterChain.doFilter(request, response);
            return;
        }

        // JWT Token is in the form "Bearer token". Remove Bearer word and get
        // only the Token
        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                System.out.println(jwtToken);
                username = jwToken.getUsernameFromToken(jwtToken);
            } catch (IllegalArgumentException e) {
                System.out.println("Unable to get JWT Token");
                throw new AuthenticationException("authentication problem");
            } catch (ExpiredJwtException e) {
                throw new AuthenticationException("authentication problem");
            }
        } else {
            System.out.println(requestTokenHeader);
            logger.warn("JWT Token does not begin with Bearer String");
            //throw new AuthenticationException("authentication problem");
        }

        if(jwToken.validateToken(jwtToken, username)){
            filterChain.doFilter(request, response);
        }

        // Once we get the token validate it.

        }

}

The error in my console when I do a get request for /api/offers with the generated JWT token in the header:

io.jsonwebtoken.io.DecodingException: Illegal base64url character: ' '
    at io.jsonwebtoken.io.Base64.ctoi(Base64.java:221) ~[jjwt-api-0.11.2.jar:0.11.2]
    at io.jsonwebtoken.io.Base64.decodeFast(Base64.java:270) ~[jjwt-api-0.11.2.jar:0.11.2]
    at io.jsonwebtoken.io.Base64Decoder.decode(Base64Decoder.java:36) ~[jjwt-api-0.11.2.jar:0.11.2]
    at io.jsonwebtoken.io.Base64Decoder.decode(Base64Decoder.java:23) ~[jjwt-api-0.11.2.jar:0.11.2]
    at io.jsonwebtoken.io.ExceptionPropagatingDecoder.decode(ExceptionPropagatingDecoder.java:36) ~[jjwt-api-0.11.2.jar:0.11.2]
    at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:309) ~[jjwt-impl-0.11.2.jar:0.11.2]
    at io.jsonwebtoken.impl.DefaultJwtParser.parse(DefaultJwtParser.java:550) ~[jjwt-impl-0.11.2.jar:0.11.2]
    at io.jsonwebtoken.impl.DefaultJwtParser.parseClaimsJws(DefaultJwtParser.java:610) ~[jjwt-impl-0.11.2.jar:0.11.2]
    at team10.server.aucserver.security.JWToken.getAllClaimsFromToken(JWToken.java:47) ~[classes/:na]
    at team10.server.aucserver.security.JWToken.getClaimFromToken(JWToken.java:51) ~[classes/:na]
    at team10.server.aucserver.security.JWToken.getUsernameFromToken(JWToken.java:57) ~[classes/:na]
    at team10.server.aucserver.security.JWTRequestFilter.doFilterInternal(JWTRequestFilter.java:52) ~[classes/:na]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.1.jar:5.3.1]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.1.jar:5.3.1]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.1.jar:5.3.1]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.1.jar:5.3.1]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.1.jar:5.3.1]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.1.jar:5.3.1]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.1.jar:5.3.1]
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[na:na]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) ~[na:na]
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.39.jar:9.0.39]
    at java.base/java.lang.Thread.run(Thread.java:832) ~[na:na]

And line 47 is the getAllClaimsFromToken method in the JWToken class.

For an extra example this is one of the tokens the encode genrated:

Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJyb25ueSIsImFkbWluIjpmYWxzZSwiaXNzIjoicHJpdmF0ZSBjb21wYW55IiwiaWF0IjoxNjExMDA1NTc4LCJleHAiOjE2MTEwMDY3Nzh9.dQwEVfSNa6EIx-U-bgHN50hmrN0wYkj-8jXRoFLTx6JB53ERBWuGUeiXLqtiJ_jTGxEISB-Lv7E9KAyPk8nV3g


Solution 1:[1]

For some reason the substring function kept some white space before the token. I changed that line in my JWTRequestFilter.

Old:

jwtToken = requestTokenHeader.substring(7);

New:

jwtToken = requestTokenHeader.split(" ")[1].trim();

The added .trim() will delete any white space before or after the string, so that was the solution for me

Solution 2:[2]

What you are decoding isn't the token, you're trying to decode the entire header value. Bearer isn't part of the token, it's the authentication scheme.

More generally, you're writing your own security infrastructure, which is almost always a very bad idea. Spring Security JWT handles all of this for you automatically; use it instead.

Solution 3:[3]

U can also use this :

jwtToken = requestTokenHeader.substring("Bearer ".length());

It solved the some problem for me.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 chrylis -cautiouslyoptimistic-
Solution 3 mohamedpieerre