social_auth_vipps-8.x-2.1/src/Controller/VippsAuthController.php
src/Controller/VippsAuthController.php
<?php
namespace Drupal\social_auth_vipps\Controller;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Render\RendererInterface;
use Drupal\social_api\Plugin\NetworkManager;
use Drupal\social_auth\Controller\OAuth2ControllerBase;
use Drupal\social_auth\SocialAuthDataHandler;
use Drupal\social_auth\User\UserAuthenticator;
use Drupal\social_auth_vipps\VippsAuthManager;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* Returns responses for Social Auth Vipps routes.
*/
class VippsAuthController extends OAuth2ControllerBase {
const OAUTH2_STATES_DRUPAL_STATE_KEY = 'social_auth_vipps.oauth2_states';
const OAUTH2_STATE_IS_VALID_IN_DRUPAL_STATE_THRESHOLD = 60 * 2;
/**
* The time service.
*
* @var \Drupal\Component\Datetime\TimeInterface
*/
protected $time;
/**
* VippsAuthController constructor.
*
* @param \Drupal\Core\Messenger\MessengerInterface $messenger
* The messenger service.
* @param \Drupal\social_api\Plugin\NetworkManager $network_manager
* Used to get an instance of social_auth_vipps network plugin.
* @param \Drupal\social_auth\User\UserAuthenticator $user_authenticator
* Manages user login/registration.
* @param \Drupal\social_auth_vipps\VippsAuthManager $vipps_manager
* Used to manage authentication methods.
* @param \Symfony\Component\HttpFoundation\RequestStack $request
* Used to access GET parameters.
* @param \Drupal\social_auth\SocialAuthDataHandler $data_handler
* The Social Auth data handler.
* @param \Drupal\Core\Render\RendererInterface $renderer
* Used to handle metadata for redirection to authentication URL.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
*/
public function __construct(
MessengerInterface $messenger,
NetworkManager $network_manager,
UserAuthenticator $user_authenticator,
VippsAuthManager $vipps_manager,
RequestStack $request,
SocialAuthDataHandler $data_handler,
RendererInterface $renderer,
TimeInterface $time
) {
parent::__construct(
'Social Auth Vipps',
'social_auth_vipps',
$messenger,
$network_manager,
$user_authenticator,
$vipps_manager,
$request,
$data_handler,
$renderer
);
$this->time = $time;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('messenger'),
$container->get('plugin.network.manager'),
$container->get('social_auth.user_authenticator'),
$container->get('social_auth_vipps.manager'),
$container->get('request_stack'),
$container->get('social_auth.data_handler'),
$container->get('renderer'),
$container->get('datetime.time')
);
}
/**
* Response for path 'user/login/vipps/callback'.
*
* Vipps returns the user here after user has authenticated.
*/
public function callback() {
/** @var \Drupal\social_auth_vipps\Provider\VippsResourceOwner|null $profile */
$profile = $this->processCallback();
// If authentication was successful.
if ($profile !== NULL) {
$profile->verificationGuard();
// Gets (or not) extra initial data.
$data = $this->userAuthenticator->checkProviderIsAssociated($profile->getId()) ? NULL : $this->providerManager->getExtraDetails();
// If user information could be retrieved.
return $this->userAuthenticator->authenticateUser(
$profile->getNickName(),
$profile->getEmail(),
$profile->getId(),
$this->providerManager->getAccessToken(),
$profile->getAvatarUrl(),
$data
);
}
return $this->redirect('user.login');
}
/**
* {@inheritdoc}
*/
public function processCallback() {
$retrieved_state = $this->request->getCurrentRequest()->query->get('state');
$stored_oauth2_states = $this->state()->get(self::OAUTH2_STATES_DRUPAL_STATE_KEY);
$result = parent::processCallback();
if ($result !== NULL) {
// Process passed successfully.
// Flush state value from the Drupal states, so it cannot be re-used.
if (isset($stored_oauth2_states[$retrieved_state])) {
unset($stored_oauth2_states[$retrieved_state]);
$this->state()->set(self::OAUTH2_STATES_DRUPAL_STATE_KEY, $stored_oauth2_states);
}
return $result;
}
// Check for "Login failed. Invalid OAuth2 state." error message.
// This might be inaccurate now, but starting from "social_auth" v.4.1.2,
// there was process callback errors handling introduced.
// Let's keep this like this for now.
$status_messages = $this->messenger()->all();
$invalid_state_error = FALSE;
foreach ($status_messages as $message_type => $messages) {
if ($message_type !== MessengerInterface::TYPE_ERROR) {
continue;
}
foreach ($messages as $message) {
$message = (string) $message;
if (strpos($message, 'Invalid OAuth2 state') !== FALSE) {
$invalid_state_error = TRUE;
break;
}
}
if ($invalid_state_error) {
break;
}
}
// Sorry, not our stuff.
if (!$invalid_state_error) {
return NULL;
}
// Run our OAuth2 state check.
if (isset($stored_oauth2_states[$retrieved_state])) {
// Great, we have a match.
// Now, let's check whether value is still valid.
$created = $stored_oauth2_states[$retrieved_state];
$allowed_until = $created + self::OAUTH2_STATE_IS_VALID_IN_DRUPAL_STATE_THRESHOLD;
if ($this->time->getRequestTime() < $allowed_until) {
// This will set needed session key, and processCallback() should pass
// successfully this time.
$this->dataHandler->set('oauth2state', $retrieved_state);
}
// Flush value from the Drupal states.
unset($stored_oauth2_states[$retrieved_state]);
$this->state()->set(self::OAUTH2_STATES_DRUPAL_STATE_KEY, $stored_oauth2_states);
// Clear status messages to prevent showing already existing error.
$this->messenger()->deleteByType(MessengerInterface::TYPE_ERROR);
// Now, run same process again.
return parent::processCallback();
}
return NULL;
}
/**
* {@inheritdoc}
*/
public function redirectToProvider() {
$method_call = parent::redirectToProvider();
// Get the value that was set in user's session during parent method call.
$session_oauth2_state = $this->dataHandler->get('oauth2state');
// Store such auth state in Drupal states for possible additional check.
if ($session_oauth2_state) {
$stored_oauth2_states = $this->state()->get(self::OAUTH2_STATES_DRUPAL_STATE_KEY);
$stored_oauth2_states[$session_oauth2_state] = $this->time->getRequestTime();
$this->state()->set(self::OAUTH2_STATES_DRUPAL_STATE_KEY, $stored_oauth2_states);
}
return $method_call;
}
}
