Description It is currently possible to create a NimbusJwtEncoder using an ImmutableJWKSet that contains an Ed25519 key (via OctetKeyPair), but it is not possible to use that encoder to generate a JWT signed with Ed25519 (EdDSA).

Problem Details

  • JwtEncoderParameters.from(header, claims) does not allow custom algorithms in the header.
  • Only enums implementing JwsAlgorithm or JwaAlgorithm are allowed.
  • The default SignatureAlgorithm enum implements JwsAlgorithm, but does not include any EdDSA algorithms like EdDSA (com.nimbusds.jose.JWSAlgorithm.EdDSA).
  • Even if a custom implementation of JwsAlgorithm is provided to represent EdDSA, it does not resolve the problem because of how NimbusJwtEncoder internally selects a signing key.
  • During encoding, the selectJwk method is called. This uses a JWKSelector created with a matcher generated by the private createJwkMatcher method.
  • Unfortunately, createJwkMatcher does not support the EdDSA algorithm family, which results in the JWKSelector being constructed with a null matcher.
  • This causes IllegalArgumentException to be thrown at runtime due to the missing matcher.

When running following code:

val header = JwsHeader.with(FixedSignatureAlgorithm.Ed25519).build()
val claims = JwtClaimsSet.builder()
    .issuer(issuer)
    .issuedAt(Instant.now())
    .expiresAt(expiresAt)
    .claim(SESSION_ID_CLAIM_NAME, sessionId)
    .build()

jwtEncoder.encode(JwtEncoderParameters.from(header, claims)).tokenValue
public enum FixedSignatureAlgorithm implements JwsAlgorithm {
    RS256(JwsAlgorithms.RS256),
    RS384(JwsAlgorithms.RS384),
    RS512(JwsAlgorithms.RS512),
    ES256(JwsAlgorithms.ES256),
    ES384(JwsAlgorithms.ES384),
    ES512(JwsAlgorithms.ES512),
    PS256(JwsAlgorithms.PS256),
    PS384(JwsAlgorithms.PS384),
    PS512(JwsAlgorithms.PS512),
    EdDSA(JWSAlgorithm.EdDSA.getName()),
    Ed25519(JWSAlgorithm.Ed25519.getName());

    private final String name;

    FixedSignatureAlgorithm(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }

    public static FixedSignatureAlgorithm from(String name) {
        for (FixedSignatureAlgorithm value : values()) {
            if (value.getName().equals(name)) {
                return value;
            }
        }
        return null;
    }
}

then such error is thrown:

org.springframework.security.oauth2.jwt.JwtEncodingException: An error occurred while attempting to encode the Jwt: Failed to select a JWK signing key -> The JWK matcher must not be null
    at org.springframework.security.oauth2.jwt.NimbusJwtEncoder.selectJwk(NimbusJwtEncoder.java:123)
    at org.springframework.security.oauth2.jwt.NimbusJwtEncoder.encode(NimbusJwtEncoder.java:108)
Caused by: java.lang.IllegalArgumentException: The JWK matcher must not be null
    at com.nimbusds.jose.jwk.JWKSelector.<init>(JWKSelector.java:51)
    at org.springframework.security.oauth2.jwt.NimbusJwtEncoder.selectJwk(NimbusJwtEncoder.java:119)
    ... 158 common frames omitted

Suggested fix To support Ed25519/EdDSA, the following would need to be addressed: 1. Add EdDSA (JWSAlgorithm.EdDSA) support to the SignatureAlgorithm enum or allow flexible/custom algorithm headers in JwtEncoderParameters. 2. Update createJwkMatcher in NimbusJwtEncoder to recognize and match EdDSA algorithm family keys like OctetKeyPair with Curve.Ed25519.

Used versions com.springframework.security:spring-security-oauth2-jose:6.3.3 com.nimbusds:nimbus-jose-jwt:9.40