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

Oauth Negotiator Laravel Package

bmatovu/oauth-negotiator

bmatovu/oauth-negotiator is a Laravel package for negotiating OAuth access tokens. It offers a fluent interface to obtain, refresh, and manage tokens from multiple providers, making third-party API integration simple with minimal configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and publish its configuration:

composer require bmatovu/oauth-negotiator
php artisan vendor:publish --provider="Bmatovu\OAuthNegotiator\OAuthNegotiatorServiceProvider"

As of v2.1.0 the package supports PHP ^8.0, so it will install cleanly on modern PHP runtimes. Check composer.json for the Laravel/illuminate/* constraint before you commit — PHP 8 is now allowed, but that says nothing about which Laravel majors are permitted.

Because the package is small and the README is the primary source of truth, read these first, in order:

  1. config/oauth-negotiator.php (after publishing) — this is where grant types, token URLs, client IDs/secrets, scopes, and cache settings live.
  2. The service provider — it tells you which classes are bound in the container and whether anything is aliased or facaded.
  3. The Negotiator/token-manager class — the public methods here are your day-to-day API (requestToken, refreshToken, getToken, or similar).

The first real use case is the classic client-credentials flow: your Laravel app needs to call a third-party API (payments, shipping, CRM, internal microservice) that issues short-lived bearer tokens. Instead of hand-rolling Http::post($tokenUrl, [...]) in every service class, you configure the client once and let the package fetch, cache, and refresh the token for you.

Minimal happy path:

// config/oauth-negotiator.php
'clients' => [
    'acme' => [
        'grant_type'    => 'client_credentials',
        'token_url'     => env('ACME_TOKEN_URL'),
        'client_id'     => env('ACME_CLIENT_ID'),
        'client_secret' => env('ACME_CLIENT_SECRET'),
        'scopes'        => ['orders.read', 'orders.write'],
    ],
],

Then resolve the client and pull a token before making your API call. Verify with php artisan tinker that a token comes back and that a second call hits the cache rather than the token endpoint.

Implementation Patterns

Wrap it in a dedicated API client. Don't sprinkle negotiator calls through controllers. Create one class per upstream API that owns the token retrieval and the subsequent Http::withToken(...) call:

class AcmeClient
{
    public function __construct(private Negotiator $oauth) {}

    public function get(string $path, array $query = [])
    {
        $token = $this->oauth->client('acme')->getToken();

        return Http::withToken($token)->baseUrl(config('services.acme.base_url'))
            ->get($path, $query)
            ->throw();
    }
}

This gives you one place to swap in retries, logging, or a different auth mechanism later.

Use it in jobs and commands, not just HTTP requests. Token caching makes this cheap — a queued job that fans out 500 calls to the upstream API should fetch the token once and reuse it across the whole batch. Resolve the client once at the top of handle() rather than per-iteration.

Compose with Laravel's HTTP client. The cleanest integration is to build a macro or a PendingRequest factory that already has the token attached. This keeps call sites idiomatic and lets you keep using Http::fake() in tests.

Multiple clients / tenants. If the package supports a named-client registry (it should, given the config shape), register one entry per upstream. For multi-tenant apps where each tenant has its own credentials, keep per-tenant credentials in the database and pass them into the negotiator at runtime rather than relying solely on static config — verify the package allows overriding credentials per call.

Pre-warm tokens in a scheduled command. If cold-start latency matters (e.g., a webhook handler that immediately calls upstream), add a scheduled job that requests each token shortly before expiry so user-facing requests never pay the token round-trip.

Testing. Bind a fake negotiator in your test container that returns a fixed token string, or point the token URL at a Http::fake() route. Assert on your API client's behavior, not on the negotiator's internals.

Gotchas and Tips

  • Maintenance risk is still the headline gotcha — but v2.1.0 is a good sign. The release is a one-line constraint widening (php: ^8.0), which removes the PHP-version blocker that previously forced --ignore-platform-reqs or a fork on PHP 8 projects. It does not tell you anything about Laravel support: still confirm the illuminate/support (or laravel/framework) constraint against your app's major before adopting. Three stars and zero dependents still means you're effectively the maintainer if something breaks — for a package this small, vendoring the two or three relevant classes into app/Support/ remains a sane hedge.
  • Re-run your install matrix after the bump. If you previously pinned an older negotiator version because of PHP constraints, upgrading is now low-risk; run composer update bmatovu/oauth-negotiator on a branch and diff composer.lock to confirm no transitive surprises.
  • Clock skew and expiry margins. Token endpoints report expires_in; if the package caches for the full duration, requests racing the expiry will 401. Confirm there's an expiry skew/buffer (typically 30–60s) and add one yourself if not.
  • Cache store matters. Tokens cached in the array store vanish per request; tokens cached in a shared Redis store can collide across staging/production if you use the same Redis. Namespace your cache keys by environment and client name.
  • Concurrency / thundering herd. With many workers and an expired token, every worker may hit the token endpoint simultaneously. Use Cache::lock() around the refresh if the package doesn't already, and treat upstream 429s from the token endpoint as a signal you need it.
  • Handle 401 by invalidating, not retrying blindly. Wrap upstream calls so a 401 triggers forget/refresh on the cached token and one retry — no more. Infinite retry loops against a misconfigured client are the classic failure mode.
  • Secrets belong in env, not config. Publish the config but keep client_secret as env('...'). Watch out for config:cache — if the package reads env() at runtime instead of config time, cached config will silently break credentials. On PHP 8.0+ this fails just as quietly, so verify with php artisan config:cache locally before deploying.
  • Debugging: when a token request fails, log the response body, not just the status — OAuth error responses (invalid_client, invalid_scope, unauthorized_client) are the actual diagnosis. Also verify grant_type casing and that scopes are space-delimited in the request body even if your config uses an array. On PHP 8, TypeError/ErrorException from untyped internals is a new failure mode worth catching around the token call.
  • Extension points to look for: a bindable cache repository, a configurable HTTP client/timeouts, and an interface you can implement for a custom negotiator. If the package hardcodes Guzzle or the cache facade, fork it — those are the two things you'll inevitably need to change.
  • Timeout your token calls. The default HTTP timeout can be long enough to hang a request worker. Set an explicit short timeout (2–5s) on the token endpoint specifically.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect