Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Oauth2 Server Public Client Laravel Package

spomky-labs/oauth2-server-public-client

Adds public client support to the spomky-labs OAuth2 server, enabling clients that cannot keep a secret to authenticate safely using authorization code flows with PKCE. Integrates with the existing server grant and client handling.

View on GitHub
Deep Wiki
Context7

Getting Started

Pull the package in alongside the core server it extends — it is a companion library, not a standalone OAuth2 server:

composer require spomky-labs/oauth2-server spomky-labs/oauth2-server-public-client

Then look at three places, in this order:

  1. The client class — the package's job is to model a client with no secret (getClientSecret() returning null / a isPublic() style flag) rather than a confidential client with a redacted secret. Read this first; it defines what the rest of the package can do.
  2. The client manager / repository — how a public client is created, persisted, and looked up. In the Spomky lineage, clients are built through a ClientManagerInterface::createClient(...)-style factory; the public-client variant is the one whose signature drops the secret argument.
  3. The token endpoint's client-authentication step — the core server normally authenticates the client via client_id + client_secret (HTTP Basic or body params). The public-client package exists so that step can be satisfied by client_id alone.

First use case: a mobile app or SPA that cannot keep a secret. Register it as a public client with an exact redirect URI, let it run the authorization-code grant without a secret, and require PKCE on the authorization request. Concretely: create the client record, hit /authorize with code_challenge, exchange the code at /token with client_id + code_verifier and no client_secret.

If you only need one public client and you control the server, sanity-check whether the core spomky-labs/oauth2-server already tolerates a null secret — this package is thin, and you may be adding a dependency for a few lines of behaviour.

Implementation Patterns

Register the client at provisioning time, not at request time. Build public clients through the manager during seeding / admin action, storing client_id, redirect URIs, allowed grant types, and allowed scopes. Keep the client_secret column nullable in your schema and assert at the domain layer that a public client never gets one written.

Separate the two client populations. Most deployments have confidential clients (server-to-server, web apps with a backend) and public clients (native, SPA). Keep distinct creation paths and distinct validation rules; a single createClient() with a nullable secret invites accidental secret-less confidential clients.

Bind PKCE unconditionally for public clients. A public client is unauthenticated, so the authorization code is the only thing standing between an attacker and a token. Enforce code_challenge/code_challenge_method=S256 at the authorization endpoint whenever the resolved client is public — do not leave it to the client to opt in. Verify code_verifier at the token endpoint and treat a missing verifier as invalid_grant.

Exact-match redirect URIs. Public clients are the case where wildcard or prefix matching is fatal. Store the full URI including path and query, compare byte-for-byte, and reject anything else with invalid_request before issuing a code.

Refresh tokens: rotate or don't issue. For public clients, either issue no refresh tokens, or rotate on every use and revoke the whole family on reuse detection. A stolen refresh token from a device is otherwise a permanent grant.

Decorator pattern for the client manager. If you need tenancy, rate limits, or audit, wrap the package's manager rather than editing it:

final class AuditingClientManager implements ClientManagerInterface
{
    public function __construct(
        private ClientManagerInterface $inner,
        private LoggerInterface $logger,
    ) {}

    public function createClient(/* ... */): ClientInterface
    {
        $client = $this->inner->createClient(/* ... */);
        $this->logger->info('client.created', ['id' => $client->getPublicId()]);
        return $client;
    }
}

Test the negative path in your own suite. A short integration test that posts to /token with a client_secret for a known public client (should be ignored or rejected consistently), one with no code_verifier (should fail), and one with a mutated redirect URI (should fail) buys more confidence than the package's own tests.

Integration tip: if you use a framework bundle (Symfony), wire the public-client manager as a distinct service alias and inject it only into the provisioning command — not into the token endpoint, which should treat all clients through the common interface.

Gotchas and Tips

  • "Public" is a security posture, not a flag. Nothing in this package makes a secret-less client safe; PKCE, exact redirect matching, short code lifetimes, and refresh rotation do. Review those before you review the package.
  • Maturity risk is real. The repository has effectively no adoption signal (0 stars, 0 dependents) and sits in the abandoned spomky-labs/oauth2-server lineage, which was superseded by web-token/jwt-framework and, for many teams, by league/oauth2-server. Pin exact versions, read the source (it's small), and budget for a fork or a migration. Do not build a long-lived product on it without a vendor-out plan.
  • Client authentication method must be none. If you advertise token_endpoint_auth_method in metadata, public clients must declare none; a mismatch causes invalid_client at the token endpoint and is a common first-hour bug.
  • Error-code confusion. Missing/invalid client_id → invalid_client; a client asking for a grant type it isn't allowed → unauthorized_client; missing verifier → invalid_grant. Endpoint consumers branch on these, so get them right rather than returning a generic 400.
  • Don't log the code or verifier. Debugging the token exchange tempts you to dump the request body; redact code, code_verifier, client_secret, and refresh_token in your PSR-3 processor.
  • Extension point: if the shipped client class is too narrow (e.g., you need per-device metadata, attestation, or a software_statement), implement the core ClientInterface yourself and let the package's manager produce your class. The interfaces are the stable contract; the concrete classes are not.
  • Watch the composer constraint. This package pins a specific major of the core server. Upgrading spomky-labs/oauth2-server without checking the public-client constraint is the most likely way to break a working install.
  • Config quirk to verify early: whether the token endpoint rejects a supplied secret for a public client or silently ignores it. Pick one behaviour, document it, and assert it in a test — implementations in this lineage have differed.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core