Files
glastree/app/Services/GoogleOAuthService.php
T
2026-06-17 13:50:41 +02:00

179 lines
5.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\GoogleOAuthConnection;
use App\Enums\GoogleService;
use Google_Client;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
class GoogleOAuthService
{
private const TOKEN_CACHE_TTL = 300;
private const MAX_RETRIES = 2;
public function buildClient(): Google_Client
{
$client = new Google_Client();
$client->setClientId(config('services.google.client_id'));
$client->setClientSecret(config('services.google.client_secret'));
$client->setRedirectUri(route('google-oauth.callback'));
$client->setAccessType('offline');
$client->setPrompt('consent');
$client->setIncludeGrantedScopes(true);
return $client;
}
public function getAuthUrl(string $service): string
{
$serviceEnum = GoogleService::from($service);
$client = $this->buildClient();
$client->setScopes($serviceEnum->scopes());
$client->setState($service);
return $client->createAuthUrl();
}
public function handleCallback(string $authorizationCode, GoogleService $service): GoogleOAuthConnection
{
$client = $this->buildClient();
$client->setScopes($service->scopes());
$client->fetchAccessTokenWithAuthCode($authorizationCode);
$tokenData = $client->getAccessToken();
if (isset($tokenData['error'])) {
throw new \RuntimeException('Google OAuth error: ' . ($tokenData['error_description'] ?? $tokenData['error']));
}
$token = $tokenData['access_token'];
$refreshToken = $tokenData['refresh_token'] ?? null;
$expiresAt = now()->addSeconds($tokenData['expires_in']);
$email = $this->getEmailFromToken($token);
$connection = GoogleOAuthConnection::updateOrCreate([
'user_id' => auth()->id(),
'service' => $service->value,
'email' => $email,
], [
'access_token' => $token,
'refresh_token' => $refreshToken,
'expires_at' => $expiresAt,
]);
return $connection;
}
public function getAccessToken(GoogleOAuthConnection $connection): string
{
$cacheKey = "google_oauth_token_{$connection->id}";
return Cache::remember($cacheKey, self::TOKEN_CACHE_TTL, function () use ($connection) {
if ($connection->isExpired()) {
$this->refreshToken($connection);
}
return $connection->access_token;
});
}
public function refreshToken(GoogleOAuthConnection $connection): void
{
if (!$connection->refresh_token) {
throw new \RuntimeException('Impossibile rinnovare: nessun refresh token');
}
$client = $this->buildClient();
$client->fetchAccessTokenWithRefreshToken($connection->refresh_token);
$tokenData = $client->getAccessToken();
if (isset($tokenData['error'])) {
$connection->delete();
Cache::forget("google_oauth_token_{$connection->id}");
throw new \RuntimeException(
'Refresh token scaduto o revocato per ' . $connection->email
);
}
$connection->access_token = $tokenData['access_token'];
$connection->expires_at = now()->addSeconds($tokenData['expires_in']);
if (isset($tokenData['refresh_token'])) {
$connection->refresh_token = $tokenData['refresh_token'];
}
$connection->save();
Cache::forget("google_oauth_token_{$connection->id}");
}
public function revoke(GoogleOAuthConnection $connection): void
{
$client = $this->buildClient();
$client->revokeToken($connection->access_token);
$connection->delete();
Cache::forget("google_oauth_token_{$connection->id}");
}
public function createXoAuth2SmtpTransport(
string $email,
string $accessToken
): \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport {
$transport = new \Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport(
'smtp.gmail.com',
587,
true,
);
$transport->setUsername($email);
$transport->setPassword($accessToken);
$transport->setAuthenticators([
new \Symfony\Component\Mailer\Transport\Smtp\Auth\XOAuth2Authenticator(),
]);
return $transport;
}
private function getEmailFromToken(string $accessToken): string
{
$client = $this->buildClient();
$payload = $client->verifyIdToken($accessToken);
if ($payload && isset($payload['email'])) {
return $payload['email'];
}
$http = new \GuzzleHttp\Client();
$response = $http->get('https://www.googleapis.com/oauth2/v1/userinfo?alt=json', [
'headers' => ['Authorization' => "Bearer $accessToken"],
]);
$data = json_decode((string) $response->getBody(), true);
return $data['email'];
}
public function getConnectionStatus(): array
{
$connections = GoogleOAuthConnection::where('user_id', auth()->id())->get();
$status = [];
foreach (GoogleService::cases() as $service) {
$connection = $connections->firstWhere('service', $service->value);
$status[$service->value] = [
'connected' => $connection !== null,
'connection' => $connection,
'email' => $connection?->email,
'expired' => $connection?->isExpired() ?? false,
];
}
return $status;
}
}