Passed
Push — master ( ef89ee...96f6f3 )
by Tobias
02:56
created

Auth0Authenticator::onAuthenticationSuccess()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 3
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Happyr\Auth0Bundle\Security\Authentication;
6
7
use Auth0\SDK\API\Authentication;
8
use Auth0\SDK\Exception\ForbiddenException;
9
use Happyr\Auth0Bundle\Model\UserInfo;
10
use Happyr\Auth0Bundle\Security\Auth0UserProviderInterface;
11
use Happyr\Auth0Bundle\Security\Passport\Auth0Badge;
12
use Psr\Container\ContainerInterface;
13
use Symfony\Component\HttpFoundation\Request;
14
use Symfony\Component\HttpFoundation\Response;
15
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
16
use Symfony\Component\Security\Core\Exception\AuthenticationException;
17
use Symfony\Component\Security\Csrf\CsrfToken;
18
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
19
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
20
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
21
use Symfony\Component\Security\Http\Authenticator\AbstractAuthenticator;
22
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
23
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
24
use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport;
25
use Symfony\Component\Security\Http\HttpUtils;
26
use Symfony\Contracts\Service\ServiceSubscriberInterface;
27
28
final class Auth0Authenticator extends AbstractAuthenticator implements ServiceSubscriberInterface
29
{
30
    /**
31
     * @var string
32
     */
33
    private $checkRoute;
34
35
    /**
36
     * @var ContainerInterface
37
     */
38
    private $locator;
39
40
    public function __construct(ContainerInterface $locator, string $checkRoute)
41
    {
42
        $this->locator = $locator;
43
        $this->checkRoute = $checkRoute;
44
    }
45
46
    public static function getSubscribedServices()
47
    {
48
        return [
49
            Authentication::class,
50
            CsrfTokenManagerInterface::class,
51
            HttpUtils::class,
52
            AuthenticationSuccessHandlerInterface::class,
53
            AuthenticationFailureHandlerInterface::class,
54
            '?'.Auth0UserProviderInterface::class,
55
        ];
56
    }
57
58
    public function supports(Request $request): ?bool
59
    {
60
        if ($request->attributes->get('_route') !== $this->checkRoute) {
61
            return false;
62
        }
63
64
        return $request->query->has('code') && $request->query->has('state');
65
    }
66
67
    public function authenticate(Request $request): PassportInterface
68
    {
69
        if (null === $code = $request->query->get('code')) {
70
            throw new AuthenticationException('No oauth code in the request.');
71
        }
72
73
        if (null === $state = $request->query->get('state')) {
74
            throw new AuthenticationException('No state in the request.');
75
        }
76
77
        if (!$this->get(CsrfTokenManagerInterface::class)->isTokenValid(new CsrfToken('auth0-sso', $state))) {
78
            throw new AuthenticationException('Invalid CSRF token');
79
        }
80
81
        try {
82
            $redirectUri = $this->get(HttpUtils::class)->generateUri($request, $this->checkRoute);
83
            $tokenStruct = $this->get(Authentication::class)->codeExchange($code, $redirectUri);
84
        } catch (ForbiddenException $e) {
85
            throw new AuthenticationException($e->getMessage(), (int) $e->getCode(), $e);
86
        }
87
88
        try {
89
            // Fetch info from the user
90
            $userInfo = $this->get(Authentication::class)->userinfo($tokenStruct['access_token']);
91
            $userModel = UserInfo::create($userInfo);
92
        } catch (\Exception $e) {
93
            throw new AuthenticationException('Could not fetch user info from Auth0', 0, $e);
94
        }
95
96
        $userProviderCallback = null;
97
        if (null !== $up = $this->get(Auth0UserProviderInterface::class)) {
98
            $userProviderCallback = static function () use ($up, $userModel) {
99
                return $up->loadByUserModel($userModel);
100
            };
101
        }
102
103
        return new SelfValidatingPassport(new UserBadge($userModel->getUserId(), $userProviderCallback), [new Auth0Badge($userModel)]);
0 ignored issues
show
Bug introduced by
It seems like $userModel->getUserId() can also be of type null; however, parameter $userIdentifier of Symfony\Component\Securi...serBadge::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

103
        return new SelfValidatingPassport(new UserBadge(/** @scrutinizer ignore-type */ $userModel->getUserId(), $userProviderCallback), [new Auth0Badge($userModel)]);
Loading history...
104
    }
105
106
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
107
    {
108
        return $this->get(AuthenticationSuccessHandlerInterface::class)->onAuthenticationSuccess($request, $token);
109
    }
110
111
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception): ?Response
112
    {
113
        return $this->get(AuthenticationFailureHandlerInterface::class)->onAuthenticationFailure($request, $exception);
114
    }
115
116
    /**
117
     * @template T of object
118
     * @psalm-param class-string<T> $service
119
     *
120
     * @return T|null
0 ignored issues
show
Bug introduced by
The type Happyr\Auth0Bundle\Security\Authentication\T was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
121
     */
122
    private function get(string $service)
123
    {
124
        if ($this->locator->has($service)) {
125
            return $this->locator->get($service);
126
        }
127
128
        return null;
129
    }
130
}
131