150 lines
4.7 KiB
PHP
150 lines
4.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Enums\GoogleService;
|
|
use App\Models\GoogleOAuthConnection;
|
|
use Google\Client;
|
|
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class GoogleOAuthService
|
|
{
|
|
private const TOKEN_URI = 'https://oauth2.googleapis.com/token';
|
|
|
|
private static function getRedirectUri(): string
|
|
{
|
|
$subfolder = config('app.url');
|
|
return rtrim($subfolder, '/') . '/auth/google/callback';
|
|
}
|
|
|
|
public function getAuthorizationUrl(GoogleService $service): string
|
|
{
|
|
$client = $this->createClient($service);
|
|
$client->setRedirectUri(self::getRedirectUri());
|
|
$client->setState($service->value);
|
|
$client->setAccessType('offline');
|
|
$client->setPrompt('consent');
|
|
$client->setIncludeGrantedScopes(true);
|
|
|
|
return $client->createAuthUrl();
|
|
}
|
|
|
|
public function handleCallback(string $code, GoogleService $service): GoogleOAuthConnection
|
|
{
|
|
$client = $this->createClient($service);
|
|
$client->setRedirectUri(self::getRedirectUri());
|
|
|
|
$token = $client->fetchAccessTokenWithAuthCode($code);
|
|
|
|
if (isset($token['error'])) {
|
|
throw new \RuntimeException('Google OAuth error: ' . ($token['error_description'] ?? $token['error']));
|
|
}
|
|
|
|
if (!isset($token['refresh_token'])) {
|
|
throw new \RuntimeException('No refresh token returned. Ensure access_type=offline and prompt=consent are set.');
|
|
}
|
|
|
|
$clientId = $client->getClientId();
|
|
$clientSecret = $client->getClientSecret();
|
|
|
|
return GoogleOAuthConnection::updateOrCreate(
|
|
['service' => $service->value],
|
|
[
|
|
'client_id' => $clientId,
|
|
'client_secret' => $clientSecret,
|
|
'refresh_token' => $token['refresh_token'],
|
|
]
|
|
);
|
|
}
|
|
|
|
public function revoke(GoogleService $service): void
|
|
{
|
|
$connection = $this->getConnection($service);
|
|
if (!$connection) {
|
|
return;
|
|
}
|
|
|
|
$client = $this->createClient($service);
|
|
$client->revokeToken();
|
|
$connection->delete();
|
|
}
|
|
|
|
public function getAccessToken(GoogleService $service): ?string
|
|
{
|
|
$connection = $this->getConnection($service);
|
|
if (!$connection) {
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
$client = $this->createClient($service);
|
|
$client->setAccessToken([
|
|
'access_token' => '',
|
|
'refresh_token' => $connection->refresh_token,
|
|
'client_id' => $connection->client_id,
|
|
'client_secret' => $connection->client_secret,
|
|
]);
|
|
|
|
if ($client->isAccessTokenExpired()) {
|
|
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
|
|
$newToken = $client->getAccessToken();
|
|
if (isset($newToken['refresh_token'])) {
|
|
$connection->refresh_token = $newToken['refresh_token'];
|
|
$connection->save();
|
|
}
|
|
}
|
|
|
|
$accessToken = $client->getAccessToken();
|
|
return $accessToken['access_token'] ?? null;
|
|
} catch (\Exception $e) {
|
|
Log::error('Google OAuth token refresh failed for ' . $service->value . ': ' . $e->getMessage());
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public function isAuthorized(GoogleService $service): bool
|
|
{
|
|
return $this->getConnection($service) !== null;
|
|
}
|
|
|
|
public function getConnection(GoogleService $service): ?GoogleOAuthConnection
|
|
{
|
|
return GoogleOAuthConnection::where('service', $service->value)->first();
|
|
}
|
|
|
|
public function buildClient(GoogleService $service): Client
|
|
{
|
|
$client = $this->createClient($service);
|
|
$connection = $this->getConnection($service);
|
|
|
|
if ($connection) {
|
|
$client->setAccessToken([
|
|
'access_token' => '',
|
|
'refresh_token' => $connection->refresh_token,
|
|
'client_id' => $connection->client_id,
|
|
'client_secret' => $connection->client_secret,
|
|
]);
|
|
|
|
if ($client->isAccessTokenExpired()) {
|
|
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
|
|
}
|
|
}
|
|
|
|
return $client;
|
|
}
|
|
|
|
private function createClient(GoogleService $service): Client
|
|
{
|
|
$client = new Client();
|
|
$client->setApplicationName(config('app.name'));
|
|
$client->setScopes([$service->scope()]);
|
|
$client->setClientId(config('services.google.client_id'));
|
|
$client->setClientSecret(config('services.google.client_secret'));
|
|
$client->setRedirectUri(self::getRedirectUri());
|
|
|
|
return $client;
|
|
}
|
|
} |