Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
LoginAction
100.00% covered (success)
100.00%
36 / 36
100.00% covered (success)
100.00%
3 / 3
5
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 __invoke
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
1
 validateInput
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2
3declare(strict_types=1);
4
5namespace App\Action\Auth;
6
7use App\Domain\Auth\Service\AuthService;
8use App\Renderer\JsonRenderer;
9use InvalidArgumentException;
10use Psr\Http\Message\ResponseInterface;
11use Psr\Http\Message\ServerRequestInterface;
12
13use function sprintf;
14
15/**
16 * Login user.
17 *
18 * POST /api/auth/login
19 */
20final readonly class LoginAction
21{
22    public function __construct(
23        private AuthService $authService,
24        private JsonRenderer $renderer
25    ) {}
26
27    public function __invoke(
28        ServerRequestInterface $request,
29        ResponseInterface $response,
30    ): ResponseInterface {
31        // Get request data
32        $data = (array)$request->getParsedBody();
33
34        // Validate required fields
35        $this->validateInput($data);
36
37        // Get IP and User Agent for session tracking
38        $serverParams = $request->getServerParams();
39        $ipAddress = $serverParams['REMOTE_ADDR'] ?? null;
40        $userAgent = $request->getHeaderLine('User-Agent');
41
42        // Login
43        $result = $this->authService->login(
44            username: $data['username'],
45            password: $data['password'],
46            ipAddress: $ipAddress,
47            userAgent: $userAgent,
48        );
49
50        $user = $result['user'];
51        $tokens = $result['tokens'];
52
53        return $this->renderer->json($response, [
54            'success' => true,
55            'message' => 'Login successful',
56            'data' => [
57                'user' => [
58                    'userId' => $user->userId,
59                    'username' => $user->username,
60                    'email' => $user->email,
61                    'role' => $user->role,
62                ],
63                'accessToken' => $tokens->accessToken,
64                'refreshToken' => $tokens->refreshToken,
65                'tokenType' => $tokens->tokenType,
66                'expiresIn' => $tokens->expiresIn,
67            ],
68        ]);
69    }
70
71    /**
72     * Validate required input fields.
73     *
74     * @param array<string, mixed> $data
75     */
76    private function validateInput(array $data): void
77    {
78        $required = ['username', 'password'];
79
80        foreach ($required as $field) {
81            if (empty($data[$field])) {
82                throw new InvalidArgumentException(
83                    sprintf('Missing required field: %s', $field),
84                );
85            }
86        }
87    }
88}