Weave Code
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

Http Oauth Laravel Package

dormilich/http-oauth

PSR-compatible OAuth2 Client Credentials extension for dormilich/http-client. Automatically fetches and caches access tokens via a token client/provider, then adds Authorization headers to outgoing requests. Works with PSR-18/17 HTTP clients and PSR-16 cache.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup for Laravel

  1. Install the package:
    composer require dormilich/http-oauth dormilich/http-client
    
  2. Register PSR-18 HTTP client (e.g., guzzlehttp/guzzle):
    composer require guzzlehttp/guzzle
    
  3. Register PSR-16 cache (e.g., symfony/cache):
    composer require symfony/cache
    
  4. Configure OAuth credentials in config/services.php:
    'oauth' => [
        'client_id' => env('OAUTH_CLIENT_ID'),
        'client_secret' => env('OAUTH_CLIENT_SECRET'),
        'token_url' => env('OAUTH_TOKEN_URL'),
    ],
    

First Use Case: Protected API Request

use Dormilich\HttpClient\Client;
use Dormilich\HttpOauth\TokenClient;
use Dormilich\HttpOauth\TokenProvider;
use Dormilich\HttpOauth\Credentials\ClientCredentials;
use Dormilich\HttpOauth\Credentials\DefaultProvider;
use Dormilich\HttpOauth\Encoder\AuthorisationEncoder;
use Symfony\Component\HttpClient\Psr18Client;
use Symfony\Component\Cache\Simple\FilesystemCache;
use Symfony\Contracts\HttpClient\HttpClientInterface;

// Laravel Service Provider
public function register()
{
    $this->app->singleton(HttpClientInterface::class, function ($app) {
        $httpClient = new Psr18Client();
        $requestFactory = new \Nyholm\Psr7\Factory\Psr17Factory();
        $streamFactory = new \Nyholm\Psr7\Factory\Psr17Factory();

        $cache = new FilesystemCache();
        $credentials = new ClientCredentials(
            config('services.oauth.client_id'),
            config('services.oauth.client_secret'),
            config('services.oauth.token_url')
        );

        $provider = new DefaultProvider($credentials);
        $tokenClient = new TokenClient($provider, $httpClient, $requestFactory, $streamFactory);
        $tokenProvider = new TokenProvider($tokenClient, $cache);
        $authorisation = new AuthorisationEncoder($tokenProvider);

        $client = new Client($httpClient, $requestFactory, $streamFactory);
        $client->addEncoder($authorisation);

        return $client;
    });
}

First API Call

use Illuminate\Support\Facades\Http;

$response = Http::client()->get('https://api.example.com/protected-endpoint');

Implementation Patterns

1. Multi-API Integration

Use ChainProvider to manage credentials for multiple OAuth providers:

$provider1 = new DefaultProvider(new ClientCredentials(
    'id1', 'secret1', 'https://provider1.com/oauth/token'
));
$provider2 = new DomainProvider();
$provider2->add(new ClientCredentials(
    'id2', 'secret2', 'https://provider2.com/oauth/token'
), ['api.example.com']);

$chain = new ChainProvider([$provider1, $provider2]);
$tokenClient = new TokenClient($chain, $httpClient, $requestFactory, $streamFactory);

2. Token Refresh Handling

Leverage automatic token refresh on 401/403 responses:

// Configure the TokenProvider to handle stale tokens
$tokenProvider = new TokenProvider($tokenClient, $cache, 300); // 5-minute cache TTL

3. Middleware Integration

Wrap the OAuth client in Laravel middleware for global protection:

// app/Http/Middleware/OAuthMiddleware.php
public function handle($request, Closure $next)
{
    $response = $next($request);
    if ($response->getStatusCode() === 401) {
        // Trigger token refresh via the client
        $this->app->make(\Dormilich\HttpOauth\TokenProvider::class)->refresh();
        return $next($request);
    }
    return $response;
}

4. Dynamic Credential Switching

Use DomainProvider for environment-specific OAuth flows:

$domainProvider = new DomainProvider();
$domainProvider->add($stagingCredentials, ['staging.example.com']);
$domainProvider->add($productionCredentials, ['api.example.com']);

5. Testing with Mock Tokens

