Revision history for Punk-OAuth2

0.08    2026-09-15
        - FIX: a consent hook that returned a RESPONSE (a consent screen,
          rather than an approve or deny) had that response freed while
          the caller still held it. It was mortalised on the way out of
          the authorize handler and mortalised again by the XSUB's own
          RETVAL, so one owned reference carried two decrements. The
          symptom is a SIGSEGV in the host application's cleanup, on a
          poisoned pointer, with no frame from this distribution in the
          stack. Nothing exercised it: no test here configured a consent
          hook at all, and every authenticate hook returned a plain
          string. t/50-hook-refs.t now drives both reference branches.
        - RFC 8707 resource indicators. /authorize and /token take a
          repeatable `resource`, checked against the client's registered
          `resources` (deny by default, as redirect_uris already is),
          carried on the code and on the refresh token, and minted into
          `aud`, so a token for one resource server is refused by another.
          A request naming no resource is unchanged: `aud` is the issuer.
        - RFC 7591 dynamic client registration at POST /register,
          advertised as `registration_endpoint`. Creates a public client
          with no secret, the code and refresh grants only, and https or
          loopback redirect URIs. Mount it behind the application's own
          per-address quota.
        - Checker->guard takes `resource_metadata`, adding RFC 9728's
          parameter to the WWW-Authenticate challenge so a client can find
          out where to authenticate. Unset, the header does not change.
        - The token endpoint's form parser keeps a repeated parameter
          instead of overwriting it, matching Punk's own query parser.
        - A `claims` hook on the authorization server, for private claims
          on the access token. It runs at /authorize with the client, the
          user and the scopes, and what it returns is bound to the code,
          so a token carries what the user approved and a token request
          cannot influence it. A refresh carries them forward. A private
          claim never overwrites a registered one.
        - Store: `resources` on clients, `resource` and `claims` on codes
          and refresh tokens, added to an existing database by ALTER
          TABLE at migrate.

0.07    2026-08-24
        - Require Fetch 0.19 for the io_uring cancelled deadline crash.
        - Gate fetch_abi on the version this dist calls, not the header's.

0.06    2026-08-23
        - Lower the min perl from 5.24 to 5.10
        - Never hand an SV * RETVAL an immortal
        - Load Fetch, MIME::Base64 and DBI once per process rather than
          once per request
        - t/40-server.t and t/44-capstone.t skip without DBI and DBD::SQLite
          instead of dying

0.05    2026-08-20
        - FIX: change abi version checks from == to <=

0.04    2026-08-18
        - SECURITY fix CVE-2026-75866: the authorization server issued access tokens outside
          a client's registration, reported by cpansec. 
        - grant_types and scopes are now enforced, deny by default.
        - SECURITY: client_credentials is refused to a public client with
          invalid_client (RFC 6749 4.4). 
        - COMPATIBILITY: a client registered without scopes now gets no
          scope, and one registered without grant_types can use no grant.

0.03    2026-08-18
        - SECURITY: fix an open redirect in same_origin_path
          (CVE-2026-75628) reported by cpansec. The guard rejected a leading "//" and any
          CR/LF, but a browser removes TAB/CR/LF from a URL before it
          parses it and treats "\" as "/" under a special scheme, so
          "/\evil.example" and "/<TAB>/evil.example" both survived the
          check and then resolved to another host. A ?return= destination
          that a user controls could therefore redirect off-site after a
          successful login.
        - same_origin_path now rejects every C0 control byte and DEL, and
          rejects a backslash anywhere in the path, rather than trying to
          enumerate the bytes a URL parser might drop. Paths that need a
          backslash must percent-encode it as %5C.

0.02    2026-08-16
        Fix stack corruption in the DBI-backed server store: SvIV(POPs)
        in pox_dbi_do (and SvTRUE(POPs) in pox_await) evaluate POPs
        twice, walking the stack pointer back into the caller's
        arguments.

0.01    2026-08-15
        First version.
        - the client side of OAuth2 and OpenID Connect for Punk:
          authorization-code flow with PKCE (S256), signed single-use
          state, OIDC nonce, id_token verification through Crypt::JWS and
          a cached JWKS, normalized identities
        - oauth2 and oauth2_login keywords (both plugin/keyword orderings
          valid, to_app tripwire); presets google, github, generic oidc
          with RFC 8414/OIDC discovery (issuer-checked, SSRF-guarded)
        - context helpers oauth2_authurl, oauth2_refresh, oauth2_provider
        - implemented in XS over the house C ABIs (jws_abi, frj_abi,
          fetch_abi), with an xs/ directory of fragments like Punk: the
          Provider and JWKS objects, Tokens, presets, the whole login
          flow (state/nonce/PKCE mint, session flow records, the
          callback's state/iss/code validation, token exchange, identity
          normalization), id_token verification, form/URL/base_url/
          safe_url/await all run in C; the only Perl is the plugin's Punk
          registration wiring (install_kw/route/helper) and POD-only
          module loaders
        - identity-only session default: provider tokens are handed to
          on_login and discarded (nothing token-shaped in the 4KB
          signed-cookie session)
        - authorization server (oauth2_server keyword): /authorize,
          /token, /revoke, /introspect, /jwks.json and RFC 8414
          metadata; grants authorization_code + PKCE (S256), refresh
          with rotation and family revocation on reuse, and
          client_credentials; ES256 JWT access tokens (RFC 9068 at+jwt)
          the resource-server checker validates statelessly; client auth
          (basic or body, constant-time on digests); authenticate and
          consent hooks; a pluggable DBI store (SQLite, digest-only,
          auto_migrate) - all protocol logic in XS
        - resource-server checkers (Punk::OAuth2::Checker): jwt (local
          JWT validation via JWKS or a static key - allowlist, signature,
          iss/aud/exp/nbf, scope) and introspect (RFC 7662, cached by
          token SHA-256), matching the OpenAPI security-map contract,
          plus a guard wrapper emitting RFC 6750 WWW-Authenticate
          (401 invalid_token / 403 insufficient_scope); all validation
          in XS
