Skip to content

Instantly share code, notes, and snippets.

@gobinathm
Forked from sebas5384/UserLogin.php
Created February 18, 2021 08:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gobinathm/0bb4dc00973abb3437050ac11f28f221 to your computer and use it in GitHub Desktop.
Save gobinathm/0bb4dc00973abb3437050ac11f28f221 to your computer and use it in GitHub Desktop.
Drupal GraphQL Cookie Login
<?php
namespace Drupal\graphql_cookie\Plugin\GraphQL\Mutations;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\graphql\Plugin\GraphQL\Mutations\MutationPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\graphql\GraphQL\Execution\ResolveContext;
use GraphQL\Type\Definition\ResolveInfo;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Flood\FloodInterface;
use Drupal\user\UserAuthInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\user\UserInterface;
use Drupal\Core\Session\AccountInterface;
use GraphQL\Error\UserError;
/**
* Login User.
*
* @GraphQLMutation(
* id = "user_login",
* name = "userLogin",
* type = "User",
* secure = false,
* nullable = false,
* schema_cache_tags = {"user_login"},
* arguments = {
* "username" = "String",
* "password" = "String"
* }
* )
*/
class UserLogin extends MutationPluginBase implements ContainerFactoryPluginInterface {
use DependencySerializationTrait;
use StringTranslationTrait;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
// @TODO Create a Trait for the user's methods.
/**
* The user authentication.
*
* @var \Drupal\user\UserAuthInterface
*/
protected $userAuth;
/**
* The flood controller.
*
* @var \Drupal\Core\Flood\FloodInterface
*/
protected $flood;
/**
* The current user service.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* {@inheritdoc}
*/
public function __construct(
array $configuration,
$pluginId,
$pluginDefinition,
EntityTypeManagerInterface $entityTypeManager,
FloodInterface $flood,
UserAuthInterface $user_auth,
ConfigFactoryInterface $configFactory,
AccountInterface $currentUser
) {
$this->entityTypeManager = $entityTypeManager;
$this->flood = $flood;
$this->userAuth = $user_auth;
$this->configFactory = $configFactory;
$this->currentUser = $currentUser;
parent::__construct($configuration, $pluginId, $pluginDefinition);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $pluginId, $pluginDefinition) {
return new static(
$configuration,
$pluginId,
$pluginDefinition,
$container->get('entity_type.manager'),
$container->get('flood'),
$container->get('user.auth'),
$container->get('config.factory'),
$container->get('current_user')
);
}
public function resolve($value, array $args, ResolveContext $context, ResolveInfo $info) {
$request = \Drupal::request();
return $this->login($request, $args);
}
/**
* Logs in a user.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request.
*
* @return \Symfony\Component\HttpFoundation\Response
* A response which contains the ID and CSRF token.
*/
public function login(Request $request, $credentials) {
if ($this->currentUser->isAuthenticated()) {
throw new UserError('The user is logged in.');
}
if (!isset($credentials['username']) && !isset($credentials['password'])) {
throw new UserError('Missing credentials.');
}
if (!isset($credentials['username'])) {
throw new UserError('Missing credentials.username.');
}
if (!isset($credentials['password'])) {
throw new UserError('Missing credentials.password.');
}
$this->floodControl($request, $credentials['username']);
if ($this->userIsBlocked($credentials['username'])) {
throw new UserError('The user has not been activated or is blocked.');
}
if ($uid = $this->userAuth->authenticate($credentials['username'], $credentials['password'])) {
$this->flood->clear('user.http_login', $this->getLoginFloodIdentifier($request, $credentials['username']));
/** @var \Drupal\user\UserInterface $user */
$user = $this->entityTypeManager->getStorage('user')->load($uid);
$this->userLoginFinalize($user);
return $user;
}
$flood_config = $this->configFactory->get('user.flood');
if ($identifier = $this->getLoginFloodIdentifier($request, $credentials['username'])) {
$this->flood->register('user.http_login', $flood_config->get('user_window'), $identifier);
}
// Always register an IP-based failed login event.
$this->flood->register('user.failed_login_ip', $flood_config->get('ip_window'));
throw new UserError('Sorry, unrecognized username or password.');
}
/**
* Gets the login identifier for user login flood control.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
* @param string $username
* The username supplied in login credentials.
*
* @return string
* The login identifier or if the user does not exist an empty string.
*/
protected function getLoginFloodIdentifier(Request $request, $username) {
$flood_config = $this->configFactory->get('user.flood');
$accounts = $this->entityTypeManager->getStorage('user')
->loadByProperties(['name' => $username, 'status' => 1]);
if ($account = reset($accounts)) {
if ($flood_config->get('uid_only')) {
// Register flood events based on the uid only, so they apply for any
// IP address. This is the most secure option.
$identifier = $account->id();
} else {
// The default identifier is a combination of uid and IP address. This
// is less secure but more resistant to denial-of-service attacks that
// could lock out all users with public user names.
$identifier = $account->id() . '-' . $request->getClientIp();
}
return $identifier;
}
return '';
}
/**
* Enforces flood control for the current login request.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The current request.
* @param string $username
* The user name sent for login credentials.
*/
protected function floodControl(Request $request, $username) {
$flood_config = $this->configFactory->get('user.flood');
if (!$this->flood->isAllowed('user.failed_login_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) {
throw new UserError('Access is blocked because of IP based flood prevention.');
}
if ($identifier = $this->getLoginFloodIdentifier($request, $username)) {
// Don't allow login if the limit for this user has been reached.
// Default is to allow 5 failed attempts every 6 hours.
if (!$this->flood->isAllowed('user.http_login', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) {
if ($flood_config->get('uid_only')) {
$error_message = sprintf('There have been more than %s failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', $flood_config->get('user_limit'));
} else {
$error_message = 'Too many failed login attempts from your IP address. This IP address is temporarily blocked.';
}
throw new UserError($error_message);
}
}
}
/**
* Verifies if the user is blocked.
*
* @param string $name
* The username.
*
* @return bool
* TRUE if the user is blocked, otherwise FALSE.
*/
protected function userIsBlocked($name) {
return user_is_blocked($name);
}
/**
* Finalizes the user login.
*
* @param \Drupal\user\UserInterface $user
* The user.
*/
protected function userLoginFinalize(UserInterface $user) {
user_login_finalize($user);
}
}
<?php
namespace Drupal\graphql_cookie\Plugin\GraphQL\Mutations;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\graphql\Plugin\GraphQL\Mutations\MutationPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\graphql\GraphQL\Execution\ResolveContext;
use GraphQL\Type\Definition\ResolveInfo;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Session\AccountInterface;
/**
* Logout the User.
*
* @GraphQLMutation(
* id = "user_logout",
* name = "userLogout",
* type = "User",
* secure = true,
* nullable = false,
* schema_cache_tags = {"user_logout"}
* )
*/
class UserLogout extends MutationPluginBase implements ContainerFactoryPluginInterface {
use DependencySerializationTrait;
use StringTranslationTrait;
/**
* The current user service.
*
* @var \Drupal\Core\Session\AccountInterface
*/
protected $currentUser;
/**
* {@inheritdoc}
*/
public function __construct(
array $configuration,
$pluginId,
$pluginDefinition,
EntityTypeManagerInterface $entityTypeManager,
AccountInterface $currentUser
) {
$this->entityTypeManager = $entityTypeManager;
$this->currentUser = $currentUser;
parent::__construct($configuration, $pluginId, $pluginDefinition);
}
/**
* {@inheritdoc}
*/
public static function create(
ContainerInterface $container,
array $configuration,
$pluginId,
$pluginDefinition
) {
return new static(
$configuration,
$pluginId,
$pluginDefinition,
$container->get('entity_type.manager'),
$container->get('current_user')
);
}
public function resolve($value, array $args, ResolveContext $context, ResolveInfo $info) {
user_logout();
$uid = $this->currentUser->id();
return $this->entityTypeManager->getStorage('user')->load($uid);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment