spring-projects/spring-security · error · BadJwtException

An error occurred while attempting to decode the Jwt…

Error message

An error occurred while attempting to decode the Jwt: Unsupported algorithm of + jwt.getHeader().getAlgorithm()

What it means

NimbusJwtDecoder.decode parses the token and rejects unauthenticated tokens: if the parsed JWT is a PlainJWT (alg=none), it throws BadJwtException 'Unsupported algorithm of none'. Spring Security never accepts unsigned JWTs since they provide no integrity.

Solutions

  1. Ensure you are sending a properly signed JWT (RS256/ES256 etc.) produced by the authorization server, not an unsigned token.
  2. Verify the client is pointing at the real token endpoint, not a stub returning unsigned tokens.
  3. Check the token has three segments with a real signature; decode the header to inspect the alg value.
  4. If you genuinely need unsigned JWT handling, use Nimbus directly — Spring Security intentionally rejects them.

Example fix

// before (client sending unsigned token)
String token = base64Header + "." + base64Payload + "."; // alg=none
// after
String token = signedJws.serialize(); // alg=RS256 with signature
Defensive patterns

Strategy: validation

Validate before calling

String[] parts = token.split("\\.");
if (parts.length != 3) throw new IllegalArgumentException("not a JWS");
String headerJson = new String(Base64.getUrlDecoder().decode(parts[0]), StandardCharsets.UTF_8);
if (headerJson.contains("\"alg\":\"none\"")) { /* reject: unsigned token */ }

Type guard

boolean isSignedJwt(String token) {
    String[] parts = token.split("\\.");
    return parts.length == 3 && !parts[2].isEmpty();
}

Try / catch

try { jwt = jwtDecoder.decode(token); }
catch (BadJwtException e) { /* return 401 invalid_token; alg=none is never acceptable */ }

Prevention

When it happens

Trigger: Calling NimbusJwtDecoder.decode(token) where JWTParser.parse returns a PlainJWT — i.e. a JWS header with alg=none and no signature segment content.

Common situations: Sending raw/unsecured tokens from dev tools or mock identity providers; tokens truncated/corrupted such that they parse as unsecured; confusion with non-JWT opaque tokens passed to a JWT decoder; attackers sending alg=none tokens (this throw is the correct security behavior).

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/1468f44bbbd17455. Report an issue: GitHub.

Appendix: source

Thrown at oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtDecoder.java:143

	 * @param claimSetConverter the {@link Converter} to use
	 */
	public void setClaimSetConverter(Converter<Map<String, Object>, Map<String, Object>> claimSetConverter) {
		Assert.notNull(claimSetConverter, "claimSetConverter cannot be null");
		this.claimSetConverter = claimSetConverter;
	}

	/**
	 * Decode and validate the JWT from its compact claims representation format.
	 * @param token the JWT value
	 * @return a validated {@link Jwt}
	 * @throws JwtException when the token is malformed or otherwise invalid
	 */
	@Override
	public Jwt decode(String token) throws JwtException {
		JWT jwt = parse(token);
		if (jwt instanceof PlainJWT) {
			this.logger.trace("Failed to decode unsigned token");
			throw new BadJwtException("Unsupported algorithm of " + jwt.getHeader().getAlgorithm());
		}
		Jwt createdJwt = createJwt(token, jwt);
		return validateJwt(createdJwt);
	}

	private JWT parse(String token) {
		try {
			return JWTParser.parse(token);
		}
		catch (Exception ex) {
			this.logger.trace("Failed to parse token", ex);
			if (ex instanceof ParseException) {
				throw new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, "Malformed token"), ex);
			}
			throw new BadJwtException(String.format(DECODING_ERROR_MESSAGE_TEMPLATE, ex.getMessage()), ex);
		}
	}

View on GitHub (pinned to 96852e8860)