spring-projects / spring-projects/spring-security

NimbusJwtEncoder should select only private keys for signing JWTs

Open Beginner friendly
#19,311 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage type: enhancement
Dominant language
Java
Stars
9.6k
Forks
6.3k
Avg merge
2d 11h
Merged PRs (30d)
52

Description

Expected Behavior

Given a JWKSource for a bunch of JWKs, the JWKSelector used in NimbusJwtEncoder should select for signing only JWKs that include the private key material.

Current Behavior

The JWKSelector selects privateOnly keys for HMAC_SHA but not for RSA or EC.

Context

NimbusJwtEncoder uses a JWKSelector to select candidate keys for signing the JWT. For HMAC-based algorithms (symmetric "signature") the selector includes privateOnly(true), correctly but arguably unnecessarily since symmetric keys are based on shared secrets, which are always private. Conversely, for RSA and EC algorithms (using private/public key-pairs) the selector does not include privateOnly(true), even though only private keys are suitable for JWT signing.

Having a JWKSource that exposes a set of keys where only the current signing key contains private key material, while older keys are retained as public-only JWKs for JWT verification, is a legitimate use case. This is a common pattern when signing keys are rotated regularly and previously issued JWTs must remain verifiable until they expire. However, it is not supported out of the box, as the JWKSelector would select both private and public-only keys, and it would fail with

Failed to select a key since there are multiple for the signing algorithm

I understand I can provide a custom Converter<List<JWK>, JWK> for further selection, e.g.:

Converter<List<JWK>, JWK> jwkConverter = jwks -> {
  List<JWK> privateJwks = new ArrayList<>();
  for (JWK jwk : jwks) {
    if (jwk.isPrivate()) {
      privateJwks.add(jwk);
    }
  }
  if (privateJwks.size() != 1) {
    throw new JwtEncodingException("...");
  }
  return privateJwks.getFirst();
};

but why is this the default behavior?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in oauth2/oauth2-jose/src/main/java/org/springframework/security/oauth2/jwt/NimbusJwtEncoder.java around the JWKSelector at line 227. Inspect how RSA, EC, and HMAC candidates are selected for signing, then verify the behavior with a JWKSource containing private and public-only keys; done means signing selects only suitable private key material for rotated keys.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
backend, security
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.