edspc/oauth-http-client-bundle
## Getting Started
### Minimal Setup
1. **Install the Bundle**
```bash
composer require edspc/oauth-http-client-bundle
Register the bundle in config/bundles.php:
return [
// ...
Edspc\OauthHttpClientBundle\EdspcOauthHttpClientBundle::class => ['all' => true],
];
Configure OAuth Providers
Update config/packages/edspc_oauth_http_client.yaml with your OAuth provider details (e.g., Zoho):
edspc_oauth_http_client:
default_auth: zoho
auth:
zoho:
token_url: 'https://accounts.zoho.com/oauth/v2/token'
client_id: '%env(ZOHO_CLIENT_ID)%'
client_secret: '%env(ZOHO_CLIENT_SECRET)%'
Define HTTP Services Configure base URIs for API endpoints in the same file:
http_services:
desk_client:
base_uri: 'https://desk.zoho.com/'
crm_client:
base_uri: 'https://www.zohoapis.com/crm/v2/'
First Use Case: Fetching an Access Token
Inject the OAuthHttpClient service and call getAccessToken():
use Edspc\OauthHttpClientBundle\Service\OAuthHttpClient;
public function __construct(private OAuthHttpClient $oauthClient) {}
public function getToken()
{
$token = $this->oauthClient->getAccessToken('zoho', [
'code' => $authorizationCodeFromUser,
'grant_type' => 'authorization_code',
'redirect_uri' => 'https://your-app.com/callback',
]);
return $token;
}
Make an Authenticated API Request
Use the createClient() method to generate a Guzzle client with the token:
$client = $this->oauthClient->createClient('desk_client', $token);
$response = $client->get('/api/tickets');
Redirect to OAuth Provider
Generate an authorization URL using getAuthorizationUrl():
$url = $this->oauthClient->getAuthorizationUrl('zoho', [
'response_type' => 'code',
'scope' => 'ZohoDesk.tickets.ALL',
'redirect_uri' => 'https://your-app.com/callback',
]);
Handle Callback Exchange the authorization code for a token in your callback controller:
public function callback(Request $request)
{
$token = $this->oauthClient->getAccessToken('zoho', [
'code' => $request->query('code'),
'grant_type' => 'authorization_code',
'redirect_uri' => 'https://your-app.com/callback',
]);
// Store token (e.g., in session or database)
}
Reuse Token for API Calls Attach the token to requests via the generated client:
$client = $this->oauthClient->createClient('crm_client', $token);
$response = $client->post('/Contacts', ['json' => ['data' => [...] ]]);
Token Storage: Store tokens in the database or cache (e.g., Redis) with a TokenRepository interface for persistence.
interface TokenRepository {
public function findByProviderAndUser(string $provider, User $user);
public function save(Token $token);
}
Refresh Tokens: Implement token refresh logic using getRefreshedAccessToken():
$refreshedToken = $this->oauthClient->getRefreshedAccessToken('zoho', $expiredToken);
Middleware for API Calls Create a middleware to inject the token into requests:
public function handle(Request $request, Closure $next)
{
$token = $this->tokenRepository->findByProviderAndUser('zoho', auth()->user());
$client = $this->oauthClient->createClient('desk_client', $token);
$request->setClient($client);
return $next($request);
}
Dynamic Configuration
Override the default config per environment (e.g., config/packages/dev/edspc_oauth_http_client.yaml).
Dependency Injection
Inject OAuthHttpClient directly into services or controllers:
public function __construct(OAuthHttpClient $oauthClient) {}
Named Clients
Use named HTTP services (e.g., desk_client, crm_client) to manage multiple API endpoints:
$deskClient = $this->oauthClient->createClient('desk_client', $token);
$crmClient = $this->oauthClient->createClient('crm_client', $token);
Custom Headers Pass additional headers when creating a client:
$client = $this->oauthClient->createClient('crm_client', $token, [
'headers' => ['X-Custom-Header' => 'value'],
]);
Token Expiry Handling
try {
$response = $client->get('/endpoint');
} catch (ServerException $e) {
if ($e->getCode() === 401) {
$token = $this->oauthClient->getRefreshedAccessToken('zoho', $token);
$client = $this->oauthClient->createClient('desk_client', $token);
return $client->get('/endpoint');
}
throw $e;
}
Redirect URI Mismatch
redirect_uri doesn’t match the registered one.redirect_uri in getAuthorizationUrl() matches the one configured in the OAuth provider dashboard.Scope Validation
$url = $this->oauthClient->getAuthorizationUrl('zoho', [
'scope' => 'ZohoDesk.tickets.ALL ZohoCRM.contacts.READ',
]);
Environment Variables
%env() for sensitive data (e.g., client_id, client_secret).Enable Guzzle Debugging Add debug middleware to inspect requests/responses:
$client = $this->oauthClient->createClient('desk_client', $token, [
'handler' => HandlerStack::create([
new \GuzzleHttp\Middleware::tap(function ($request) {
\Log::debug('Request:', [
'url' => (string) $request->getUri(),
'method' => $request->getMethod(),
'headers' => $request->getHeaders(),
'body' => $request->getBody(),
]);
}),
new \GuzzleHttp\Middleware::tap(function ($response) {
\Log::debug('Response:', [
'status' => $response->getStatusCode(),
'body' => (string) $response->getBody(),
]);
}),
]),
]);
Token Validation Verify token structure before use:
$token = $this->oauthClient->getAccessToken(...);
if (!isset($token['access_token']) || empty($token['access_token'])) {
throw new \RuntimeException('Failed to obtain access token');
}
Custom Token Storage
Extend the bundle by implementing a TokenRepository interface:
class DatabaseTokenRepository implements TokenRepository {
public function findByProviderAndUser(string $provider, User $user) {
// Custom logic to fetch token from DB
}
public function save(Token $token) {
// Custom logic to save token
}
}
Bind it in services.yaml:
services:
App\Service\DatabaseTokenRepository:
tags: ['edspc_oauth_http_client.token_repository']
Add New OAuth Providers
Dynamically register providers by extending the OAuthProvider class or using the addProvider() method (if available):
$this->oauthClient->addProvider('google', [
'token_url' => 'https://oauth2.googleapis.com/token',
'client_id' => '%env(GOOGLE_CLIENT_ID)%',
]);
Custom HTTP Client Configuration Override the default Guzzle client configuration:
How can I help you explore Laravel packages today?