NAME
Catalyst::Plugin::OAuth2::AuthorizationServer::Server - Pure-logic OAuth 2.1 Authorization Server engine
DESCRIPTION
The pure-logic OAuth 2.1 engine behind the Catalyst seam. Holds the Store reference, the signing key, and configuration; is otherwise stateless (the only mutable state lives in the injected Store).
Access tokens are signed with a symmetric HMAC algorithm only: jwt_alg may be HS256 (the default), HS384 or HS512. Asymmetric signing (RS*, ES*, PS*) and alg=none are not supported, and no JWKS is published: this is deliberate for the MCP single-server profile, where the Authorization Server and Resource Server share one deployment and one key. signing_key must be at least as long as the algorithm's hash output (32, 48 or 64 bytes respectively, per RFC 7518 3.2); a shorter key is rejected at construction.
METHODS
mint_access_token( \%claims, $aud )
Mint a signed JWT access token. The engine stamps iss, aud, iat, exp and jti, and they are stamped after \%claims, so a caller cannot override them. $aud defaults to the configured resource list. Returns the encoded JWT string.
register_client( \%metadata )
Dynamic Client Registration (RFC 7591). Validates redirect_uris (must be present; each must be HTTPS or loopback HTTP; no fragments; within length limits). Generates a client_id, calls Store::create_client, and returns the stored client hashref.
Per RFC 7591 3.2.1, registration also rejects metadata asking for anything this AS does not support, with invalid_client_metadata. The allow-lists are taken from "metadata_document", so registration can never accept a value the discovery document does not advertise:
token_endpoint_auth_methodmust be one oftoken_endpoint_auth_methods_supported(none: this profile registers public PKCE clients, soclient_secret_basicand friends are rejected).grant_typesmust be a non-empty arrayref, each value one ofgrant_types_supported(authorization_code,refresh_token).response_typesmust be a non-empty arrayref, each value one ofresponse_types_supported(code).scopeis checked againstscopes_supportedonly when the AS is configured with one; with noscopes_supportedthe server declares no constraint, soscopeis left free-form.
Everything else RFC 7591 leaves free-form (client_name, client_uri, logo_uri, contacts, software_id, extension fields) is stored as given and never rejected merely for being present.
validate_authorize( \%params )
Validate an authorization request (RFC 6749 4.1.1 + PKCE RFC 7636). Checks client, redirect_uri, response_type, code_challenge (must be a 43-character base64url string), code_challenge_method (must be S256), scope, and resource. On success, stashes the request via Store::save_authorization_request and returns { request_id }.
issue_code( $subject, $request_id )
Atomically consume the stashed authorization request and mint a single-use authorization code bound to $subject. Returns { code, redirect_uri, state }. Throws invalid_request if the request is unknown or expired.
exchange_authorization_code( \%params )
Authorization-code grant (RFC 6749 4.1.3). Validates code, redirect_uri, client_id binding, and PKCE verifier. Returns { access_token, token_type, expires_in, refresh_token } (plus scope if the request carried one).
refresh( \%params )
Refresh-token grant (RFC 6749 6). Rotates the refresh token via Store::rotate_refresh_token and mints a new access + refresh pair. Optionally validates a client_id parameter against the binding.
metadata_document
Return the RFC 8414 Authorization Server Metadata hashref.