You are here

AuthenticationController.php in Janrain Registration 8

File

src/Controller/AuthenticationController.php
View source
<?php

namespace Drupal\janrain_capture\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Url;
use Drupal\user\Entity\User;
use Drupal\janrain_capture\JanrainCaptureApi;
use Drupal\janrain_capture\JanrainMarkupBuilder;
use GuzzleHttp\Psr7\Uri;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use function GuzzleHttp\Psr7\parse_query;

/**
 * Authentication controller.
 */
class AuthenticationController extends ControllerBase {

  /**
   * An instance of the "janrain_capture.capture_api" service.
   *
   * @var \Drupal\janrain_capture\JanrainCaptureApi
   */
  protected $captureApi;

  /**
   * An instance of the "janrain_capture.markup_builder" service.
   *
   * @var \Drupal\janrain_capture\JanrainMarkupBuilder
   */
  protected $markupBuilder;

  /**
   * {@inheritdoc}
   */
  public function __construct(JanrainCaptureApi $capture_api, JanrainMarkupBuilder $markup_builder) {
    $this->captureApi = $capture_api;
    $this->markupBuilder = $markup_builder;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) : self {
    return new static($container
      ->get('janrain_capture.capture_api'), $container
      ->get('janrain_capture.markup_builder'));
  }

  /**
   * Restore password form.
   */
  public function forgot() {
    return $this->markupBuilder
      ->getScreenRenderArray('forgot');
  }

  /**
   * Edit profile page.
   */
  public function editProfile() {
    $access_token = $this->captureApi
      ->getAccessToken();
    $return = $this->markupBuilder
      ->getScreenRenderArray('edit-profile');
    $return['janrain_capture_edit_js'] = [
      '#markup' => '<script>var access_token = "' . $access_token . '";</script>',
      '#allowed_tags' => [
        'script',
      ],
      '#cache' => [
        'contexts' => [
          'user',
        ],
        'max-age' => 60,
      ],
    ];
    return $return;
  }

  /**
   * View profile page.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The incoming HTTP request.
   */
  public function viewProfile(Request $request) {
    if (!$request->query
      ->get('uuid')) {
      $user = User::load(\Drupal::currentUser()
        ->id());
      $uuid = $user
        ->uuid();
      return new RedirectResponse(Url::fromRoute('janrain_capture.view_profile', [
        'uuid' => $uuid,
      ])
        ->toString(), RedirectResponse::HTTP_MOVED_PERMANENTLY);
    }

    // Get current user's UUID and compare it against UUID from the parameter.
    $current_janrain_uuid = $this->captureApi
      ->getUserProfile()
      ->getUuid();
    if ($current_janrain_uuid == $_GET['uuid']) {
      return $this->markupBuilder
        ->getScreenRenderArray('public-profile');
    }
    else {
      throw new \InvalidArgumentException('An invalid uuid is given.');
    }
  }

  /**
   * Logout user from the system.
   */
  public function logout() {
    user_logout();
    return new RedirectResponse(Url::fromRoute('<front>', [], [
      'absolute' => TRUE,
    ])
      ->toString(), RedirectResponse::HTTP_MOVED_PERMANENTLY);
  }

  /**
   * Login or reset a password for a user using Janrain API.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The incoming HTTP request.
   *
   * @return string
   *   The URI to redirect the user to or the forgot password link usually
   *   used in email.
   */
  public function login(Request $request) {

    // Usually, this controller should return a URI to redirect a user to.
    // This is valid for authentication. When the password reset requested
    // a user will receive an email with the link and, opening it in a
    // browser, this controller must show the real HTML page instead of
    // just a URI.
    $response_class = Response::class;
    $one_time_login_link = FALSE;
    if ($request->query
      ->get('url_type') === 'forgot') {
      return $this
        ->forgot();
    }
    $destination_url = $this
      ->getDestinationUrl($request)
      ->setAbsolute()
      ->toString();
    try {

      // The authentication can throw exceptions so their messages
      // will be exposed on the frontend.
      $this->captureApi
        ->authenticate($this
        ->getAuthorizationCode($request), $request
        ->getUri());
      if ($one_time_login_link) {
        $this
          ->messenger()
          ->addStatus('You have been successfully logged in via one-time login link.');
      }
      $module_handler = \Drupal::moduleHandler();
      $module_handler
        ->alter('janrain_capture_auth_destination', $destination_url, $this->captureApi
        ->getUserProfile(), $this->captureApi
        ->getCurrentUser());
    } catch (\Throwable $e) {
      $this
        ->messenger()
        ->addError($e
        ->getMessage());
    }
    return new $response_class($destination_url);
  }

  /**
   * Returns the authorization code.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return string
   *   The OAuth authorization code.
   */
  protected function getAuthorizationCode(Request $request) : string {

    // If the request has no "code" it means it's malformed.
    if (!$request->query
      ->has('code')) {
      throw new BadRequestHttpException($this
        ->t('Malformed request. Authorization code is missing.'));
    }
    $code = $request->query
      ->get('code');

    // The code must be read first and then removed from the request. This
    // is required for an operation, for instance, for resetting the password.
    // The link that user will get via email will look the following:
    // https://a.com/janrain_capture/oauth?url_type=forgot&code=8uy9j8quyj3tam
    // The Janrain will expect "redirect_uri" without the "code":
    // https://a.com/janrain_capture/oauth?url_type=forgot
    // If the domain will differ, OAuth will throw the "redirect_uri does not
    // match expected value" error.
    $request->query
      ->remove('code');

    // Override global variables to ensure the "code" is no longer presented.
    $request
      ->overrideGlobals();

    // Return ejected value.
    return $code;
  }

  /**
   * Returns the URL to redirect to.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request.
   *
   * @return \Drupal\Core\Url
   *   The URL to redirect to.
   */
  protected function getDestinationUrl(Request $request) : Url {

    // See whether the request has HTTP referer.
    if ($request->server
      ->has('HTTP_REFERER')) {
      $request_uri = new Uri($request
        ->getUri());
      $referer_uri = new Uri($request->server
        ->get('HTTP_REFERER'));

      // Make sure we'll not redirect out of the current origin.
      if ($referer_uri
        ->getAuthority() === $request_uri
        ->getAuthority()) {
        return Url::fromUserInput($referer_uri
          ->getPath(), [
          'query' => parse_query($referer_uri
            ->getQuery()),
        ]);
      }
    }

    // Fallback to the front page.
    return Url::fromRoute('<front>');
  }

}

Classes

Namesort descending Description
AuthenticationController Authentication controller.