View source
<?php
namespace Drupal\openid_connect\Controller;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Logger\LoggerChannelTrait;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\Routing\Access\AccessInterface;
use Drupal\Core\Routing\TrustedRedirectResponse;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\externalauth\AuthmapInterface;
use Drupal\openid_connect\OpenIDConnect;
use Drupal\openid_connect\OpenIDConnectClientEntityInterface;
use Drupal\openid_connect\OpenIDConnectSessionInterface;
use Drupal\openid_connect\OpenIDConnectStateTokenInterface;
use Drupal\openid_connect\Plugin\OpenIDConnectClientInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
class OpenIDConnectRedirectController implements ContainerInjectionInterface, AccessInterface {
use LoggerChannelTrait;
use MessengerTrait;
use StringTranslationTrait;
protected $stateToken;
protected $requestStack;
protected $openIDConnect;
protected $session;
protected $configFactory;
protected $authmap;
protected $currentUser;
protected $moduleHandler;
protected $languageManager;
protected $entityTypeManager;
public function __construct(OpenIDConnect $openid_connect, OpenIDConnectStateTokenInterface $state_token, RequestStack $request_stack, OpenIDConnectSessionInterface $session, ConfigFactoryInterface $config_factory, AuthmapInterface $authmap, AccountProxyInterface $current_user, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager, EntityTypeManagerInterface $entity_type_manager) {
$this->openIDConnect = $openid_connect;
$this->stateToken = $state_token;
$this->requestStack = $request_stack;
$this->session = $session;
$this->configFactory = $config_factory;
$this->authmap = $authmap;
$this->currentUser = $current_user;
$this->moduleHandler = $module_handler;
$this->languageManager = $language_manager;
$this->entityTypeManager = $entity_type_manager;
}
public static function create(ContainerInterface $container) : OpenIDConnectRedirectController {
return new static($container
->get('openid_connect.openid_connect'), $container
->get('openid_connect.state_token'), $container
->get('request_stack'), $container
->get('openid_connect.session'), $container
->get('config.factory'), $container
->get('externalauth.authmap'), $container
->get('current_user'), $container
->get('module_handler'), $container
->get('language_manager'), $container
->get('entity_type.manager'));
}
public function access() : AccessResultInterface {
$request = $this->requestStack
->getCurrentRequest();
$state_token = $request
->get('state');
if ($state_token && $this->stateToken
->confirm($state_token)) {
return AccessResult::allowed();
}
return AccessResult::forbidden();
}
public function authenticate(OpenIDConnectClientEntityInterface $openid_connect_client) : RedirectResponse {
$request = $this->requestStack
->getCurrentRequest();
$this->session
->retrieveStateToken();
$params = $this->session
->retrieveOp();
$op = $params['op'] ?? 'login';
$uid = $params['uid'] ?? NULL;
$plugin = $openid_connect_client
->getPlugin();
if (!$request
->get('error') && (!$plugin instanceof OpenIDConnectClientInterface || !$request
->get('code'))) {
throw new NotFoundHttpException();
}
$provider_param = [
'@provider' => $openid_connect_client
->label(),
];
if ($request
->get('error')) {
if (in_array($request
->get('error'), [
'interaction_required',
'login_required',
'account_selection_required',
'consent_required',
])) {
$this
->messenger()
->addWarning($this
->t('Logging in with @provider has been canceled.', $provider_param));
}
else {
$variables = [
'@error' => $request
->get('error'),
'@details' => $request
->get('error_description') ? $request
->get('error_description') : $this
->t('Unknown error.'),
];
$message = 'Authorization failed: @error. Details: @details';
$this
->getLogger('openid_connect_' . $openid_connect_client
->getPluginId())
->error($message, $variables);
$this
->messenger()
->addError($this
->t('Could not authenticate with @provider.', $provider_param));
}
}
else {
$tokens = $plugin
->retrieveTokens($request
->get('code'));
if ($tokens) {
if ($op === 'login') {
$success = $this->openIDConnect
->completeAuthorization($openid_connect_client, $tokens);
if (!$success) {
$this
->messenger()
->addError($this
->t('Logging in with @provider could not be completed due to an error.', $provider_param));
}
}
elseif ($op === 'connect' && $uid === (int) $this->currentUser
->id()) {
$success = $this->openIDConnect
->connectCurrentUser($openid_connect_client, $tokens);
if ($success) {
$this
->messenger()
->addMessage($this
->t('Account successfully connected with @provider.', $provider_param));
}
else {
$this
->messenger()
->addError($this
->t('Connecting with @provider could not be completed due to an error.', $provider_param));
}
}
}
else {
$this
->messenger()
->addError($this
->t('Failed to get authentication tokens for @provider. Check logs for further details.', $provider_param));
}
}
$session = $this->session
->retrieveDestination();
$destination = $session['destination'] ?: $this->configFactory
->get('openid_connect.settings')
->get('redirect_login');
$langcode = $session['langcode'] ?: $this->languageManager
->getCurrentLanguage()
->getId();
$language = $this->languageManager
->getLanguage($langcode);
$redirect = Url::fromUri('internal:/' . ltrim($destination, '/'), [
'language' => $language,
])
->toString();
return new RedirectResponse($redirect);
}
public function redirectLogout() {
$language = $this->languageManager
->getCurrentLanguage();
$default_url = Url::fromRoute('<front>', [], [
'language' => $language,
])
->toString(TRUE);
$response = new RedirectResponse($default_url
->getGeneratedUrl());
$mapped_users = $this->authmap
->getAll($this->currentUser
->id());
if (is_array($mapped_users) & !empty($mapped_users)) {
foreach (array_keys($mapped_users) as $key) {
$client_name = substr($key, 15);
if (!empty($client_name)) {
$entity = current($this->entityTypeManager
->getStorage('openid_connect_client')
->loadByProperties([
'id' => $client_name,
]));
if ($entity) {
$endpoints = $entity
->getPlugin()
->getEndpoints();
$config = $this->configFactory
->get('openid_connect.settings');
$redirect_logout = $config
->get('redirect_logout');
$redirect_logout_url = empty($redirect_logout) ? FALSE : Url::fromUri('internal:/' . ltrim($redirect_logout, '/'), [
'language' => $language,
]);
$end_session_enabled = $config
->get('end_session_enabled') ?? FALSE;
if ($end_session_enabled && !empty($endpoints['end_session'])) {
$url_options = [
'query' => [
'id_token_hint' => $this->session
->retrieveIdToken(),
],
];
if ($redirect_logout_url) {
$url_options['query']['post_logout_redirect_uri'] = $redirect_logout_url
->setAbsolute()
->toString(TRUE)
->getGeneratedUrl();
}
$redirect = Url::fromUri($endpoints['end_session'], $url_options)
->toString(TRUE);
$response = new TrustedRedirectResponse($redirect
->getGeneratedUrl());
$response
->addCacheableDependency($redirect);
}
else {
if (!$end_session_enabled) {
$this
->messenger()
->addWarning($this
->t('@provider does not support log out. You are logged out of this site but not out of the OpenID Connect provider.', [
'@provider' => $entity
->label(),
]));
}
if ($redirect_logout_url) {
$url = $redirect_logout_url
->toString(TRUE)
->getGeneratedUrl();
$response = new TrustedRedirectResponse($url);
$response
->addCacheableDependency($url);
}
}
$rsp = [
'response' => &$response,
];
$context = [
'client' => $client_name,
];
$this->moduleHandler
->alter('openid_connect_redirect_logout', $rsp, $context);
}
}
}
}
user_logout();
return $response;
}
}