Files
php_assessment_1/lib/google/oauth2.php
T

208 lines
6.2 KiB
PHP
Raw Normal View History

2025-02-04 23:06:08 +01:00
<?php
namespace Lib\Google;
class GoogleOAuth2 {
private string $clientId;
private string $clientSecret;
private string $redirectUri;
private array $scopes = [];
private ?string $accessToken = null;
private ?string $refreshToken = null;
private ?int $tokenExpires = null;
private const AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
private const TOKEN_URL = 'https://oauth2.googleapis.com/token';
public function __construct(string $clientId, string $clientSecret, string $redirectUri) {
$this->clientId = $clientId;
$this->clientSecret = $clientSecret;
$this->redirectUri = $redirectUri;
}
/**
* Add scopes for the OAuth2 request
* @param array $scopes Array of Google OAuth2 scopes
* @return self
*/
public function addScopes(array $scopes): self {
$this->scopes = array_merge($this->scopes, $scopes);
return $this;
}
/**
* Generate the authorization URL
* @param array $additionalParams Additional URL parameters
* @return string
*/
public function getAuthorizationUrl(array $additionalParams = []): string {
$params = array_merge([
'client_id' => $this->clientId,
'redirect_uri' => $this->redirectUri,
'response_type' => 'code',
'access_type' => 'offline',
'prompt' => 'consent',
'scope' => implode(' ', array_unique($this->scopes))
], $additionalParams);
return self::AUTH_URL . '?' . http_build_query($params);
}
/**
* Exchange authorization code for tokens
* @param string $code Authorization code
* @return array Token response
* @throws \Exception
*/
public function exchangeCode(string $code): array {
$response = $this->makeTokenRequest([
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $this->redirectUri
]);
$this->setTokens($response);
return $response;
}
/**
* Refresh the access token
* @throws \Exception
*/
public function refreshAccessToken(): array {
if (!$this->refreshToken) {
throw new \Exception('No refresh token available');
}
$response = $this->makeTokenRequest([
'grant_type' => 'refresh_token',
'refresh_token' => $this->refreshToken
]);
$this->setTokens($response);
return $response;
}
/**
* Make an authenticated request to Google APIs
* @param string $url
* @param string $method
* @param array $headers
* @param mixed $body
* @param bool $rawResponse Whether to return raw response instead of JSON
* @return array|string
* @throws \Exception
*/
public function makeAuthenticatedRequest(
string $url,
string $method = 'GET',
array $headers = [],
$body = null,
bool $rawResponse = false
){
if ($this->isTokenExpired()) {
$this->refreshAccessToken();
}
$headers = array_merge([
'Authorization: Bearer ' . $this->accessToken,
'Accept: application/json'
], $headers);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_CUSTOMREQUEST => $method
]);
if ($body && in_array($method, ['POST', 'PUT', 'PATCH'])) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
}
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode >= 400) {
throw new \Exception('Request failed with status ' . $httpCode . ': ' . $response);
}
return $rawResponse ? $response : json_decode($response, true);
}
/**
* Set tokens from OAuth2 response
* @param array $response
*/
private function setTokens(array $response): void {
$this->accessToken = $response['access_token'];
if (isset($response['refresh_token'])) {
$this->refreshToken = $response['refresh_token'];
}
$this->tokenExpires = time() + ($response['expires_in'] ?? 3600);
}
/**
* Check if the current access token is expired
* @return bool
*/
private function isTokenExpired(): bool {
return !$this->accessToken || !$this->tokenExpires || time() >= $this->tokenExpires;
}
/**
* Make a token request to Google's OAuth2 server
* @param array $params
* @return array
* @throws \Exception
*/
private function makeTokenRequest(array $params): array {
$params = array_merge([
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret
], $params);
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => self::TOKEN_URL,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($params),
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded']
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new \Exception('Token request failed with status ' . $httpCode . ': ' . $response);
}
return json_decode($response, true);
}
// Getters for tokens and expiration
public function getAccessToken(): ?string {
return $this->accessToken;
}
public function setAccessToken(string $accessToken): void {
$this->accessToken = $accessToken;
}
public function getRefreshToken(): ?string {
return $this->refreshToken;
}
public function setRefreshToken(string $refreshToken): void {
$this->refreshToken = $refreshToken;
}
public function getTokenExpires(): ?int {
return $this->tokenExpires;
}
}