And if you really must decode it, then you can call the base64 decode yourself - and know what you're doing is unsafe.
In the case of an unverified token, it is best to pretend the token doesn't even exist.
Check out e.g. auth0's doc. https://auth0.com/docs/tokens/json-web-tokens/validate-json-...
> If the JOSE Header contains a "cty" (content type) value of "JWT", then the Message is a JWT that was the subject of nested signing or encryption operations. In this case, return to Step 1, using the Message as the JWT. [0]
This is also step 8 of validating a JWT. There is a number of validation steps you need to take before you can decide how to process the payload.
You'll also note that none of the previous 7 steps actually decode the payload ("the Message") - they only find it. And 4 of those steps are about verifying that the header isn't dangerous.
validator = new JwtValidator();
// By default, the validator should reject everything, until you call…
validator.add_signing_key(new RsaSigned((a strongly-typed RSA key));
// I wouldn't even add this to the library, but let's
// say you want to comply with the RFC fully:
validator.add_signing_key(new CompletelyUnsafeNoneAlgorithm());
// (And I'd name it that explicitly, to let the consumer know that
// what they're doing is nuts.)
This validator knows exactly which algorithms — and keys — are valid.If the full keyset is somehow not known ahead of time, a callback to enumerate keys of a certain type could be configured.
Better API design here prevents this vuln. from ever happening.
(Personally, I wish the RFC had never added "alg": "none"; it's a mistake waiting to happen. I wish JWS/JWT could be updated to remove it.)