DEV Community

Cover image for Drupal - API Resources for Custom login
Mahmoud Sayed
Mahmoud Sayed

Posted on • Edited on

2 1

Drupal - API Resources for Custom login

Drupal 9 rest custom login resource, return session data to build cookie in frontend.
missing csrf (can be obtained at /session/token).

Create rest plugin using Drush command.
$ drush generate plugin-rest-resource
Or using an alias
$ drush gen rest-resource

This is a POST resource, so run $ drush cr
Using Rest UI enable resource and add a permission for anonymous role.

<?php

namespace Drupal\custom_rest_api\Plugin\rest\resource;

use Drupal\Core\Session\AccountProxyInterface;
use Drupal\rest\ModifiedResourceResponse;
use Drupal\rest\Plugin\ResourceBase;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Session\SessionManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Password\PasswordInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

/**
 * Represents Custom login resource records as resources.
 *
 * @RestResource (
 *   id = "custom_rest_api_custom_login_resource",
 *   label = @Translation("Custom login resource"),
 *   uri_paths = {
 *     "create" = "/api/custom/login"
 *   }
 * )
 *
 * @DCG
 * This plugin exposes database records as REST resources. In order to enable it
 * import the resource configuration into active configuration storage. You may
 * find an example of such configuration in the following file:
 * core/modules/rest/config/optional/rest.resource.entity.node.yml.
 * Alternatively you can make use of REST UI module.
 * @see https://www.drupal.org/project/restui
 * For accessing Drupal entities through REST interface use
 * \Drupal\rest\Plugin\rest\resource\EntityResource plugin.
 */
class CustomLoginResource extends ResourceBase {
  /**
   * A current user instance.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  protected $sessionManager;

  protected $moduleHandler;

  protected $password;

  /**
   * Constructs a new CustomLoginResource object.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin_id for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param array $serializer_formats
   *   The available serialization formats.
   * @param \Psr\Log\LoggerInterface $logger
   *   A logger instance.
   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
   *   A current user instance.
   */
  public function __construct(
    array $configuration,
          $plugin_id,
          $plugin_definition,
    array $serializer_formats,
    LoggerInterface $logger,
    AccountProxyInterface $current_user,
    SessionManagerInterface $session_manager,
    ModuleHandlerInterface $module_handler,
    PasswordInterface $password) {
    parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);

    $this->currentUser = $current_user;
    $this->sessionManager = $session_manager;
    $this->moduleHandler = $module_handler;
    $this->password = $password;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->getParameter('serializer.formats'),
      $container->get('logger.factory')->get('exp_fs'),
      $container->get('current_user'),
      $container->get('session_manager'),
      $container->get('module_handler'),
      $container->get('password')
    );
  }

  /**
   * Responds to POST requests.
   *
   * @return \Drupal\rest\ModifiedResourceResponse
   *   The HTTP response object.
   *
   * @throws \Symfony\Component\HttpKernel\Exception\HttpException
   *   Throws exception expected.
   */
  public function post($data) {
    $this->validate($data);
    $pass_check = FALSE;
    $name = $data['name'];
    $pass = $data['pass'];

    $account = user_load_by_name(trim($name));
    if ($account) {
      $pass_check = $this->password->check(trim($pass), $account->getPassword());
    }
    else {
      $body = [
        'error' => 'Wrong username and/or password.',
      ];
    }

    if ($pass_check == FALSE) {
      $body = [
        'error' => 'Wrong username and/or password..',
      ];
    }
    else {
      $session = \Drupal::service('session');
      $session->migrate();
      $session->set('uid', $account->id());
      $this->moduleHandler->invokeAll('user_login', [$account]);
      user_login_finalize($account);

      $sess_name = $this->sessionManager->getName();
      $sess_id = $this->sessionManager->getId();

      $body = [
        'sess_name' => $sess_name,
        'sess_id' => $sess_id,
        'current_user' => [
          'name' => $account->getAccountName(),
          'uid' => $account->id(),
          'roles' => $account->getRoles(),
        ],
      ];
    }

    return new ModifiedResourceResponse($body, 200);
  }

  /**
   * Validates incoming record.
   *
   * @param mixed $record
   *   Data to validate.
   *
   * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
   */
  protected function validate($record) {
    if (!is_array($record) || count($record) == 0) {
      throw new BadRequestHttpException(t('No record content received'));
    }

    if (empty($record['name'])) {
      throw new BadRequestHttpException(t('name id is required'));
    }
    if (empty($record['pass'])) {
      throw new BadRequestHttpException(t('Password date is required'));
    }
  }

}
Enter fullscreen mode Exit fullscreen mode

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay