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
Keyboard Shortcuts
Global
s
Focus search bar
?
Bring up this help dialog
GitHub
gp
Go to pull requests
gi
Go to GitHub issues (only if GitHub is preferred repository)