From e54197f8a54f79bec67ab8038d7109656baa703d Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Sat, 16 May 2026 21:33:06 +0300 Subject: [PATCH] implement auth controller Green phase: AuthController handles login (authenticate + create session + set cookie), logout (delete session + clear cookie), and me (return user from request attribute). --- backend/app/Controllers/AuthController.php | 145 +++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 backend/app/Controllers/AuthController.php diff --git a/backend/app/Controllers/AuthController.php b/backend/app/Controllers/AuthController.php new file mode 100644 index 0000000..5e77602 --- /dev/null +++ b/backend/app/Controllers/AuthController.php @@ -0,0 +1,145 @@ +parseBody($request); + + try { + $user = $this->authenticateUser->execute( + new AuthenticateUserRequest( + email: $body['email'] ?? null, + password: $body['password'] ?? null, + ), + ); + } catch (BadRequestException $exception) { + return $this->jsonResponse( + new Response(400), + ['error' => $exception->getMessage()], + 400, + ); + } catch (UnauthorizedException $exception) { + return $this->jsonResponse( + new Response(401), + ['error' => $exception->getMessage()], + 401, + ); + } + + $session = $this->createSession->execute($user); + + $response = $this->jsonResponse( + new Response(200), + ['user' => $this->buildUserPayload($user)], + 200, + ); + + $cookieValue = sprintf( + '%s=%s; Expires=%s; Path=/; HttpOnly; SameSite=Lax', + AuthMiddleware::COOKIE_NAME, + $session->getToken(), + $session->getExpiresAt()->format('D, d-M-Y H:i:s T'), + ); + + return $response->withHeader('Set-Cookie', $cookieValue); + } + + public function logout( + ServerRequestInterface $request, + ResponseInterface $response, + ): ResponseInterface { + $cookies = $request->getCookieParams(); + $token = $cookies[AuthMiddleware::COOKIE_NAME] ?? null; + + if (is_string($token) && $token !== '') { + $this->logout->execute($token); + } + + $response = new Response(204); + + $cookieValue = sprintf( + '%s=; Expires=%s; Path=/; HttpOnly; SameSite=Lax', + AuthMiddleware::COOKIE_NAME, + 'Thu, 01-Jan-1970 00:00:00 GMT', + ); + + return $response->withHeader('Set-Cookie', $cookieValue); + } + + public function me( + ServerRequestInterface $request, + ResponseInterface $response, + ): ResponseInterface { + $user = $request->getAttribute('user'); + + if (! $user instanceof User) { + return $this->jsonResponse( + new Response(401), + ['error' => 'unauthenticated'], + 401, + ); + } + + return $this->jsonResponse( + new Response(200), + ['user' => $this->buildUserPayload($user)], + 200, + ); + } + + private function buildUserPayload(User $user): array + { + return [ + 'id' => $user->getId(), + 'email' => $user->getEmail()->value(), + ]; + } + + private function jsonResponse( + ResponseInterface $response, + array $data, + int $status, + ): ResponseInterface { + $response = $response->withStatus($status); + $response->getBody()->write(json_encode($data)); + + return $response->withHeader('Content-Type', 'application/json'); + } + + private function parseBody(ServerRequestInterface $request): array + { + $contentType = $request->getHeaderLine('Content-Type'); + + if (str_contains($contentType, 'application/json')) { + $body = (string) $request->getBody(); + $decoded = json_decode($body, true); + + return is_array($decoded) ? $decoded : []; + } + + return (array) $request->getParsedBody(); + } +}