View source
<?php
namespace Drupal\janrain_capture;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\KeyValueStore\KeyValueDatabaseFactory;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\janrain_capture\Authentication\AccessToken;
use Drupal\janrain_capture\Authentication\RefreshToken;
use Drupal\janrain_capture\Exception\JanrainApiCallError;
use Drupal\janrain_capture\Exception\JanrainUnauthorizedError;
use Drupal\janrain_capture\Exception\JsonParseError;
use Drupal\janrain_capture\User\JanrainUserProfile;
use Drupal\user\UserDataInterface;
use Drupal\user\UserInterface;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\TransferException;
class JanrainCaptureApi implements JanrainCaptureApiInterface {
protected $httpClient;
protected $userStorage;
protected $currentUser;
protected $userData;
protected $moduleHandler;
protected $logger;
protected $dbStorage;
protected $clientId;
protected $clientSecret;
protected $captureAddress;
public function __construct(Client $http_client, AccountProxyInterface $current_user, UserDataInterface $user_data, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, KeyValueDatabaseFactory $database_factory, EntityTypeManagerInterface $entity_type_manager, LoggerChannelFactoryInterface $logger_factory) {
$config = $config_factory
->get('janrain_capture.settings')
->get('capture');
$this->clientId = $config['client_id'] ?? '';
$this->clientSecret = $config['client_secret'] ?? '';
$this->captureAddress = $config['capture_server'] ?? '';
$this->logger = $logger_factory
->get('janrain_capture');
$this->userData = $user_data;
$this->dbStorage = $database_factory
->get('janrain_capture');
$this->httpClient = $http_client;
$this->userStorage = $entity_type_manager
->getStorage('user');
$this->currentUser = $current_user;
$this->moduleHandler = $module_handler;
}
public function authenticate(string $auth_code, string $redirect_uri) : UserInterface {
$token = $this
->getToken(static::GRANT_TYPE_AUTHORIZATION_CODE, [
'code' => $auth_code,
'redirect_uri' => $redirect_uri,
]);
$profile = $this
->getEntity($token);
$email = $profile
->getEmail();
$uuid = $profile
->getUuid();
$accounts = $this->userStorage
->getQuery('OR')
->condition('uuid', $uuid)
->condition('mail', $email)
->execute();
if (empty($accounts)) {
$is_new = TRUE;
$account = $this->userStorage
->create([
'uuid' => $uuid,
'name' => $email,
'mail' => $email,
'status' => TRUE,
]);
$this->userStorage
->save($account);
}
else {
$is_new = FALSE;
$account = $this->userStorage
->load(reset($accounts));
}
if ($account
->getAccountName() !== $email) {
$account
->setUsername($email);
$account
->save();
}
user_login_finalize($account);
$this->currentUser = $account;
$this->userData
->set('janrain_capture', $account
->id(), 'janrain_username', $profile
->getUsername());
$this->moduleHandler
->invokeAll('janrain_capture_user_authenticated', [
$profile,
$account,
$is_new,
]);
$this
->cache($token);
return $account;
}
public function getAccessToken(bool $force_refresh = FALSE) : AccessToken {
$access_token = $this
->cache();
if ($access_token === NULL) {
throw new JanrainUnauthorizedError('The user has never been authenticated.', -1);
}
if (!$force_refresh && !$access_token
->isExpired()) {
return $access_token;
}
return $this
->cache($this
->getToken(static::GRANT_TYPE_REFRESH_TOKEN, [
'refresh_token' => $access_token
->getRefreshToken()
->getToken(),
]));
}
public function getUserProfile() : JanrainUserProfile {
try {
return $this
->getEntity($this
->getAccessToken());
} catch (JanrainUnauthorizedError $e) {
return $this
->getEntity($this
->getAccessToken(TRUE));
}
}
public function isJanrainAccount(UserInterface $account) : bool {
return (bool) $this->userData
->get('janrain_capture', $account
->id(), 'janrain_username');
}
protected function cache(AccessToken $access_token = NULL) : ?AccessToken {
$user_id = $this->currentUser
->id();
if ($user_id < 1) {
throw new \LogicException('Cannot read/store an access token for an unauthenticated user.');
}
$cache_key = "{$user_id}:access_token";
if ($access_token === NULL) {
return $this->dbStorage
->get($cache_key);
}
$this->dbStorage
->set($cache_key, $access_token);
return $access_token;
}
protected function getToken(string $grant_type, array $parameters) : AccessToken {
if (!in_array($grant_type, [
static::GRANT_TYPE_AUTHORIZATION_CODE,
static::GRANT_TYPE_REFRESH_TOKEN,
], TRUE)) {
throw new \InvalidArgumentException(sprintf('The "$grant_type" argument is invalid for the "%s" method.', __METHOD__));
}
$parameters['grant_type'] = $grant_type;
$data = $this
->call('oauth/token', $parameters);
return new AccessToken($data->access_token, $data->expires_in, new RefreshToken($data->refresh_token));
}
protected function getEntity(AccessToken $access_token) : JanrainUserProfile {
$entity = $this
->call('entity', [], $access_token
->getToken());
if (isset($entity->code)) {
throw new JanrainUnauthorizedError($entity->error_description, $entity->code);
}
return new JanrainUserProfile($entity->result);
}
protected function call($command, array $data = [], string $access_token = NULL) : \stdClass {
$headers = [];
if ($access_token !== NULL) {
$headers['Authorization'] = "OAuth {$access_token}";
}
$data['client_id'] = $this->clientId;
$data['client_secret'] = $this->clientSecret;
try {
$result = $this->httpClient
->post($this->captureAddress . '/' . $command, [
'headers' => $headers,
'form_params' => $data,
]);
} catch (RequestException $e) {
$result = $e
->getResponse();
} catch (TransferException $e) {
$this->logger
->error('The exception is thrown during API call: @message', [
'@message' => $e
->getMessage(),
]);
throw $e;
}
$body = (string) $result
->getBody();
$json = json_decode($body);
if ($json === NULL) {
$this->logger
->error('JSON parse error for response data: @data', [
'@data' => $body,
]);
throw new JsonParseError($body);
}
if ($json->stat === 'error') {
$this->logger
->error('Error response received: @response', [
'@response' => var_export($json, TRUE),
]);
throw new JanrainApiCallError($json->error_description, $json->code, $json);
}
return $json;
}
public function getCurrentUser() : UserInterface {
return $this->currentUser;
}
}