Revision history for Catalyst-Plugin-OAuth2-AuthorizationServer

0.003     2026-07-16
          - Genericise the example OAuth scopes (now example:*) and remove
            downstream-application references from the README and the
            perl_critic author test comment, so the distribution reads as
            standalone. Examples and tests only; no functional change.

0.002     Unreleased
          - Version bump so consumers reliably install the current code over any
            0.001 already present in an environment (cpanm treats same-version as
            satisfied); no code change from the 0.001 line below.
          - Initial MCP-profile OAuth 2.1 Authorization Server.
          - INCOMPATIBLE: code_verifier is now validated as 43-128 characters of
            the RFC 7636 4.1 unreserved set before the PKCE comparison. A client
            sending a shorter or otherwise non-conformant verifier that worked
            before now gets invalid_grant.
          - INCOMPATIBLE: the Store contract has changed; any implementation of
            Role::Store must be updated. rotate_refresh_token now returns undef
            (unknown/expired), { binding => \%binding } (was live, now revoked),
            or { binding => \%binding, reused => 1 } (a replay), instead of a
            bare binding or undef. A rotated token must be retained as a
            tombstone until its original expires_at rather than deleted, or
            reuse detection silently stops working. create_refresh_token gains
            family_id in the binding and must return false, persisting nothing,
            if that family has been revoked. revoke_family($family_id) is a new
            required verb.
          - Refresh-token reuse now revokes the whole token family (RFC 9700):
            replaying a rotated token revokes every token descended from the
            same authorization, including the one the legitimate client holds.
            The error is deliberately identical to an unknown token, so it gives
            an attacker no signal that reuse was detected.
          - Refresh rotation and family revocation are safe against a concurrent
            replay: a rotation in flight when revoke_family runs can no longer
            create its successor into the revoked family. Store implementers
            must make create_refresh_token's check-and-insert mutually exclusive
            with revoke_family for a family (SERIALIZABLE or a per-family lock);
            a bare INSERT ... WHERE NOT EXISTS write-skews under READ COMMITTED.
          - Access tokens carry a jti claim. Nothing reads it; it exists so a
            revocation denylist can be added later without changing the token
            format for tokens already issued.
          - signing_key length is now validated against the RFC 7518 3.2 minimum
            for the configured HS algorithm (32/48/64 bytes). A short key was
            previously accepted and produced brute-forceable tokens.
          - A request that repeats a parameter is now rejected with
            invalid_request (RFC 6749 3.2.1) instead of silently taking the last
            value. On /authorize the rejection renders directly and never
            redirects, since it happens before redirect_uri is validated.
          - Dynamic Client Registration rejects unsupported metadata values with
            invalid_client_metadata (RFC 7591 2) instead of storing them
            verbatim. The allow-lists are derived from the metadata document, so
            registration cannot accept what discovery does not advertise.
          - LIMITATIONS now states that family revocation does not kill access
            tokens already minted from the family (they are stateless JWTs and
            live out access_ttl), and that a concurrent double-refresh is
            indistinguishable from a replay and will revoke the family.
