link_orcid-1.0.0-rc1/src/OrcidClient.php
src/OrcidClient.php
<?php
declare(strict_types=1);
namespace Drupal\link_orcid;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\key\KeyRepositoryInterface;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\GuzzleException;
/**
* ORCID API client.
*/
final class OrcidClient {
public function __construct(
private readonly ConfigFactoryInterface $configFactory,
private readonly KeyRepositoryInterface $keyRepository,
private readonly ClientInterface $httpClient,
private readonly EntityTypeManagerInterface $entityTypeManager,
private readonly AccountProxyInterface $currentUser,
) {}
/**
* The base URL for ORCID API.
*/
private function baseUrl(): string {
$sandbox = (bool) $this->config()->get('sandbox');
return $sandbox ? 'https://sandbox.orcid.org' : 'https://orcid.org';
}
/**
* The token URL for ORCID API.
*/
private function tokenUrl(): string {
return $this->baseUrl() . '/oauth/token';
}
/**
* The authorize URL for ORCID API.
*/
private function authorizeUrl(): string {
return $this->baseUrl() . '/oauth/authorize';
}
/**
* Module configuration.
*/
private function config() {
return $this->configFactory->get('link_orcid.settings');
}
/**
* Get the URL to redirect user to ORCID authorize endpoint.
*/
public function getAuthorizeRedirect(string $state, string $redirect_uri): string {
$client_id = (string) $this->config()->get('client_id');
$query = http_build_query([
'client_id' => $client_id,
'response_type' => 'code',
'scope' => '/authenticate',
'redirect_uri' => $redirect_uri,
'state' => $state,
], '', '&', PHP_QUERY_RFC3986);
return $this->authorizeUrl() . '?' . $query;
}
/**
* Exchange the received code for an access token and ORCID iD.
*
* @return array{orcid:string}|null
* Array with 'orcid' key on success, NULL on failure.
*/
public function exchangeCodeForToken(string $code, string $state, string $redirect_uri): ?array {
$client_id = (string) $this->config()->get('client_id');
$secret_key_id = (string) $this->config()->get('secret_key');
$secret = $this->keyRepository->getKey($secret_key_id)?->getKeyValue();
if (!$client_id || !$secret) {
throw new \RuntimeException('ORCID client credentials are not configured.');
}
try {
$response = $this->httpClient->request('POST', $this->tokenUrl(), [
'form_params' => [
'client_id' => $client_id,
'client_secret' => $secret,
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $redirect_uri,
],
'headers' => [
'Accept' => 'application/json',
],
]);
$data = json_decode((string) $response->getBody(), TRUE) ?: [];
if (!empty($data['orcid'])) {
return ['orcid' => (string) $data['orcid']];
}
return NULL;
}
catch (GuzzleException $e) {
throw new \RuntimeException('ORCID token exchange failed: ' . $e->getMessage(), 0, $e);
}
}
/**
* Save ORCID to current user selected field.
*/
public function saveCurrentUserOrcid(string $orcid): void {
$user = $this->entityTypeManager->getStorage('user')->load($this->currentUser->id());
if (!$user) {
throw new \RuntimeException('User not found.');
}
$field = (string) $this->config()->get('user_field');
/** @var \Drupal\user\UserInterface $user */
if (!$field || !$user->hasField($field)) {
throw new \RuntimeException('Configured ORCID field not found on user entity.');
}
$user->set($field, $orcid);
$user->save();
}
}
