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 Laravel Package

eher/oauth

OAuth 2.0 authentication for Laravel applications, adding token-based authorization, configurable grant flows, access and refresh token handling, and middleware to protect routes and authenticate API requests. Integrates with Eloquent models and the service container.

Deep Wiki
Context7

Getting Started

eher/oauth is not a Laravel package — it is a framework-agnostic PHP OAuth 1.0a client implementation with no service provider, no facade, and no config file. You drop it in and call it directly. Treat it as a library you wrap, not a package you configure.

Minimal steps

  1. Before writing any code, open the repository and find the LICENSE file. The Packagist metadata reports NOASSERTION, which means Composer could not identify a license. Do not ship this into a commercial codebase until a human has read the license text.
  2. Install and confirm it resolves against your PHP version (it is an older library; expect no ^8.x PHP constraint, so it may install on PHP 8 only because nothing declares otherwise):
composer require eher/oauth
  1. Where to look first, in this order:

    • src/Eher/OAuth/Consumer.php — the credential pair (key/secret).
    • src/Eher/OAuth/Token.php — request tokens, access tokens, and the empty token used during step 1.
    • src/Eher/OAuth/Request.php — the core class: fromConsumerAndToken(), sign(), toHeader(), toUrl(), toPostData().
    • src/Eher/OAuth/HmacSha1.php, RsaSha1.php, PlainText.php — the signature methods.
    • src/Eher/OAuth/Util.php — RFC 3986 encoding helpers; most signature bugs trace back here.
  2. Your first use case. The realistic one in a Laravel app is a single-provider integration (Xero, NetSuite, Tumblr, Upwork, Magento 1, legacy Twitter endpoints) where the provider has not migrated to OAuth 2. Sign one request end to end:

use Eher\OAuth\Consumer;
use Eher\OAuth\Token;
use Eher\OAuth\Request;
use Eher\OAuth\HmacSha1;

$consumer = new Consumer(config('oauth1.xero.key'), config('oauth1.xero.secret'));
$token    = new Token($connection->token, $connection->secret);

$request = Request::fromConsumerAndToken(
    $consumer,
    $token,
    'GET',
    'https://api.xero.com/api.xro/2.0/Contacts',
    ['page' => 1]
);

$request->sign(new HmacSha1(), $consumer, $token);

Http::withHeaders(['Authorization' => $request->toHeader()])->get($request->toUrl());

If that round-trips against a sandbox account, you understand the whole library. Everything else is plumbing.

Implementation Patterns

Wrap it in a single service class. Never let Consumer/Request objects leak into controllers. Bind one class per provider in the container:

final class OAuth1Signer
{
    public function __construct(private array $credentials) {}

    public function sign(string $method, string $url, array $params, ?Token $token = null): array
    {
        $consumer = new Consumer($this->credentials['key'], $this->credentials['secret']);
        $token ??= new Token();

        $request = Request::fromConsumerAndToken($consumer, $token, $method, $url, $params);
        $request->sign(new HmacSha1(), $consumer, $token);

        return [
            'url'     => $request->toUrl(),
            'headers' => ['Authorization' => $request->toHeader()],
            'body'    => $request->toPostData(),
        ];
    }
}

Config. One file, one array key per provider, all secrets via env(). Laravel's config:cache will freeze these, so never compute credentials at runtime inside the config file.

Three-legged flow is the main workflow and it is entirely manual:

// 1. Obtain a request token
$req = Request::fromConsumerAndToken($consumer, new Token(), 'POST', $requestTokenUrl, [
    'oauth_callback' => route('oauth1.callback'),
]);
$req->sign(new HmacSha1(), $consumer, new Token());
// POST to $requestTokenUrl with $req->toHeader() and $req->toPostData()

// 2. Redirect the user
return redirect($authorizeUrl . '?' . http_build_query(['oauth_token' => $requestToken]));

// 3. Exchange the verifier for an access token
$req = Request::fromConsumerAndToken($consumer, $requestTokenObj, 'POST', $accessTokenUrl, [
    'oauth_verifier' => $request->query('oauth_verifier'),
]);

Persist the request token secret between steps 1 and 2 — it is needed for step 3, and providers will reject the exchange without it.

Token storage. A dedicated oauth1_connections table (user_id, provider, token, secret, expires_at). Encrypt the secrets with Crypt::encryptString(); treat a leaked OAuth 1 token secret as equivalent to a leaked API password.

Guzzle / Http integration. The cleanest pattern is to sign first, then hand the signed pieces to the client and do not let the client add or reorder query parameters afterwards:

$signed = $signer->sign('POST', $url, $params, $token);

Http::withHeaders($signed['headers'])
    ->withBody($signed['body'], 'application/x-www-form-urlencoded')
    ->post($signed['url']);

Idempotency for tests. Wrap Request generation in a small factory that lets you inject a fixed nonce and timestamp. Deterministic signatures make HTTP-level tests against a mocked provider trivial; without this you cannot assert on the Authorization header.

Queue it. OAuth 1 providers are usually slow and rate-limited. Signing is cheap and stateless — build the request inside the job so a retry regenerates a fresh timestamp and nonce.

Gotchas and Tips

The base string is everything. When you get a 401, the problem is almost never the library — it is the base string. Dump the normalized method, URL, and parameter string and compare character by character. Providers historically shipped a signature debugger (Twitter's OAuth tool, Xero's signature tester); use it if it exists.

Double encoding. Pass decoded parameters to fromConsumerAndToken(). If you hand it a value that is already percent-encoded, Util will encode it again and the signature will not match. This is the single most common failure mode with this library.

Never mix $parameters with a pre-built query string. If the URL already contains ?count=5 and you also pass ['count' => 5], you sign the parameter twice. Pass a bare URL and let the library build the query.

Port and scheme normalization. https://example.com:443/path and https://example.com/path are different strings but the same signature base. Normalize URLs before signing.

Request bodies. For application/x-www-form-urlencoded, body parameters must be included in the signature. For multipart/form-data and application/json, they must not be. toPostData() only handles the former — for JSON bodies, sign the URL only and send the JSON payload separately.

Check what toHeader() actually returns in your version. In some builds it returns the header value; in others it includes the Authorization: prefix. Passing the wrong one to Http::withHeaders() yields a malformed header and an opaque rejection. Print it once and confirm.

Timestamp skew. oauth_timestamp comes from time(). A server drifted a few minutes will fail every request. Keep NTP running and never cache signed requests — regenerate on each retry.

Nonce uniqueness. The library generates one, but providers keep replay caches. If you are replaying a request from a queue, regenerate rather than reusing the signed artifact.

RSA needs a key and ext-openssl. RsaSha1 requires a PEM private key and is only used by providers that explicitly demand it. HmacSha1 covers the overwhelming majority; do not add RSA complexity speculatively.

License and maintenance are the real risk. 29 stars, uncertain license, no recent activity. Pin the exact version in composer.json, and vendor the source if it is load-bearing. Have thephpleague/oauth1-client bookmarked as the maintained escape hatch — the Consumer/Token/Request shape is similar enough that porting is a matter of hours, not days.

Extension points. If a provider does something non-standard, subclass Eher\OAuth\SignatureMethod and implement the interface rather than patching the vendor directory. Custom parameter normalization (e.g. a provider that does not sort parameters) is usually solved by overriding the method that builds the base string.

Do not enable strict Composer security auditing on this package. It will flag as unmaintained and potentially fail CI for a dependency that is functioning correctly. Suppress the specific advisory rather than disabling auditing globally.

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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package