Override the TokenProvider in tests:

// tests/TestCase.php
protected function getMockTokenProvider()
{
    $mockToken = [
        'access_token' => 'mock_token_123',
        'expires_in' => 3600,
    ];
    $cache = new \Symfony\Component\Cache\Adapter\ArrayAdapter();
    $cache->set('oauth_token', $mockToken, new \DateInterval('PT1H'));

    return new \Dormilich\HttpOauth\TokenProvider(
        $this->createMock(TokenClient::class),
        $cache
    );
}

Gotchas and Tips

1. Token Expiry Quirks

  • No expires_in in Response: If the OAuth provider omits expires_in, the package assumes the token is non-expiring but may still fail silently. Solution: Configure a default TTL in TokenProvider:
    $tokenProvider = new TokenProvider($tokenClient, $cache, 300); // Force 5-minute TTL
    
  • Server-Side Token Validation: Some APIs validate tokens server-side. If you encounter 403 Forbidden after refresh, check if the token format (e.g., Bearer <token>) matches the API’s expectations.

2. Cache Pitfalls

  • Stale Tokens: If the cache persists indefinitely, stale tokens may cause unexpected 401s. Solution: Set a reasonable TTL (e.g., 300 seconds) or implement a cache invalidation hook.
  • Cache Provider: Ensure your PSR-16 cache supports tagging if you need to invalidate tokens programmatically:
    $cache->delete('oauth_token_*'); // Wildcard deletion (if supported)
    

3. Credential Provider Order

  • ChainProvider Behavior: The first matching provider in the chain takes precedence. Tip: Place the most specific provider (e.g., DomainProvider) before the fallback (DefaultProvider).
  • No Match Found: If no provider matches, the request won’t include an Authorization header. Verify your domain patterns (e.g., api.example.com vs. *.example.com).

4. Debugging OAuth Flows

  • Enable HTTP Logging: Use Guzzle’s middleware to log OAuth requests:
    $httpClient = new Psr18Client([
        'handler' => HandlerStack::create([
            new \GuzzleHttp\Middleware::tap(function ($request) {
                \Log::debug('OAuth Request:', [
                    'url' => (string) $request->getUri(),
                    'headers' => $request->getHeaders(),
                ]);
            }),
        ]),
    ]);
    
  • Token Debugging: Dump the raw token response to check for issues:
    $tokenClient->getToken()->then(function ($response) {
        \Log::debug('Token Response:', $response->getBody());
    });
    

5. Extension Points

  • Custom Token Storage: Replace TokenProvider to use a database or Redis:
    class DatabaseTokenProvider implements TokenProviderInterface {
        public function getToken(): string { /* Query DB */ }
        public function refresh(): void { /* Update DB */ }
    }
    
  • Custom Encoder: Extend AuthorisationEncoder to modify the Authorization header format:
    class CustomAuthorisationEncoder extends AuthorisationEncoder {
        protected function getHeaderValue(): string {
            return 'Custom ' . parent::getHeaderValue();
        }
    }
    
  • Pre-Flight Requests: If the OAuth provider requires a pre-flight request (e.g., for CSRF), configure the TokenClient to handle redirects:
    $tokenClient = new TokenClient($provider, $httpClient, $requestFactory, $streamFactory, [
        'allow_redirects' => true,
    ]);
    

6. Laravel-Specific Tips

  • Service Container Binding: Bind the OAuth client as a singleton in a service provider:
    $this->app->singleton('oauth.client', function ($app) {
        return (new Client($app->make(HttpClientInterface::class), ...))->addEncoder(...);
    });
    
  • Environment Variables: Use Laravel’s .env for sensitive credentials:
    OAUTH_CLIENT_ID=your_client_id
    OAUTH_CLIENT_SECRET=your_secret
    OAUTH_TOKEN_URL=https://oauth.example.com/token
    
  • Rate Limiting: Combine with Laravel’s throttle middleware to avoid hitting API rate limits:
    Route::middleware(['throttle:60,1'])->group(function () {
        // OAuth-protected routes
    });
    

7. Common Errors

Error Cause Solution
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.
besmartand-pro/php-quality-config
sentix/ai-chatbot
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity