Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
LoginFormAuthenticator
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
6 / 6
10
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
 supports
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getLoginUrl
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 authenticate
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
3
 getCredentials
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 onAuthenticationSuccess
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace App\Security;
6
7use Override;
8use Symfony\Component\DependencyInjection\Attribute\Autowire;
9use Symfony\Component\HttpFoundation\RedirectResponse;
10use Symfony\Component\HttpFoundation\Request;
11use Symfony\Component\Routing\RouterInterface;
12use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
13use Symfony\Component\Security\Core\Exception\AuthenticationException;
14use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
15use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
16use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge;
17use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
18use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
19use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
20use Symfony\Component\Security\Http\SecurityRequestAttributes;
21use Symfony\Component\Security\Http\Util\TargetPathTrait;
22use UnexpectedValueException;
23
24class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
25{
26    use TargetPathTrait;
27
28    public function __construct(
29        private readonly RouterInterface $router,
30        #[Autowire('%env(APP_ENV)%')]
31        private readonly string $appEnv
32    ) {}
33
34    #[Override]
35    public function supports(Request $request): bool
36    {
37        if ('/login' !== $request->getPathInfo()) {
38            return false;
39        }
40
41        return $request->isMethod('POST');
42    }
43
44    #[Override]
45    protected function getLoginUrl(Request $request): string
46    {
47        return $this->router->generate('login');
48    }
49
50    #[Override]
51    public function authenticate(Request $request): Passport
52    {
53        if (false === in_array($this->appEnv, ['dev', 'test'])) {
54            throw new UnexpectedValueException('GTFO!');
55        }
56
57        $credentials = $this->getCredentials($request);
58
59        $identifier = $credentials['identifier'];
60
61        if ('' === $identifier) {
62            throw new AuthenticationException('User identifier cannot be empty.');
63        }
64
65        return new SelfValidatingPassport(
66            new UserBadge($credentials['identifier']),
67            [
68                new CsrfTokenBadge('login', $credentials['csrf_token']),
69                new RememberMeBadge(),
70            ]
71        );
72    }
73
74    /**
75     * @return array{identifier: string, csrf_token: string}
76     */
77    private function getCredentials(
78        Request $request
79    ): array
80    {
81        $credentials = [
82            'identifier' => (string)$request->request->get('identifier'),
83            'csrf_token' => (string)$request->request->get('_csrf_token'),
84        ];
85
86        $request->getSession()->set(
87            SecurityRequestAttributes::LAST_USERNAME,
88            $credentials['identifier']
89        );
90
91        return $credentials;
92    }
93
94    #[Override]
95    public function onAuthenticationSuccess(
96        Request $request,
97        TokenInterface $token,
98        string $firewallName
99    ): RedirectResponse
100    {
101        if ($targetPath = $this->getTargetPath(
102            $request->getSession(),
103            $firewallName
104        )
105        ) {
106            return new RedirectResponse($targetPath);
107        }
108
109        return new RedirectResponse($this->router->generate('welcome'));
110    }
111}