src/Controller/AuthController.php line 144

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use Doctrine\DBAL\Connection;
  4. use Symfony\Component\HttpFoundation\Cookie;
  5. use Symfony\Component\HttpFoundation\JsonResponse;
  6. use Symfony\Component\HttpFoundation\Request;
  7. use Symfony\Component\HttpFoundation\Response;
  8. use Symfony\Component\Routing\Annotation\Route;
  9. class AuthController extends BaseController
  10. {
  11.     /**
  12.      * @Route("/auth/register", name="auth_register", methods={"POST"})
  13.      */
  14.     public function register(Request $requestConnection $db): Response
  15.     {
  16.         $this->ensureSchema($db);
  17.         $payload $this->payload($request);
  18.         $login trim((string) ($payload['login'] ?? ''));
  19.         $email trim((string) ($payload['email'] ?? ''));
  20.         $password = (string) ($payload['password'] ?? '');
  21.         $passwordRepeat = (string) ($payload['passwordRepeat'] ?? $payload['password_repeat'] ?? '');
  22.         if ($login === '' || $email === '' || $password === '') {
  23.             return $this->error('Все поля обязательны'422);
  24.         }
  25.         if (mb_strlen($login) < || mb_strlen($login) > 50) {
  26.             return $this->error('Логин должен быть от 3 до 50 символов'422);
  27.         }
  28.         if (!filter_var($emailFILTER_VALIDATE_EMAIL)) {
  29.             return $this->error('Некорректный e-mail'422);
  30.         }
  31.         if (mb_strlen($password) < 6) {
  32.             return $this->error('Пароль короче 6 символов'422);
  33.         }
  34.         if ($passwordRepeat !== '' && $password !== $passwordRepeat) {
  35.             return $this->error('Пароли не совпадают'422);
  36.         }
  37.         $exists $db->fetchOne(
  38.             'SELECT 1 FROM users WHERE login = ? OR email = ? LIMIT 1',
  39.             [$login$email]
  40.         );
  41.         if ($exists) {
  42.             return $this->error('Такой логин или e-mail уже зарегистрирован'409);
  43.         }
  44.         $plainToken bin2hex(random_bytes(32));
  45.         $userHash 'local:' $login;
  46.         $db->insert('users', [
  47.             'login' => $login,
  48.             'email' => $email,
  49.             'password_hash' => password_hash($passwordPASSWORD_DEFAULT),
  50.             'balance' => 0,
  51.             'hash' => $userHash,
  52.             'img' => '/assets/profile.svg',
  53.             'auth_token_hash' => hash('sha256'$plainToken),
  54.             'ip' => (string) $request->getClientIp(),
  55.             'ref_id' => 0,
  56.             'refs' => 0,
  57.             'created_at' => (new \DateTimeImmutable())->format('Y-m-d H:i:s'),
  58.         ]);
  59.         $request->getSession()->migrate(true);
  60.         $request->getSession()->set('hash'$userHash);
  61.         return $this->tokenResponse($plainToken, [
  62.             'ok' => true,
  63.             'user' => [
  64.                 'login' => $login,
  65.                 'email' => $email,
  66.                 'balance' => 0,
  67.             ],
  68.         ], $request);
  69.     }
  70.     /**
  71.      * @Route("/auth/login", name="auth_login", methods={"POST"})
  72.      */
  73.     public function login(Request $requestConnection $db): Response
  74.     {
  75.         $this->ensureSchema($db);
  76.         $payload $this->payload($request);
  77.         $login trim((string) ($payload['login'] ?? ''));
  78.         $password = (string) ($payload['password'] ?? '');
  79.         if ($login === '' || $password === '') {
  80.             return $this->error('Введите логин и пароль'422);
  81.         }
  82.         $user $db->fetchAssociative(
  83.             'SELECT * FROM users WHERE login = ? OR email = ? LIMIT 1',
  84.             [$login$login]
  85.         );
  86.         if (!$user || !password_verify($password, (string) $user['password_hash'])) {
  87.             return $this->error('Неверный логин или пароль'401);
  88.         }
  89.         $plainToken bin2hex(random_bytes(32));
  90.         $db->update('users', ['auth_token_hash' => hash('sha256'$plainToken)], ['id' => $user['id']]);
  91.         $request->getSession()->migrate(true);
  92.         $request->getSession()->set('hash'$user['hash']);
  93.         return $this->tokenResponse($plainToken, [
  94.             'ok' => true,
  95.             'user' => $this->publicUser($user),
  96.         ], $request);
  97.     }
  98.     /**
  99.      * @Route("/auth/logout", name="auth_logout", methods={"POST"})
  100.      */
  101.     public function logout(Request $requestConnection $db): Response
  102.     {
  103.         $user $this->getAuthorizedUser($request$db);
  104.         if ($user) {
  105.             $db->update('users', ['auth_token_hash' => null], ['id' => $user['id']]);
  106.         }
  107.         $request->getSession()->invalidate();
  108.         $response = new JsonResponse(['ok' => true]);
  109.         $response->headers->clearCookie('auth_token''/');
  110.         return $response;
  111.     }
  112.     /**
  113.      * @Route("/auth/me", name="auth_me", methods={"GET"})
  114.      */
  115.     public function me(Request $requestConnection $db): Response
  116.     {
  117.         $this->ensureSchema($db);
  118.         return new JsonResponse([
  119.             'user' => $this->publicUser($this->getAuthorizedUser($request$db)),
  120.         ], 200, [], false);
  121.     }
  122.     private function payload(Request $request): array
  123.     {
  124.         $decoded json_decode((string) $request->getContent(), true);
  125.         if (is_array($decoded)) {
  126.             return $decoded;
  127.         }
  128.         return $request->request->all();
  129.     }
  130.     private function error(string $messageint $status): JsonResponse
  131.     {
  132.         return new JsonResponse(['error' => $message], $status, [], false);
  133.     }
  134.     private function tokenResponse(string $plainToken, array $payloadRequest $request): JsonResponse
  135.     {
  136.         $response = new JsonResponse($payload200, [], false);
  137.         $response->headers->setCookie(Cookie::create(
  138.             'auth_token',
  139.             $plainToken,
  140.             new \DateTimeImmutable('+7 days'),
  141.             '/',
  142.             null,
  143.             $request->isSecure(),
  144.             true,
  145.             false,
  146.             Cookie::SAMESITE_STRICT
  147.         ));
  148.         return $response;
  149.     }
  150.     private function ensureSchema(Connection $db): void
  151.     {
  152.         $platform $db->getDatabasePlatform()->getName();
  153.         if (strpos($platform'mysql') !== false) {
  154.             $db->executeStatement(
  155.                 "CREATE TABLE IF NOT EXISTS users (
  156.                     id INT AUTO_INCREMENT NOT NULL,
  157.                     login VARCHAR(50) NOT NULL,
  158.                     email VARCHAR(180) NOT NULL,
  159.                     password_hash VARCHAR(255) NOT NULL,
  160.                     balance DECIMAL(10, 2) NOT NULL DEFAULT 0,
  161.                     hash VARCHAR(50) NOT NULL,
  162.                     img VARCHAR(512) NOT NULL DEFAULT '/assets/profile.svg',
  163.                     auth_token VARCHAR(225) DEFAULT NULL,
  164.                     auth_token_hash VARCHAR(64) DEFAULT NULL,
  165.                     ip VARCHAR(50) DEFAULT '',
  166.                     ref_id INT NOT NULL DEFAULT 0,
  167.                     refs INT NOT NULL DEFAULT 0,
  168.                     created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  169.                     UNIQUE INDEX uniq_users_login (login),
  170.                     UNIQUE INDEX uniq_users_email (email),
  171.                     UNIQUE INDEX uniq_users_hash (hash),
  172.                     UNIQUE INDEX uniq_users_auth_token_hash (auth_token_hash),
  173.                     PRIMARY KEY(id)
  174.                 ) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB"
  175.             );
  176.             return;
  177.         }
  178.         $db->executeStatement(
  179.             "CREATE TABLE IF NOT EXISTS users (
  180.                 id INTEGER PRIMARY KEY AUTOINCREMENT,
  181.                 login VARCHAR(50) NOT NULL UNIQUE,
  182.                 email VARCHAR(180) NOT NULL UNIQUE,
  183.                 password_hash VARCHAR(255) NOT NULL,
  184.                 balance NUMERIC NOT NULL DEFAULT 0,
  185.                 hash VARCHAR(50) NOT NULL UNIQUE,
  186.                 img VARCHAR(512) NOT NULL DEFAULT '/assets/profile.svg',
  187.                 auth_token VARCHAR(225) DEFAULT NULL,
  188.                 auth_token_hash VARCHAR(64) DEFAULT NULL UNIQUE,
  189.                 ip VARCHAR(50) DEFAULT '',
  190.                 ref_id INTEGER NOT NULL DEFAULT 0,
  191.                 refs INTEGER NOT NULL DEFAULT 0,
  192.                 created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
  193.             )"
  194.         );
  195.     }
  196. }