Revision history for Punk-OAuth2

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
