pre version 2
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user