|
<?php |
|
|
|
namespace Drupal\symfony_mailer_office365; |
|
|
|
use Drupal\Core\Cache\CacheBackendInterface; |
|
use Drupal\Core\Site\Settings; |
|
use Psr\Http\Client\ClientInterface; |
|
use Psr\Http\Message\ServerRequestFactoryInterface; |
|
use Psr\Http\Message\StreamFactoryInterface; |
|
use Psr\Log\LoggerInterface; |
|
|
|
final class Office365OAuthTokenProvider |
|
{ |
|
private const OAUTH_URL = 'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token'; |
|
private const SCOPE = 'https://outlook.office365.com/.default'; |
|
private const GRANT_TYPE = 'client_credentials'; |
|
private const CACHE_KEY = 'email-token'; |
|
|
|
private readonly string $tenant; |
|
private readonly string $clientId; |
|
private readonly string $clientSecret; |
|
|
|
public function __construct( |
|
protected readonly ClientInterface $httpClient, |
|
protected readonly ServerRequestFactoryInterface $serverRequestFactory, |
|
protected readonly StreamFactoryInterface $streamFactory, |
|
protected readonly CacheBackendInterface $cache, |
|
protected readonly LoggerInterface $logger, |
|
) { |
|
if ($credentials = Settings::get('oauth_credentials', [])) { |
|
$this->tenant = $credentials['tenant']; |
|
$this->clientId = $credentials['client_id']; |
|
$this->clientSecret = $credentials['client_secret']; |
|
} else { |
|
throw new \RuntimeException('oauth_credentials settings are not set.'); |
|
} |
|
} |
|
|
|
public function getToken(): string |
|
{ |
|
return $this->cache->get(self::CACHE_KEY)?->data ?? $this->fetchToken(); |
|
} |
|
|
|
public function fetchToken(): string |
|
{ |
|
$data = [ |
|
'client_id' => $this->clientId, |
|
'client_secret' => $this->clientSecret, |
|
'scope' => self::SCOPE, |
|
'grant_type' => self::GRANT_TYPE, |
|
]; |
|
|
|
$oAuthUrl = str_replace('{tenant}', $this->tenant, self::OAUTH_URL); |
|
|
|
$body = $this->streamFactory->createStream(http_build_query($data)); |
|
$request = $this->serverRequestFactory->createServerRequest('POST', $oAuthUrl) |
|
->withHeader('Content-Type', 'application/x-www-form-urlencoded') |
|
->withBody($body); |
|
|
|
try { |
|
$response = $this->httpClient->sendRequest($request); |
|
if (200 !== $response->getStatusCode()) { |
|
throw new \RuntimeException('Failed to fetch oauth token from Microsoft: ' . $response->getBody()); |
|
} |
|
|
|
$auth = json_decode((string)$response->getBody(), true, 512, JSON_THROW_ON_ERROR); |
|
|
|
$accessToken = $auth['access_token'] ?? null; |
|
|
|
if (!$accessToken) { |
|
throw new \RuntimeException('Received empty access token from Microsoft: ' . $response->getBody()); |
|
} |
|
|
|
$expiresIn = $auth['expires_in'] ?? time() + 300; |
|
|
|
// Subtracting 60 seconds from the TTL |
|
// as a safety margin to certainly not use an expiring token. |
|
$expiryTime = $expiresIn - 60; |
|
$this->cache->set(self::CACHE_KEY, $accessToken, $expiryTime); |
|
|
|
return $accessToken; |
|
} catch (\Throwable $exception) { |
|
$this->logger->alert($exception->getMessage()); |
|
} |
|
|
|
return ''; |
|
} |
|
} |
I updated it to match Drupal
https://gist.github.com/AuNguyen1999/122af301843cae6afac612f02368ef08