Completed
Pull Request — master (#604)
by Ian
32:00
created

AbstractGrant::setRefreshTokenRepository()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
/**
3
 * OAuth 2.0 Abstract grant.
4
 *
5
 * @author      Alex Bilbie <[email protected]>
6
 * @copyright   Copyright (c) Alex Bilbie
7
 * @license     http://mit-license.org/
8
 *
9
 * @link        https://github.com/thephpleague/oauth2-server
10
 */
11
namespace League\OAuth2\Server\Grant;
12
13
use League\Event\EmitterAwareTrait;
14
use League\OAuth2\Server\CryptTrait;
15
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
16
use League\OAuth2\Server\Entities\ClientEntityInterface;
17
use League\OAuth2\Server\Entities\ScopeEntityInterface;
18
use League\OAuth2\Server\Exception\OAuthServerException;
19
use League\OAuth2\Server\Repositories\AccessTokenRepositoryInterface;
20
use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface;
21
use League\OAuth2\Server\Repositories\ClientRepositoryInterface;
22
use League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface;
23
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
24
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
25
use League\OAuth2\Server\RequestEvent;
26
use League\OAuth2\Server\RequestTypes\AuthorizationRequest;
27
use Psr\Http\Message\ServerRequestInterface;
28
29
/**
30
 * Abstract grant class.
31
 */
32
abstract class AbstractGrant implements GrantTypeInterface
33
{
34
    use EmitterAwareTrait, CryptTrait;
35
36
    const SCOPE_DELIMITER_STRING = ' ';
37
38
    /**
39
     * @var ClientRepositoryInterface
40
     */
41
    protected $clientRepository;
42
43
    /**
44
     * @var AccessTokenRepositoryInterface
45
     */
46
    protected $accessTokenRepository;
47
48
    /**
49
     * @var ScopeRepositoryInterface
50
     */
51
    protected $scopeRepository;
52
53
    /**
54
     * @var \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface
55
     */
56
    protected $authCodeRepository;
57
58
    /**
59
     * @var \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface
60
     */
61
    protected $refreshTokenRepository;
62
63
    /**
64
     * @var \League\OAuth2\Server\Repositories\UserRepositoryInterface
65
     */
66
    protected $userRepository;
67
68
    /**
69
     * @var \DateInterval
70
     */
71
    protected $refreshTokenTTL;
72
73
    /**
74
     * @param ClientRepositoryInterface $clientRepository
75
     */
76
    public function setClientRepository(ClientRepositoryInterface $clientRepository)
77
    {
78
        $this->clientRepository = $clientRepository;
79
    }
80
81
    /**
82
     * @param AccessTokenRepositoryInterface $accessTokenRepository
83
     */
84
    public function setAccessTokenRepository(AccessTokenRepositoryInterface $accessTokenRepository)
85
    {
86
        $this->accessTokenRepository = $accessTokenRepository;
87
    }
88
89
    /**
90
     * @param ScopeRepositoryInterface $scopeRepository
91
     */
92
    public function setScopeRepository(ScopeRepositoryInterface $scopeRepository)
93
    {
94
        $this->scopeRepository = $scopeRepository;
95
    }
96
97
    /**
98
     * @param \League\OAuth2\Server\Repositories\RefreshTokenRepositoryInterface $refreshTokenRepository
99
     */
100
    public function setRefreshTokenRepository(RefreshTokenRepositoryInterface $refreshTokenRepository)
101
    {
102
        $this->refreshTokenRepository = $refreshTokenRepository;
103
    }
104
105
    /**
106
     * @param \League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface $authCodeRepository
107
     */
108
    public function setAuthCodeRepository(AuthCodeRepositoryInterface $authCodeRepository)
109
    {
110
        $this->authCodeRepository = $authCodeRepository;
111
    }
112
113
    /**
114
     * @param \League\OAuth2\Server\Repositories\UserRepositoryInterface $userRepository
115
     */
116
    public function setUserRepository(UserRepositoryInterface $userRepository)
117
    {
118
        $this->userRepository = $userRepository;
119
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124
    public function setRefreshTokenTTL(\DateInterval $refreshTokenTTL)
125
    {
126
        $this->refreshTokenTTL = $refreshTokenTTL;
127
    }
128
129
    /**
130
     * Validate the client.
131
     *
132
     * @param \Psr\Http\Message\ServerRequestInterface $request
133
     *
134
     * @throws \League\OAuth2\Server\Exception\OAuthServerException
135
     *
136
     * @return \League\OAuth2\Server\Entities\ClientEntityInterface
137
     */
138
    protected function validateClient(ServerRequestInterface $request)
139
    {
140
        list($basicAuthUser, $basicAuthPassword) = $this->getBasicAuthCredentials($request);
141
142
        $clientId = $this->getRequestParameter('client_id', $request, $basicAuthUser);
143
        if (is_null($clientId)) {
144
            throw OAuthServerException::invalidRequest('client_id');
145
        }
146
147
        // If the client is confidential require the client secret
148
        $clientSecret = $this->getRequestParameter('client_secret', $request, $basicAuthPassword);
149
150
        $client = $this->clientRepository->getClientEntity(
151
            $clientId,
152
            $this->getIdentifier(),
153
            $clientSecret,
154
            true
155
        );
156
157
        if (!$client instanceof ClientEntityInterface) {
158
            $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
159
            throw OAuthServerException::invalidClient();
160
        }
161
162
        // If a redirect URI is provided ensure it matches what is pre-registered
163
        $redirectUri = $this->getRequestParameter('redirect_uri', $request, null);
164
        if ($redirectUri !== null) {
165
            if (
166
                is_string($client->getRedirectUri())
167
                && (strcmp($client->getRedirectUri(), $redirectUri) !== 0)
168
            ) {
169
                $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
170
                throw OAuthServerException::invalidClient();
171
            } elseif (
172
                is_array($client->getRedirectUri())
173
                && in_array($redirectUri, $client->getRedirectUri()) === false
174
            ) {
175
                $this->getEmitter()->emit(new RequestEvent(RequestEvent::CLIENT_AUTHENTICATION_FAILED, $request));
176
                throw OAuthServerException::invalidClient();
177
            }
178
        }
179
180
        return $client;
181
    }
182
183
    /**
184
     * Validate scopes in the request.
185
     *
186
     * @param string $scopes
187
     * @param string $redirectUri
188
     *
189
     * @throws \League\OAuth2\Server\Exception\OAuthServerException
190
     *
191
     * @return \League\OAuth2\Server\Entities\ScopeEntityInterface[]
192
     */
193
    public function validateScopes(
194
        $scopes,
195
        $redirectUri = null
196
    ) {
197
        $scopesList = array_filter(
198
            explode(self::SCOPE_DELIMITER_STRING, trim($scopes)),
199
            function ($scope) {
200
                return !empty($scope);
201
            }
202
        );
203
204
        $scopes = [];
205
        foreach ($scopesList as $scopeItem) {
206
            $scope = $this->scopeRepository->getScopeEntityByIdentifier($scopeItem);
207
208
            if (!$scope instanceof ScopeEntityInterface) {
209
                throw OAuthServerException::invalidScope($scopeItem, $redirectUri);
210
            }
211
212
            $scopes[] = $scope;
213
        }
214
215
        return $scopes;
216
    }
217
218
    /**
219
     * Retrieve request parameter.
220
     *
221
     * @param string                                   $parameter
222
     * @param \Psr\Http\Message\ServerRequestInterface $request
223
     * @param mixed                                    $default
224
     *
225
     * @return null|string
226
     */
227
    protected function getRequestParameter($parameter, ServerRequestInterface $request, $default = null)
228
    {
229
        $requestParameters = (array) $request->getParsedBody();
230
231
        return isset($requestParameters[$parameter]) ? $requestParameters[$parameter] : $default;
232
    }
233
234
    /**
235
     * Retrieve HTTP Basic Auth credentials with the Authorization header
236
     * of a request. First index of the returned array is the username,
237
     * second is the password (so list() will work). If the header does
238
     * not exist, or is otherwise an invalid HTTP Basic header, return
239
     * [null, null].
240
     *
241
     * @param \Psr\Http\Message\ServerRequestInterface $request
242
     * @return string[]|null[]
243
     */
244
    protected function getBasicAuthCredentials(ServerRequestInterface $request)
245
    {
246
        if (!$request->hasHeader('Authorization')) {
247
            return [null, null];
248
        }
249
250
        $header = $request->getHeader('Authorization')[0];
251
        if (strpos($header, 'Basic ') !== 0) {
252
            return [null, null];
253
        }
254
255
        if (!($decoded = base64_decode(substr($header, 6)))) {
256
            return [null, null];
257
        }
258
259
        if (strpos($decoded, ':') === false) {
260
            return [null, null]; // HTTP Basic header without colon isn't valid
261
        }
262
263
        return explode(':', $decoded, 2);
264
    }
265
266
    /**
267
     * Retrieve query string parameter.
268
     *
269
     * @param string                                   $parameter
270
     * @param \Psr\Http\Message\ServerRequestInterface $request
271
     * @param mixed                                    $default
272
     *
273
     * @return null|string
274
     */
275
    protected function getQueryStringParameter($parameter, ServerRequestInterface $request, $default = null)
276
    {
277
        return isset($request->getQueryParams()[$parameter]) ? $request->getQueryParams()[$parameter] : $default;
278
    }
279
280
    /**
281
     * Retrieve cookie parameter.
282
     *
283
     * @param string                                   $parameter
284
     * @param \Psr\Http\Message\ServerRequestInterface $request
285
     * @param mixed                                    $default
286
     *
287
     * @return null|string
288
     */
289
    protected function getCookieParameter($parameter, ServerRequestInterface $request, $default = null)
290
    {
291
        return isset($request->getCookieParams()[$parameter]) ? $request->getCookieParams()[$parameter] : $default;
292
    }
293
294
    /**
295
     * Retrieve server parameter.
296
     *
297
     * @param string                                   $parameter
298
     * @param \Psr\Http\Message\ServerRequestInterface $request
299
     * @param mixed                                    $default
300
     *
301
     * @return null|string
302
     */
303
    protected function getServerParameter($parameter, ServerRequestInterface $request, $default = null)
304
    {
305
        return isset($request->getServerParams()[$parameter]) ? $request->getServerParams()[$parameter] : $default;
306
    }
307
308
    /**
309
     * Issue an access token.
310
     *
311
     * @param \DateInterval                                         $accessTokenTTL
312
     * @param \League\OAuth2\Server\Entities\ClientEntityInterface  $client
313
     * @param string                                                $userIdentifier
314
     * @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
315
     *
316
     * @return \League\OAuth2\Server\Entities\AccessTokenEntityInterface
317
     */
318
    protected function issueAccessToken(
319
        \DateInterval $accessTokenTTL,
320
        ClientEntityInterface $client,
321
        $userIdentifier,
322
        array $scopes = []
323
    ) {
324
        $accessToken = $this->accessTokenRepository->getNewToken($client, $scopes, $userIdentifier);
325
        $accessToken->setClient($client);
326
        $accessToken->setUserIdentifier($userIdentifier);
327
        $accessToken->setIdentifier($this->generateUniqueIdentifier());
328
        $accessToken->setExpiryDateTime((new \DateTime())->add($accessTokenTTL));
329
330
        foreach ($scopes as $scope) {
331
            $accessToken->addScope($scope);
332
        }
333
334
        $this->accessTokenRepository->persistNewAccessToken($accessToken);
335
336
        return $accessToken;
337
    }
338
339
    /**
340
     * Issue an auth code.
341
     *
342
     * @param \DateInterval                                         $authCodeTTL
343
     * @param \League\OAuth2\Server\Entities\ClientEntityInterface  $client
344
     * @param string                                                $userIdentifier
345
     * @param string                                                $redirectUri
346
     * @param \League\OAuth2\Server\Entities\ScopeEntityInterface[] $scopes
347
     *
348
     * @return \League\OAuth2\Server\Entities\AuthCodeEntityInterface
349
     */
350
    protected function issueAuthCode(
351
        \DateInterval $authCodeTTL,
352
        ClientEntityInterface $client,
353
        $userIdentifier,
354
        $redirectUri,
355
        array $scopes = []
356
    ) {
357
        $authCode = $this->authCodeRepository->getNewAuthCode();
358
        $authCode->setIdentifier($this->generateUniqueIdentifier());
359
        $authCode->setExpiryDateTime((new \DateTime())->add($authCodeTTL));
360
        $authCode->setClient($client);
361
        $authCode->setUserIdentifier($userIdentifier);
362
        $authCode->setRedirectUri($redirectUri);
363
364
        foreach ($scopes as $scope) {
365
            $authCode->addScope($scope);
366
        }
367
368
        $this->authCodeRepository->persistNewAuthCode($authCode);
369
370
        return $authCode;
371
    }
372
373
    /**
374
     * @param \League\OAuth2\Server\Entities\AccessTokenEntityInterface $accessToken
375
     *
376
     * @return \League\OAuth2\Server\Entities\RefreshTokenEntityInterface
377
     */
378
    protected function issueRefreshToken(AccessTokenEntityInterface $accessToken)
379
    {
380
        $refreshToken = $this->refreshTokenRepository->getNewRefreshToken();
381
        $refreshToken->setIdentifier($this->generateUniqueIdentifier());
382
        $refreshToken->setExpiryDateTime((new \DateTime())->add($this->refreshTokenTTL));
383
        $refreshToken->setAccessToken($accessToken);
384
385
        $this->refreshTokenRepository->persistNewRefreshToken($refreshToken);
386
387
        return $refreshToken;
388
    }
389
390
    /**
391
     * Generate a new unique identifier.
392
     *
393
     * @param int $length
394
     *
395
     * @throws \League\OAuth2\Server\Exception\OAuthServerException
396
     *
397
     * @return string
398
     */
399
    protected function generateUniqueIdentifier($length = 40)
400
    {
401
        try {
402
            return bin2hex(random_bytes($length));
403
            // @codeCoverageIgnoreStart
404
        } catch (\TypeError $e) {
405
            throw OAuthServerException::serverError('An unexpected error has occurred');
406
        } catch (\Error $e) {
407
            throw OAuthServerException::serverError('An unexpected error has occurred');
408
        } catch (\Exception $e) {
409
            // If you get this message, the CSPRNG failed hard.
410
            throw OAuthServerException::serverError('Could not generate a random string');
411
        }
412
        // @codeCoverageIgnoreEnd
413
    }
414
415
    /**
416
     * {@inheritdoc}
417
     */
418
    public function canRespondToAccessTokenRequest(ServerRequestInterface $request)
419
    {
420
        $requestParameters = (array) $request->getParsedBody();
421
422
        return (
423
            array_key_exists('grant_type', $requestParameters)
424
            && $requestParameters['grant_type'] === $this->getIdentifier()
425
        );
426
    }
427
428
    /**
429
     * {@inheritdoc}
430
     */
431
    public function canRespondToAuthorizationRequest(ServerRequestInterface $request)
432
    {
433
        return false;
434
    }
435
436
    /**
437
     * {@inheritdoc}
438
     */
439
    public function validateAuthorizationRequest(ServerRequestInterface $request)
440
    {
441
        throw new \LogicException('This grant cannot validate an authorization request');
442
    }
443
444
    /**
445
     * {@inheritdoc}
446
     */
447
    public function completeAuthorizationRequest(AuthorizationRequest $authorizationRequest)
448
    {
449
        throw new \LogicException('This grant cannot complete an authorization request');
450
    }
451
}
452