guzzlehttp/oauth-subscriber
Guzzle middleware for OAuth 1.0 request signing (consumer key/secret + token/secret) compatible with Guzzle 7.11+ and PHP 7.2.5+. Add to a HandlerStack, set auth=oauth, and optionally override token credentials per request.
To integrate guzzlehttp/oauth-subscriber into a Laravel application, start by installing the package via Composer:
composer require guzzlehttp/oauth-subscriber
For a basic OAuth 1.0 API call (e.g., Twitter v1.1), configure the middleware in a service class or directly in your HTTP client:
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Subscriber\Oauth\Oauth1;
class TwitterService
{
public function __construct()
{
$stack = HandlerStack::create();
$stack->push(new Oauth1([
'consumer_key' => env('TWITTER_CONSUMER_KEY'),
'consumer_secret' => env('TWITTER_CONSUMER_SECRET'),
'token' => env('TWITTER_TOKEN'),
'token_secret' => env('TWITTER_TOKEN_SECRET'),
]));
$this->client = new Client([
'base_uri' => 'https://api.twitter.com/1.1/',
'handler' => $stack,
]);
}
public function fetchHomeTimeline()
{
return $this->client->get('statuses/home_timeline.json', ['auth' => 'oauth']);
}
}
For Laravel’s built-in HTTP client, use the withOptions method to inject the middleware:
use Illuminate\Support\Facades\Http;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Subscriber\Oauth\Oauth1;
$stack = HandlerStack::create();
$stack->push(new Oauth1([
'consumer_key' => env('API_CONSUMER_KEY'),
'consumer_secret' => env('API_CONSUMER_SECRET'),
'token' => env('API_TOKEN'),
'token_secret' => env('API_TOKEN_SECRET'),
]));
$response = Http::withOptions([
'handler' => $stack,
])->get('https://api.example.com/endpoint', ['auth' => 'oauth']);
Leverage Laravel’s service container to bind the OAuth subscriber for reuse across services:
// In AppServiceProvider@boot()
$this->app->singleton(Oauth1::class, function ($app) {
return new Oauth1([
'consumer_key' => env('CONSUMER_KEY'),
'consumer_secret' => env('CONSUMER_SECRET'),
'token' => env('TOKEN'),
'token_secret' => env('TOKEN_SECRET'),
]);
});
// In a service class
public function __construct(Oauth1 $oauthSubscriber)
{
$stack = HandlerStack::create();
$stack->push($oauthSubscriber);
$this->client = new Client(['handler' => $stack]);
}
Override credentials per request for multi-tenant or multi-service scenarios:
$response = $this->client->get('endpoint', [
'auth' => 'oauth',
'oauth' => [
'token' => 'tenant_specific_token',
'token_secret' => 'tenant_specific_token_secret',
],
]);
For APIs requiring RSA-SHA1 (e.g., some payment gateways):
$stack->push(new Oauth1([
'consumer_key' => env('CONSUMER_KEY'),
'consumer_secret' => env('CONSUMER_SECRET'),
'private_key_file' => storage_path('app/private_key.pem'),
'private_key_passphrase' => env('PRIVATE_KEY_PASSPHRASE'),
'signature_method' => Oauth1::SIGNATURE_METHOD_RSA,
]));
Set auth => 'oauth' globally in the client for all requests:
$client = new Client([
'handler' => $stack,
'auth' => 'oauth', // Enables OAuth signing for all requests
]);
Ensure retries maintain OAuth signing by placing the OAuth subscriber before retry middleware:
$stack = HandlerStack::create();
$stack->push(new Oauth1([...])); // OAuth must run first
$stack->push(new RetryMiddleware([...]));
Nonce Entropy Fix (v0.8.1)
RSA-SHA1 Key Validation
Duplicate Query Parameters
PHP 8.5 Non-Finite Float Warnings
Secret Exposure in Logs
oauth or auth keys, as they may include secrets. Use Laravel’s tap() or custom logging filters.Two-Legged OAuth Misconfiguration
token and token_secret to empty strings (''). Omitting them entirely may cause unexpected behavior.Validate Headers
Use httpbin.org to inspect the Authorization header:
$response = $client->get('https://httpbin.org/headers', ['auth' => 'oauth']);
dd($response->json()['headers']['Authorization']);
Check Signature Method
Ensure the signature_method matches the API’s requirements (default is HMAC-SHA1). For RSA, explicitly set:
'signature_method' => Oauth1::SIGNATURE_METHOD_RSA,
Test with Postman/cURL
Manually replicate the request in Postman or cURL to verify the Authorization header matches expectations.
Case Sensitivity
OAuth parameters are case-sensitive. Ensure oauth_signature_method, oauth_consumer_key, etc., match the API’s expectations.
Parameter Ordering The package normalizes parameters, but some APIs are sensitive to the order of parameters in the signature base string. Test thoroughly.
Empty Values The package signs bare query/form parameters as empty values (since v0.9.0). If your API expects otherwise, adjust your request formatting.
Custom Nonce Generator Override the nonce generation for specialized use cases:
$oauth = new Oauth1([...]);
$oauth->setNonceGenerator(function () {
return bin2hex(random_bytes(16));
});
Timestamp Precision Adjust timestamp precision for APIs requiring specific formats:
$oauth = new Oauth1([...]);
$oauth->setTimestampFormat('Ymd\THis\Z'); // ISO 8601
Middleware Order Place the OAuth subscriber before any middleware that modifies request data (e.g., query parameters, headers) to ensure proper signing.
Laravel Events
Listen for Illuminate\HttpClient\Events\Request to dynamically modify OAuth settings:
use Illuminate\HttpClient\Events\Request;
Event::listen(Request::class, function (Request $event) {
if ($event->url() === 'special-endpoint') {
$event->request->setOption('oauth', [
'token' => 'special_token',
'token_secret' => 'special_token_secret',
]);
}
});
RSA-SHA1 Overhead RSA signing is computationally expensive. Cache the signed request if possible or consider HMAC-SHA256 for better performance:
'signature_method' => Oauth1::SIGNATURE_METHOD_HMAC_SHA256,
Middleware Stack Size Keep the middleware stack lean. Each subscriber adds overhead, especially for high-throughput APIs.
Store Secrets Securely
Use Laravel’s .env or a secrets manager (e.g., AWS Secrets Manager, HashiCorp Vault) for credentials.
Rotate Credentials Implement a rotation strategy for tokens/secrets, especially for long-lived applications.
Validate Responses
Check for OAuth-specific error responses (e.g., oauth_problem in Twitter’s API) and handle them gracefully.
Avoid Hardcoding Never hardcode credentials in your codebase. Always use environment variables or secure storage.
How can I help you explore Laravel packages today?