PHP
OAuth 2.0 Client Credentials Grant for API Access
Learn to securely obtain an access token using the OAuth 2.0 Client Credentials grant type for server-to-server API authentication with Guzzle HTTP client in PHP.
<?php
require 'vendor/autoload.php'; // Assuming you use Composer to manage dependencies (e.g., guzzlehttp/guzzle)
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
class OAuthClientCredentials
{
private $tokenUrl;
private $clientId;
private $clientSecret;
private $scope;
private $httpClient;
private $accessToken = null;
private $expiresAt = 0;
public function __construct(string $tokenUrl, string $clientId, string $clientSecret, string $scope = '')
{
$this->tokenUrl = $tokenUrl;
$this->clientId = $clientId;
$this->clientSecret = $clientSecret;
$this->scope = $scope;
$this->httpClient = new Client();
}
/**
* Obtains an access token using the Client Credentials Grant.
* Caches the token and refreshes if expired.
*
* @return string The access token.
* @throws GuzzleException If token request fails.
* @throws \Exception If token data is invalid.
*/
public function getAccessToken(): string
{
if ($this->accessToken && $this->expiresAt > time() + 60) { // Refresh 60 seconds before actual expiry
return $this->accessToken;
}
try {
$response = $this->httpClient->post($this->tokenUrl, [
'form_params' => [
'grant_type' => 'client_credentials',
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'scope' => $this->scope,
],
'headers' => [
'Accept' => 'application/json',
],
]);
$data = json_decode($response->getBody()->getContents(), true);
if (!isset($data['access_token']) || !isset($data['expires_in'])) {
throw new \Exception('Invalid token response from server.');
}
$this->accessToken = $data['access_token'];
$this->expiresAt = time() + $data['expires_in'];
return $this->accessToken;
} catch (GuzzleException $e) {
error_log("Failed to obtain OAuth access token: " . $e->getMessage());
throw $e;
}
}
/**
* Make an authenticated API request.
*
* @param string $method HTTP method (GET, POST, etc.)
* @param string $url The full URL for the API request.
* @param array $options Guzzle request options.
* @return \Psr\Http\Message\ResponseInterface
* @throws GuzzleException
*/
public function makeAuthenticatedRequest(string $method, string $url, array $options = []): \Psr\Http\Message\ResponseInterface
{
$token = $this->getAccessToken();
$options['headers']['Authorization'] = 'Bearer ' . $token;
return $this->httpClient->request($method, $url, $options);
}
}
// Example Usage:
// $tokenUrl = 'https://auth.example.com/oauth/token';
// $clientId = 'your_client_id';
// $clientSecret = 'your_client_secret';
// $scope = 'read write'; // Optional scopes
// try {
// $oauthClient = new OAuthClientCredentials($tokenUrl, $clientId, $clientSecret, $scope);
// // Get an access token
// // $accessToken = $oauthClient->getAccessToken();
// // echo "Access Token: " . $accessToken . "
";
// // Make an authenticated API request
// $apiResponse = $oauthClient->makeAuthenticatedRequest('GET', 'https://api.example.com/data');
// echo "API Response: " . $apiResponse->getBody()->getContents() . "
";
// } catch (GuzzleException $e) {
// echo "API or OAuth error: " . $e->getMessage() . "
";
// } catch (\Exception $e) {
// echo "General error: " . $e->getMessage() . "
";
// }
How it works: This PHP snippet demonstrates how to implement the OAuth 2.0 Client Credentials grant flow using Guzzle HTTP client. It encapsulates the logic for requesting an access token from an authorization server using a client ID and secret. The token is cached and automatically refreshed before expiry, allowing subsequent API requests to be made with a valid `Bearer` token in the `Authorization` header, suitable for server-to-server communication.