Lines
89.65%
26 / 29
Methods
71.42%
5 / 7
Classes
0.00%
0 / 1
| Name | Lines | Methods | CRAP | ||||
|---|---|---|---|---|---|---|---|
| __construct | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| supports | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| authenticate | 85.71% | 12 / 14 | 0.00% | 0 / 1 | 3.03 | ||
| onAuthenticationSuccess | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| onAuthenticationFailure | 0.00% | 0 / 1 | 0.00% | 0 / 1 | 2 | ||
| start | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| unauthorized | 100.00% | 10 / 10 | 100.00% | 1 / 1 | 1 | ||
| 27 | class GoogleApiTokenAuthenticator extends AbstractAuthenticator implements AuthenticationEntryPointInterface | |
| 28 | { | |
| 29 | private const string BEARER_PREFIX = 'Bearer '; | |
| 30 | ||
| 31 | public function __construct( | |
| 32 | private readonly GoogleIdTokenVerifierInterface $idTokenVerifier, | |
| 33 | private readonly GoogleUserResolver $userResolver, | |
| 34 | private readonly AvatarUpdater $avatarUpdater, | |
| 35 | ) { | |
| 36 | } | |
| 37 | ||
| 38 | #[\Override] | |
| 39 | public function supports(Request $request): bool | |
| 40 | { | |
| 41 | return \str_starts_with((string) $request->headers->get('Authorization'), self::BEARER_PREFIX); | |
| 42 | } | |
| 43 | ||
| 44 | #[\Override] | |
| 45 | public function authenticate(Request $request): Passport | |
| 46 | { | |
| 47 | $idToken = \trim(\substr((string) $request->headers->get('Authorization'), \strlen(self::BEARER_PREFIX))); | |
| 48 | ||
| 49 | if ('' === $idToken) { | |
| 50 | throw new CustomUserMessageAuthenticationException('Missing ID token.'); | |
| 51 | } | |
| 52 | ||
| 53 | $payload = $this->idTokenVerifier->verify($idToken); | |
| 54 | ||
| 55 | if (null === $payload) { | |
| 56 | throw new CustomUserMessageAuthenticationException('Invalid ID token.'); | |
| 57 | } | |
| 58 | ||
| 59 | [$googleId, $email] = GoogleUserResolver::identity($payload); | |
| 60 | ||
| 61 | return new SelfValidatingPassport( | |
| 62 | new UserBadge($googleId, function () use ($googleId, $email, $payload): User { | |
| 63 | $user = $this->userResolver->resolve($googleId, $email); | |
| 64 | // The app signs in with Google only: its users get their picture here | |
| 65 | $this->avatarUpdater->update($user, $payload['picture'] ?? null); | |
| 66 | ||
| 67 | return $user; | |
| 68 | }) | |
| 69 | ); | |
| 70 | } | |
| 71 | ||
| 72 | #[\Override] | |
| 73 | public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response | |
| 74 | { | |
| 75 | return null; | |
| 76 | } | |
| 77 | ||
| 78 | #[\Override] | |
| 79 | public function onAuthenticationFailure(Request $request, AuthenticationException $exception): JsonResponse | |
| 80 | { | |
| 81 | return $this->unauthorized(\strtr($exception->getMessageKey(), $exception->getMessageData())); | |
| 82 | } | |
| 83 | ||
| 84 | #[\Override] | |
| 85 | public function start(Request $request, ?AuthenticationException $authException = null): JsonResponse | |
| 86 | { | |
| 87 | return $this->unauthorized('Authentication required.'); | |
| 88 | } | |
| 89 | ||
| 90 | /** | |
| 91 | * An RFC 7807 problem, like API Platform's own errors. | |
| 92 | */ | |
| 93 | private function unauthorized(string $message): JsonResponse | |
| 94 | { | |
| 95 | return new JsonResponse( | |
| 96 | [ | |
| 97 | 'type' => '/errors/401', | |
| 98 | 'title' => 'An error occurred', | |
| 99 | 'status' => Response::HTTP_UNAUTHORIZED, | |
| 100 | 'detail' => $message, | |
| 101 | ], | |
| 102 | Response::HTTP_UNAUTHORIZED, | |
| 103 | ['WWW-Authenticate' => 'Bearer', 'Content-Type' => 'application/problem+json'], | |
| 104 | ); | |
| 105 | } | |
| 106 | } |