spomky-labs/oauth2-server-utils
Utility helpers for the spomky-labs/oauth2-server PHP library, designed to simplify common OAuth2 server setup and token-related tasks. Useful for developers building standards-compliant authorization servers with the Spomky Labs OAuth2 framework.
Install and inspect before you code against it.
composer require spomky-labs/oauth2-server-utils
This is a small, low-traffic utility package (no stars, no known dependents), so treat the source as the documentation. Your first ten minutes should be:
composer show spomky-labs/oauth2-server-utils — note the exact version and its declared dependencies (psr/http-message, and whichever spomky-labs/oauth2-server version it aligns with).vendor/spomky-labs/oauth2-server-utils/src/ end to end. It is small; you will finish it in one sitting and it is the only reliable reference you have. Look for the request-parsing entry points (typically a RequestUtil/RequestUtils style class) and any interfaces it defines for parameter extraction.composer.json for the require block — if it pins an old PSR-7 or an abandoned spomky-labs/oauth2-server major, that tells you immediately whether this fits a greenfield project or only an existing one.First use case: extracting the OAuth2 parameters from a PSR-7 ServerRequestInterface at a token endpoint — grant_type, client_id/client_secret, code, refresh_token, scope — including the Authorization: Basic client-authentication header, without hand-rolling that parsing in every controller. Wire it in front of your grant handling:
$request = Psr17Factory::createServerRequest(...); // or whatever your framework gives you
$params = $utils->getRequestParameters($request); // confirm exact signature in src/
$grantType = $params['grant_type'] ?? null;
If you are not already running a spomky-labs/oauth2-server stack, stop here and evaluate league/oauth2-server first — that is where the PHP ecosystem consolidated, and this package is not a full server.
Normalize once, at the edge. The most valuable pattern with a package like this is to call it exactly once, in middleware or a front controller, and convert the PSR-7 request into a plain value object your grant handlers consume. Downstream code should never touch getParsedBody() or getHeaderLine('Authorization') directly — that keeps the utility (and its quirks) isolated.
Client authentication extraction. The package's real job is the boring-but-easy-to-get-wrong part of OAuth2: reading client_id/client_secret from either HTTP Basic or the request body. Pattern:
scope/grant_type can never be spoofed by a body parameter named like a credential.Framework integration. There is no Laravel service provider here. Register a small singleton in your container that wraps the utility, and inject it into a TokenController. A typical Laravel wiring:
$this->app->singleton(OAuthRequestParser::class, fn () => new OAuthRequestParser(new RequestUtil()));
Then in the controller, hand it $request->toPsrRequest() (via symfony/psr-http-message-bridge or psr7 adapter) rather than the Laravel Request, because the utility is PSR-7 typed.
Compose with the grant layer, don't replace it. Use this package for request shaping and let your grant classes own validation, storage, and token issuance. Keeping that boundary means that when you eventually migrate off this package, you delete one adapter class and nothing else moves.
Test with fixture requests, not mocks. Because the parsing rules are the whole product, write table-driven tests that feed ServerRequestInterface instances (form-encoded body, Basic header, JSON body, malformed header) and assert the extracted parameter set. That test suite is your regression net against an unmaintained dependency.
Maintenance risk is the headline gotcha. Zero stars and zero dependents means no community pressure to fix bugs and no Stack Overflow coverage. Pin the version in composer.json (exact "x.y.z" or a tight range), commit composer.lock, and vendor/fork it if you ship anything long-lived. Read the changelog before every composer update.
PSR-7 bodies are streams and are not always parsed. $request->getParsedBody() returns null unless something upstream parsed it (Slim, for example, does; a bare PSR-7 request does not). If you construct requests yourself in tests or in a custom front controller, call parse_str((string) $request->getBody(), $data) — and remember that reading the stream moves its pointer, so rewind or cache if the body is read twice.
The Authorization header disappears under some SAPIs. Apache with mod_cgi/FastCGI strips it; you may need SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1 plus a check of $request->getServerParams()['HTTP_AUTHORIZATION']. This produces the maddening "works locally, 401 in production" class of bug. Log the raw header presence (not its value) in your token endpoint.
Body vs. query precedence. OAuth2 says token endpoint parameters come from the body, but libraries are inconsistent about whether they merge getQueryParams(). Decide explicitly and assert it in tests; don't inherit the library's choice by accident.
JSON clients. application/json token requests are not RFC 6749-compliant, but clients send them anyway. If the utility only handles form-encoded bodies, you will get empty parameter sets with no error — add an explicit content-type guard that returns invalid_request rather than a confusing "unsupported grant type".
Case and duplicate parameters. grant_type=Authorization_Code should not work; make sure you are not lowercasing the value. And parse_str silently lets the last duplicate win — validate against a whitelist of expected parameters and reject unexpected ones in strict mode.
Debugging technique. When a token request fails, dump the normalized parameter set (secrets redacted) from your middleware before your grant logic runs. Nine times out of ten the bug is upstream of the grant code: credentials in the wrong place, or an empty body.
Extension points are limited, so wrap rather than patch. If you need custom extraction (mTLS client auth, private_key_jwt), subclass or decorate the utility in your own namespace. Never edit vendor/; with a package this quiet, an upstream fix is unlikely to arrive and you will be re-applying the patch forever.
How can I help you explore Laravel packages today?