userProvider = $userProvider; $this->sessionStorage = $sessionStorage; $optionResolver = new OptionsResolver([ Option::string('login_path', '/login')->min(1), Option::string('login_key', 'login')->min(1), Option::string('password_key', 'password')->min(1), Option::string('target_path', '/')->min(1), Option::mixed('on_failure')->validator(function ($value) { return is_callable($value) || $value === null; })->setOptional(null), ]); $options = $optionResolver->resolve($options); $this->loginPath = '/'.ltrim($options['login_path'], '/'); $this->loginKey = $options['login_key']; $this->passwordKey = $options['password_key']; $this->targetPath = '/'.ltrim($options['target_path'], '/'); $this->onFailure = $options['on_failure']; } /** * @throws AuthenticationException * @throws UserNotFoundException * @throws InvalidCredentialsException */ public function authenticate(ServerRequestInterface $request): ?AuthIdentity { if ($this->sessionStorage->has('user_identifier')) { $identifier = $this->sessionStorage->get('user_identifier'); $user = $this->userProvider->findByIdentifier($identifier); if ($user instanceof UserInterface) { return new AuthIdentity($user, false); } } $path = $request->getUri()->getPath(); $method = $request->getMethod(); if ($path === $this->loginPath && $method === 'GET') { return null; } if ($path !== $this->loginPath) { throw new AuthenticationException('Authentication required.'); } if ($method !== 'POST') { throw new AuthenticationException('Login form must be submitted using POST.'); } list($login, $password) = self::extractCredentials($request, $this->loginKey, $this->passwordKey); if (empty($login) || empty($password)) { throw new InvalidCredentialsException("Credentials cannot be empty."); } $this->sessionStorage->put(self::LAST_USERNAME, $login); /** * @var PasswordAuthenticatedUserInterface|UserInterface|null $user */ $user = $this->userProvider->findByIdentifier($login); if (!$user instanceof UserInterface) { throw new UserNotFoundException("Invalid username or password."); } if (!$user instanceof PasswordAuthenticatedUserInterface) { throw new AuthenticationException("The resolved user does not support password authentication."); } if (!$this->userProvider->isPasswordValid($user, $password)) { throw new InvalidCredentialsException("Invalid username or password."); } $this->sessionStorage->put('user_identifier', $user->getUserIdentifier()); return new AuthIdentity($user, true); } public function onSuccess(ServerRequestInterface $request, ResponseFactoryInterface $responseFactory): ?ResponseInterface { $response = $responseFactory->createResponse(302); return $response->withHeader('Location', $this->targetPath); } public function onFailure(ServerRequestInterface $request, ResponseFactoryInterface $responseFactory, ?AuthenticationException $exception = null): ResponseInterface { if ($exception) { $this->sessionStorage->put(self::AUTHENTICATION_ERROR, $exception->getMessage()); $request = $request->withAttribute(self::AUTHENTICATION_ERROR, $exception->getMessage()); } if (!is_callable($this->onFailure)) { $response = $responseFactory->createResponse(302); return $response->withHeader('Location', $this->loginPath); } return ($this->onFailure)($request, $responseFactory, $exception); } private static function extractCredentials(ServerRequestInterface $request, string $keyLogin, string $keyPassword): array { $data = $request->getParsedBody(); $login = $data[$keyLogin] ?? ''; $pass = $data[$keyPassword] ?? ''; return [ $login, $pass ]; } }