Skip to content

fix(core): keep the cause of a load or verification failure - #699

Merged
Spomky merged 1 commit into
4.2.xfrom
fix/loader-exception-chaining
Aug 28, 2026
Merged

fix(core): keep the cause of a load or verification failure#699
Spomky merged 1 commit into
4.2.xfrom
fix/loader-exception-chaining

Conversation

@Spomky

@Spomky Spomky commented Aug 28, 2026

Copy link
Copy Markdown
Member

Closes #681.

When loading a token failed, every layer swallowed the exception explaining why, and the caller ended up with a bare Unable to load and verify the token. and no previous. Whatever the real reason was — malformed token, unsupported alg, rejected header, unusable key — the message was the same.

The last error met along the way is now chained as the previous exception. No BC break: the exception classes and the messages are unchanged, only getPrevious() is populated.

Changes

File Change
JWSSerializerManager, JWESerializerManager Unsupported input. now chains the last InvalidArgumentException thrown by a serializer
JWSLoader, JWELoader the last error — serialization, header checker or key — is passed as $previous
JWSVerifier, JWEDecrypter the per-key Throwables that are discarded to try the next key are reported to the caller

JWSVerifier::verifyWithKeySet() and JWEDecrypter::decryptUsingKeySet() return a bool and cannot throw without changing their semantics, and adding a parameter would break classes extending them. The callable that observes those failures is therefore passed as an undeclared additional argument and read with func_num_args()/func_get_arg(5) — the same pattern already used by JWEDecrypter::decryptCEK(). It will become part of the signature in 5.0.0. A subclass that overrides the method simply ignores it and behaves exactly as today.

Result

Exception - Unable to load and verify the token.
  -> InvalidArgumentException - The algorithm "RS256" is not supported.
  -> InvalidArgumentException - Wrong key type.
  -> InvalidHeaderException - Unsupported algorithm.
  -> InvalidArgumentException - Unsupported input. -> InvalidArgumentException - Unsupported input

One case still has no previous, by design: right kty, right alg, signature that does not match. No Throwable is thrown there — verify() simply returns false.

The dedicated exception hierarchy (so that catch (JoseException) becomes possible) remains the separate 4.3.0 issue.

Checks

  • 876 tests pass, including 8 new ones covering serialization failure, unsupported algorithm, rejected key and header checker failure, for both JWS and JWE
  • PHPStan, ECS and Rector are green

@Spomky Spomky added this to the 4.2.1 milestone Aug 28, 2026
@Spomky Spomky added the bug label Aug 28, 2026
@Spomky Spomky self-assigned this Aug 28, 2026
The loaders, the verifier, the decrypter and the serializer managers all
swallowed the exception explaining why a token could not be used, and the
caller was left with a bare "Unable to load and verify the token." with no
previous exception.

The last error met along the way is now chained as the previous exception. The
per-key failures of JWSVerifier and JWEDecrypter, which cannot throw without
changing the return semantics, are reported through a callable accepted as an
additional argument; it will become part of the signature in 5.0.0.

Closes #681
@Spomky
Spomky force-pushed the fix/loader-exception-chaining branch from 8b7db79 to 7e93ca0 Compare August 28, 2026 12:30
@Spomky
Spomky merged commit b8d4766 into 4.2.x Aug 28, 2026
17 checks passed
@Spomky
Spomky deleted the fix/loader-exception-chaining branch August 28, 2026 12:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Loaders and verifiers discard the original exception, making failures impossible to diagnose

1 participant