Generation2Authenticator::getUser()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 29
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 14
c 0
b 0
f 0
dl 0
loc 29
rs 9.7998
cc 4
nc 3
nop 2
1
<?php
2
3
namespace App\Security;
4
5
use App\Entity\User;
6
use D3strukt0r\OAuth2\Client\Provider\Generation2ResourceOwner;
0 ignored issues
show
Bug introduced by
The type D3strukt0r\OAuth2\Client...eneration2ResourceOwner 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...
7
use Doctrine\ORM\EntityManagerInterface;
8
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
9
use KnpU\OAuth2ClientBundle\Client\OAuth2ClientInterface;
10
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
11
use KnpU\OAuth2ClientBundle\Security\User\OAuthUserProvider;
12
use Symfony\Component\HttpFoundation\RedirectResponse;
13
use Symfony\Component\HttpFoundation\Request;
14
use Symfony\Component\HttpFoundation\Response;
15
use Symfony\Component\HttpKernel\KernelInterface;
16
use Symfony\Component\Routing\RouterInterface;
17
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
18
use Symfony\Component\Security\Core\Exception\AuthenticationException;
19
use Symfony\Component\Security\Core\User\UserInterface;
20
use Symfony\Component\Security\Core\User\UserProviderInterface;
21
use UnexpectedValueException;
22
23
class Generation2Authenticator extends SocialAuthenticator
24
{
25
    private $clientRegistry;
26
    private $em;
27
    private $router;
28
    private $kernel;
29
30
    public function __construct(
31
        ClientRegistry $clientRegistry,
32
        EntityManagerInterface $em,
33
        RouterInterface $router,
34
        KernelInterface $kernel
35
    ) {
36
        $this->clientRegistry = $clientRegistry;
37
        $this->em = $em;
38
        $this->router = $router;
39
        $this->kernel = $kernel;
40
    }
41
42
    /**
43
     * Returns a response that directs the user to authenticate.
44
     *
45
     * This is called when an anonymous request accesses a resource that
46
     * requires authentication. The job of this method is to return some
47
     * response that "helps" the user start into the authentication process.
48
     *
49
     * Examples:
50
     *  A) For a form login, you might redirect to the login page
51
     *      return new RedirectResponse('/login');
52
     *  B) For an API token authentication system, you return a 401 response
53
     *      return new Response('Auth header required', 401);
54
     *
55
     * @param Request                 $request       The request that resulted in an AuthenticationException
56
     * @param AuthenticationException $authException The exception that started the authentication process
57
     *
58
     * @return Response
59
     */
60
    public function start(Request $request, AuthenticationException $authException = null)
61
    {
62
        // not called in our app, but if it were, redirecting to the
63
        // login page makes sense
64
        $url = $this->router->generate('login');
65
66
        return new RedirectResponse($url);
67
    }
68
69
    /**
70
     * Does the authenticator support the given Request?
71
     *
72
     * If this returns false, the authenticator will be skipped.
73
     *
74
     * @param Request $request The request
75
     *
76
     * @return bool
77
     */
78
    public function supports(Request $request)
79
    {
80
        // continue ONLY if the URL matches the check URL
81
        return '/login-check' === $request->getPathInfo();
82
    }
83
84
    /**
85
     * Get the authentication credentials from the request and return them
86
     * as any type (e.g. an associate array).
87
     *
88
     * Whatever value you return here will be passed to getUser() and checkCredentials()
89
     *
90
     * For example, for a form login, you might:
91
     *
92
     *      return array(
93
     *          'username' => $request->request->get('_username'),
94
     *          'password' => $request->request->get('_password'),
95
     *      );
96
     *
97
     * Or for an API token that's on a header, you might use:
98
     *
99
     *      return array('api_key' => $request->headers->get('X-API-TOKEN'));
100
     *
101
     * @param Request $request The request
102
     *
103
     * @throws UnexpectedValueException If null is returned
104
     *
105
     * @return mixed Any non-null value
106
     */
107
    public function getCredentials(Request $request)
108
    {
109
        // this method is only called if supports() returns true
110
        return $this->fetchAccessToken($this->getClient());
111
    }
112
113
    /**
114
     * Return a UserInterface object based on the credentials.
115
     *
116
     * The *credentials* are the return value from getCredentials()
117
     *
118
     * You may throw an AuthenticationException if you wish. If you return
119
     * null, then a UsernameNotFoundException is thrown for you.
120
     *
121
     * @param mixed                 $credentials  The credentials
122
     * @param UserProviderInterface $userProvider The user provider
123
     *
124
     * @throws AuthenticationException
125
     *
126
     * @return UserInterface|null
127
     */
128
    public function getUser($credentials, UserProviderInterface $userProvider)
129
    {
130
        /** @var OAuthUserProvider $userProvider */
131
132
        /** @var Generation2ResourceOwner $originUser */
133
        $originUser = $this->getClient()->fetchUserFromToken($credentials);
134
135
        // 1) have they logged in with Facebook before? Easy!
136
        $existingUser = $this->em->getRepository('App:User')->findOneBy(['remote_id' => $originUser->getId()]);
137
        if ($existingUser) {
138
            if (empty($existingUser->getTokenData()) || unserialize($existingUser->getTokenData()) !== $credentials) {
139
                $existingUser->setTokenData(serialize($credentials));
140
                $this->em->flush();
141
            }
142
143
            return $existingUser;
144
        }
145
146
        // 2) Maybe you just want to "register" them by creating
147
        // a User object
148
        $user = (new User())
149
            ->setRemoteId($originUser->getId())
150
            ->setUsername($originUser->getUsername())
151
            ->setTokenData(serialize($credentials))
152
        ;
153
        $this->em->persist($user);
154
        $this->em->flush();
155
156
        return $user;
157
    }
158
159
    /**
160
     * Called when authentication executed, but failed (e.g. wrong username password).
161
     *
162
     * This should return the Response sent back to the user, like a
163
     * RedirectResponse to the login page or a 403 response.
164
     *
165
     * If you return null, the request will continue, but the user will
166
     * not be authenticated. This is probably not what you want to do.
167
     *
168
     * @param Request                 $request   The request
169
     * @param AuthenticationException $exception The exception
170
     *
171
     * @return Response|null
172
     */
173
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
174
    {
175
        $this->saveAuthenticationErrorToSession($request, $exception);
176
        $loginUrl = $this->router->generate('login');
177
178
        return new RedirectResponse($loginUrl);
179
    }
180
181
    /**
182
     * Called when authentication executed and was successful!
183
     *
184
     * This should return the Response sent back to the user, like a
185
     * RedirectResponse to the last page they visited.
186
     *
187
     * If you return null, the current request will continue, and the user
188
     * will be authenticated. This makes sense, for example, with an API.
189
     *
190
     * @param Request        $request     The request
191
     * @param TokenInterface $token       The token
192
     * @param string         $providerKey The provider (i.e. firewall) key
193
     *
194
     * @return Response|null
195
     */
196
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
197
    {
198
        if (!$url = $this->getPreviousUrl($request, $providerKey)) {
199
            $url = $this->router->generate('index');
200
        }
201
202
        return new RedirectResponse($url);
203
    }
204
205
    /**
206
     * @return OAuth2ClientInterface
207
     */
208
    private function getClient()
209
    {
210
        return $this->clientRegistry->getClient('generation2');
211
    }
212
}
213