Subversion Repositories cheapmusic

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
103 - 1
<?php
2
/*
3
 * Copyright 2015 Google Inc.
4
 *
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
 *
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
 
18
namespace Google\Auth;
19
 
20
use Google\Auth\HttpHandler\HttpHandlerFactory;
21
use GuzzleHttp\Psr7;
22
use GuzzleHttp\Psr7\Request;
23
use InvalidArgumentException;
24
use Psr\Http\Message\RequestInterface;
25
use Psr\Http\Message\ResponseInterface;
26
use Psr\Http\Message\UriInterface;
27
 
28
/**
29
 * OAuth2 supports authentication by OAuth2 2-legged flows.
30
 *
31
 * It primary supports
32
 * - service account authorization
33
 * - authorization where a user already has an access token
34
 */
35
class OAuth2 implements FetchAuthTokenInterface
36
{
37
    const DEFAULT_EXPIRY_SECONDS = 3600; // 1 hour
38
    const DEFAULT_SKEW_SECONDS = 60; // 1 minute
39
    const JWT_URN = 'urn:ietf:params:oauth:grant-type:jwt-bearer';
40
 
41
    /**
42
     * TODO: determine known methods from the keys of JWT::methods.
43
     */
44
    public static $knownSigningAlgorithms = array(
45
        'HS256',
46
        'HS512',
47
        'HS384',
48
        'RS256',
49
    );
50
 
51
    /**
52
     * The well known grant types.
53
     *
54
     * @var array
55
     */
56
    public static $knownGrantTypes = array(
57
        'authorization_code',
58
        'refresh_token',
59
        'password',
60
        'client_credentials',
61
    );
62
 
63
    /**
64
     * - authorizationUri
65
     *   The authorization server's HTTP endpoint capable of
66
     *   authenticating the end-user and obtaining authorization.
67
     *
68
     * @var UriInterface
69
     */
70
    private $authorizationUri;
71
 
72
    /**
73
     * - tokenCredentialUri
74
     *   The authorization server's HTTP endpoint capable of issuing
75
     *   tokens and refreshing expired tokens.
76
     *
77
     * @var UriInterface
78
     */
79
    private $tokenCredentialUri;
80
 
81
    /**
82
     * The redirection URI used in the initial request.
83
     *
84
     * @var string
85
     */
86
    private $redirectUri;
87
 
88
    /**
89
     * A unique identifier issued to the client to identify itself to the
90
     * authorization server.
91
     *
92
     * @var string
93
     */
94
    private $clientId;
95
 
96
    /**
97
     * A shared symmetric secret issued by the authorization server, which is
98
     * used to authenticate the client.
99
     *
100
     * @var string
101
     */
102
    private $clientSecret;
103
 
104
    /**
105
     * The resource owner's username.
106
     *
107
     * @var string
108
     */
109
    private $username;
110
 
111
    /**
112
     * The resource owner's password.
113
     *
114
     * @var string
115
     */
116
    private $password;
117
 
118
    /**
119
     * The scope of the access request, expressed either as an Array or as a
120
     * space-delimited string.
121
     *
122
     * @var string
123
     */
124
    private $scope;
125
 
126
    /**
127
     * An arbitrary string designed to allow the client to maintain state.
128
     *
129
     * @var string
130
     */
131
    private $state;
132
 
133
    /**
134
     * The authorization code issued to this client.
135
     *
136
     * Only used by the authorization code access grant type.
137
     *
138
     * @var string
139
     */
140
    private $code;
141
 
142
    /**
143
     * The issuer ID when using assertion profile.
144
     *
145
     * @var string
146
     */
147
    private $issuer;
148
 
149
    /**
150
     * The target audience for assertions.
151
     *
152
     * @var string
153
     */
154
    private $audience;
155
 
156
    /**
157
     * The target sub when issuing assertions.
158
     *
159
     * @var string
160
     */
161
    private $sub;
162
 
163
    /**
164
     * The number of seconds assertions are valid for.
165
     *
166
     * @var int
167
     */
168
    private $expiry;
169
 
170
    /**
171
     * The signing key when using assertion profile.
172
     *
173
     * @var string
174
     */
175
    private $signingKey;
176
 
177
    /**
178
     * The signing algorithm when using an assertion profile.
179
     *
180
     * @var string
181
     */
182
    private $signingAlgorithm;
183
 
184
    /**
185
     * The refresh token associated with the access token to be refreshed.
186
     *
187
     * @var string
188
     */
189
    private $refreshToken;
190
 
191
    /**
192
     * The current access token.
193
     *
194
     * @var string
195
     */
196
    private $accessToken;
197
 
198
    /**
199
     * The current ID token.
200
     *
201
     * @var string
202
     */
203
    private $idToken;
204
 
205
    /**
206
     * The lifetime in seconds of the current access token.
207
     *
208
     * @var int
209
     */
210
    private $expiresIn;
211
 
212
    /**
213
     * The expiration time of the access token as a number of seconds since the
214
     * unix epoch.
215
     *
216
     * @var int
217
     */
218
    private $expiresAt;
219
 
220
    /**
221
     * The issue time of the access token as a number of seconds since the unix
222
     * epoch.
223
     *
224
     * @var int
225
     */
226
    private $issuedAt;
227
 
228
    /**
229
     * The current grant type.
230
     *
231
     * @var string
232
     */
233
    private $grantType;
234
 
235
    /**
236
     * When using an extension grant type, this is the set of parameters used by
237
     * that extension.
238
     */
239
    private $extensionParams;
240
 
241
    /**
242
     * Create a new OAuthCredentials.
243
     *
244
     * The configuration array accepts various options
245
     *
246
     * - authorizationUri
247
     *   The authorization server's HTTP endpoint capable of
248
     *   authenticating the end-user and obtaining authorization.
249
     *
250
     * - tokenCredentialUri
251
     *   The authorization server's HTTP endpoint capable of issuing
252
     *   tokens and refreshing expired tokens.
253
     *
254
     * - clientId
255
     *   A unique identifier issued to the client to identify itself to the
256
     *   authorization server.
257
     *
258
     * - clientSecret
259
     *   A shared symmetric secret issued by the authorization server,
260
     *   which is used to authenticate the client.
261
     *
262
     * - scope
263
     *   The scope of the access request, expressed either as an Array
264
     *   or as a space-delimited String.
265
     *
266
     * - state
267
     *   An arbitrary string designed to allow the client to maintain state.
268
     *
269
     * - redirectUri
270
     *   The redirection URI used in the initial request.
271
     *
272
     * - username
273
     *   The resource owner's username.
274
     *
275
     * - password
276
     *   The resource owner's password.
277
     *
278
     * - issuer
279
     *   Issuer ID when using assertion profile
280
     *
281
     * - audience
282
     *   Target audience for assertions
283
     *
284
     * - expiry
285
     *   Number of seconds assertions are valid for
286
     *
287
     * - signingKey
288
     *   Signing key when using assertion profile
289
     *
290
     * - refreshToken
291
     *   The refresh token associated with the access token
292
     *   to be refreshed.
293
     *
294
     * - accessToken
295
     *   The current access token for this client.
296
     *
297
     * - idToken
298
     *   The current ID token for this client.
299
     *
300
     * - extensionParams
301
     *   When using an extension grant type, this is the set of parameters used
302
     *   by that extension.
303
     *
304
     * @param array $config Configuration array
305
     */
306
    public function __construct(array $config)
307
    {
308
        $opts = array_merge([
309
            'expiry' => self::DEFAULT_EXPIRY_SECONDS,
310
            'extensionParams' => [],
311
            'authorizationUri' => null,
312
            'redirectUri' => null,
313
            'tokenCredentialUri' => null,
314
            'state' => null,
315
            'username' => null,
316
            'password' => null,
317
            'clientId' => null,
318
            'clientSecret' => null,
319
            'issuer' => null,
320
            'sub' => null,
321
            'audience' => null,
322
            'signingKey' => null,
323
            'signingAlgorithm' => null,
324
            'scope' => null,
325
        ], $config);
326
 
327
        $this->setAuthorizationUri($opts['authorizationUri']);
328
        $this->setRedirectUri($opts['redirectUri']);
329
        $this->setTokenCredentialUri($opts['tokenCredentialUri']);
330
        $this->setState($opts['state']);
331
        $this->setUsername($opts['username']);
332
        $this->setPassword($opts['password']);
333
        $this->setClientId($opts['clientId']);
334
        $this->setClientSecret($opts['clientSecret']);
335
        $this->setIssuer($opts['issuer']);
336
        $this->setSub($opts['sub']);
337
        $this->setExpiry($opts['expiry']);
338
        $this->setAudience($opts['audience']);
339
        $this->setSigningKey($opts['signingKey']);
340
        $this->setSigningAlgorithm($opts['signingAlgorithm']);
341
        $this->setScope($opts['scope']);
342
        $this->setExtensionParams($opts['extensionParams']);
343
        $this->updateToken($opts);
344
    }
345
 
346
    /**
347
     * Verifies the idToken if present.
348
     *
349
     * - if none is present, return null
350
     * - if present, but invalid, raises DomainException.
351
     * - otherwise returns the payload in the idtoken as a PHP object.
352
     *
353
     * if $publicKey is null, the key is decoded without being verified.
354
     *
355
     * @param string $publicKey The public key to use to authenticate the token
356
     * @param array $allowed_algs List of supported verification algorithms
357
     *
358
     * @return null|object
359
     */
360
    public function verifyIdToken($publicKey = null, $allowed_algs = array())
361
    {
362
        $idToken = $this->getIdToken();
363
        if (is_null($idToken)) {
364
            return null;
365
        }
366
 
367
        $resp = $this->jwtDecode($idToken, $publicKey, $allowed_algs);
368
        if (!property_exists($resp, 'aud')) {
369
            throw new \DomainException('No audience found the id token');
370
        }
371
        if ($resp->aud != $this->getAudience()) {
372
            throw new \DomainException('Wrong audience present in the id token');
373
        }
374
 
375
        return $resp;
376
    }
377
 
378
    /**
379
     * Obtains the encoded jwt from the instance data.
380
     *
381
     * @param array $config array optional configuration parameters
382
     *
383
     * @return string
384
     */
385
    public function toJwt(array $config = [])
386
    {
387
        if (is_null($this->getSigningKey())) {
388
            throw new \DomainException('No signing key available');
389
        }
390
        if (is_null($this->getSigningAlgorithm())) {
391
            throw new \DomainException('No signing algorithm specified');
392
        }
393
        $now = time();
394
 
395
        $opts = array_merge([
396
            'skew' => self::DEFAULT_SKEW_SECONDS,
397
        ], $config);
398
 
399
        $assertion = [
400
            'iss' => $this->getIssuer(),
401
            'aud' => $this->getAudience(),
402
            'exp' => ($now + $this->getExpiry()),
403
            'iat' => ($now - $opts['skew']),
404
        ];
405
        foreach ($assertion as $k => $v) {
406
            if (is_null($v)) {
407
                throw new \DomainException($k . ' should not be null');
408
            }
409
        }
410
        if (!(is_null($this->getScope()))) {
411
            $assertion['scope'] = $this->getScope();
412
        }
413
        if (!(is_null($this->getSub()))) {
414
            $assertion['sub'] = $this->getSub();
415
        }
416
 
417
        return $this->jwtEncode($assertion, $this->getSigningKey(),
418
            $this->getSigningAlgorithm());
419
    }
420
 
421
    /**
422
     * Generates a request for token credentials.
423
     *
424
     * @return RequestInterface the authorization Url.
425
     */
426
    public function generateCredentialsRequest()
427
    {
428
        $uri = $this->getTokenCredentialUri();
429
        if (is_null($uri)) {
430
            throw new \DomainException('No token credential URI was set.');
431
        }
432
 
433
        $grantType = $this->getGrantType();
434
        $params = array('grant_type' => $grantType);
435
        switch ($grantType) {
436
            case 'authorization_code':
437
                $params['code'] = $this->getCode();
438
                $params['redirect_uri'] = $this->getRedirectUri();
439
                $this->addClientCredentials($params);
440
                break;
441
            case 'password':
442
                $params['username'] = $this->getUsername();
443
                $params['password'] = $this->getPassword();
444
                $this->addClientCredentials($params);
445
                break;
446
            case 'refresh_token':
447
                $params['refresh_token'] = $this->getRefreshToken();
448
                $this->addClientCredentials($params);
449
                break;
450
            case self::JWT_URN:
451
                $params['assertion'] = $this->toJwt();
452
                break;
453
            default:
454
                if (!is_null($this->getRedirectUri())) {
455
                    # Grant type was supposed to be 'authorization_code', as there
456
                    # is a redirect URI.
457
                    throw new \DomainException('Missing authorization code');
458
                }
459
                unset($params['grant_type']);
460
                if (!is_null($grantType)) {
461
                    $params['grant_type'] = $grantType;
462
                }
463
                $params = array_merge($params, $this->getExtensionParams());
464
        }
465
 
466
        $headers = [
467
            'Cache-Control' => 'no-store',
468
            'Content-Type' => 'application/x-www-form-urlencoded',
469
        ];
470
 
471
        return new Request(
472
            'POST',
473
            $uri,
474
            $headers,
475
            Psr7\build_query($params)
476
        );
477
    }
478
 
479
    /**
480
     * Fetches the auth tokens based on the current state.
481
     *
482
     * @param callable $httpHandler callback which delivers psr7 request
483
     *
484
     * @return array the response
485
     */
486
    public function fetchAuthToken(callable $httpHandler = null)
487
    {
488
        if (is_null($httpHandler)) {
489
            $httpHandler = HttpHandlerFactory::build();
490
        }
491
 
492
        $response = $httpHandler($this->generateCredentialsRequest());
493
        $credentials = $this->parseTokenResponse($response);
494
        $this->updateToken($credentials);
495
 
496
        return $credentials;
497
    }
498
 
499
    /**
500
     * Obtains a key that can used to cache the results of #fetchAuthToken.
501
     *
502
     * The key is derived from the scopes.
503
     *
504
     * @return string a key that may be used to cache the auth token.
505
     */
506
    public function getCacheKey()
507
    {
508
        if (is_string($this->scope)) {
509
            return $this->scope;
510
        } elseif (is_array($this->scope)) {
511
            return implode(':', $this->scope);
512
        }
513
 
514
        // If scope has not set, return null to indicate no caching.
515
        return null;
516
    }
517
 
518
    /**
519
     * Parses the fetched tokens.
520
     *
521
     * @param ResponseInterface $resp the response.
522
     *
523
     * @return array the tokens parsed from the response body.
524
     *
525
     * @throws \Exception
526
     */
527
    public function parseTokenResponse(ResponseInterface $resp)
528
    {
529
        $body = (string)$resp->getBody();
530
        if ($resp->hasHeader('Content-Type') &&
531
            $resp->getHeaderLine('Content-Type') == 'application/x-www-form-urlencoded'
532
        ) {
533
            $res = array();
534
            parse_str($body, $res);
535
 
536
            return $res;
537
        } else {
538
            // Assume it's JSON; if it's not throw an exception
539
            if (null === $res = json_decode($body, true)) {
540
                throw new \Exception('Invalid JSON response');
541
            }
542
 
543
            return $res;
544
        }
545
    }
546
 
547
    /**
548
     * Updates an OAuth 2.0 client.
549
     *
550
     * @example
551
     *   client.updateToken([
552
     *     'refresh_token' => 'n4E9O119d',
553
     *     'access_token' => 'FJQbwq9',
554
     *     'expires_in' => 3600
555
     *   ])
556
     *
557
     * @param array $config
558
     *  The configuration parameters related to the token.
559
     *
560
     *  - refresh_token
561
     *    The refresh token associated with the access token
562
     *    to be refreshed.
563
     *
564
     *  - access_token
565
     *    The current access token for this client.
566
     *
567
     *  - id_token
568
     *    The current ID token for this client.
569
     *
570
     *  - expires_in
571
     *    The time in seconds until access token expiration.
572
     *
573
     *  - expires_at
574
     *    The time as an integer number of seconds since the Epoch
575
     *
576
     *  - issued_at
577
     *    The timestamp that the token was issued at.
578
     */
579
    public function updateToken(array $config)
580
    {
581
        $opts = array_merge([
582
            'extensionParams' => [],
583
            'access_token' => null,
584
            'id_token' => null,
585
            'expires_in' => null,
586
            'expires_at' => null,
587
            'issued_at' => null,
588
        ], $config);
589
 
590
        $this->setExpiresAt($opts['expires_at']);
591
        $this->setExpiresIn($opts['expires_in']);
592
        // By default, the token is issued at `Time.now` when `expiresIn` is set,
593
        // but this can be used to supply a more precise time.
594
        if (!is_null($opts['issued_at'])) {
595
            $this->setIssuedAt($opts['issued_at']);
596
        }
597
 
598
        $this->setAccessToken($opts['access_token']);
599
        $this->setIdToken($opts['id_token']);
600
        // The refresh token should only be updated if a value is explicitly
601
        // passed in, as some access token responses do not include a refresh
602
        // token.
603
        if (array_key_exists('refresh_token', $opts)) {
604
            $this->setRefreshToken($opts['refresh_token']);
605
        }
606
    }
607
 
608
    /**
609
     * Builds the authorization Uri that the user should be redirected to.
610
     *
611
     * @param array $config configuration options that customize the return url
612
     *
613
     * @return UriInterface the authorization Url.
614
     *
615
     * @throws InvalidArgumentException
616
     */
617
    public function buildFullAuthorizationUri(array $config = [])
618
    {
619
        if (is_null($this->getAuthorizationUri())) {
620
            throw new InvalidArgumentException(
621
                'requires an authorizationUri to have been set');
622
        }
623
 
624
        $params = array_merge([
625
            'response_type' => 'code',
626
            'access_type' => 'offline',
627
            'client_id' => $this->clientId,
628
            'redirect_uri' => $this->redirectUri,
629
            'state' => $this->state,
630
            'scope' => $this->getScope(),
631
        ], $config);
632
 
633
        // Validate the auth_params
634
        if (is_null($params['client_id'])) {
635
            throw new InvalidArgumentException(
636
                'missing the required client identifier');
637
        }
638
        if (is_null($params['redirect_uri'])) {
639
            throw new InvalidArgumentException('missing the required redirect URI');
640
        }
641
        if (!empty($params['prompt']) && !empty($params['approval_prompt'])) {
642
            throw new InvalidArgumentException(
643
                'prompt and approval_prompt are mutually exclusive');
644
        }
645
 
646
        // Construct the uri object; return it if it is valid.
647
        $result = clone $this->authorizationUri;
648
        $existingParams = Psr7\parse_query($result->getQuery());
649
 
650
        $result = $result->withQuery(
651
            Psr7\build_query(array_merge($existingParams, $params))
652
        );
653
 
654
        if ($result->getScheme() != 'https') {
655
            throw new InvalidArgumentException(
656
                'Authorization endpoint must be protected by TLS');
657
        }
658
 
659
        return $result;
660
    }
661
 
662
    /**
663
     * Sets the authorization server's HTTP endpoint capable of authenticating
664
     * the end-user and obtaining authorization.
665
     *
666
     * @param string $uri
667
     */
668
    public function setAuthorizationUri($uri)
669
    {
670
        $this->authorizationUri = $this->coerceUri($uri);
671
    }
672
 
673
    /**
674
     * Gets the authorization server's HTTP endpoint capable of authenticating
675
     * the end-user and obtaining authorization.
676
     *
677
     * @return UriInterface
678
     */
679
    public function getAuthorizationUri()
680
    {
681
        return $this->authorizationUri;
682
    }
683
 
684
    /**
685
     * Gets the authorization server's HTTP endpoint capable of issuing tokens
686
     * and refreshing expired tokens.
687
     *
688
     * @return string
689
     */
690
    public function getTokenCredentialUri()
691
    {
692
        return $this->tokenCredentialUri;
693
    }
694
 
695
    /**
696
     * Sets the authorization server's HTTP endpoint capable of issuing tokens
697
     * and refreshing expired tokens.
698
     *
699
     * @param string $uri
700
     */
701
    public function setTokenCredentialUri($uri)
702
    {
703
        $this->tokenCredentialUri = $this->coerceUri($uri);
704
    }
705
 
706
    /**
707
     * Gets the redirection URI used in the initial request.
708
     *
709
     * @return string
710
     */
711
    public function getRedirectUri()
712
    {
713
        return $this->redirectUri;
714
    }
715
 
716
    /**
717
     * Sets the redirection URI used in the initial request.
718
     *
719
     * @param string $uri
720
     */
721
    public function setRedirectUri($uri)
722
    {
723
        if (is_null($uri)) {
724
            $this->redirectUri = null;
725
 
726
            return;
727
        }
728
        // redirect URI must be absolute
729
        if (!$this->isAbsoluteUri($uri)) {
730
            // "postmessage" is a reserved URI string in Google-land
731
            // @see https://developers.google.com/identity/sign-in/web/server-side-flow
732
            if ('postmessage' !== (string)$uri) {
733
                throw new InvalidArgumentException(
734
                    'Redirect URI must be absolute');
735
            }
736
        }
737
        $this->redirectUri = (string)$uri;
738
    }
739
 
740
    /**
741
     * Gets the scope of the access requests as a space-delimited String.
742
     *
743
     * @return string
744
     */
745
    public function getScope()
746
    {
747
        if (is_null($this->scope)) {
748
            return $this->scope;
749
        }
750
 
751
        return implode(' ', $this->scope);
752
    }
753
 
754
    /**
755
     * Sets the scope of the access request, expressed either as an Array or as
756
     * a space-delimited String.
757
     *
758
     * @param string|array $scope
759
     *
760
     * @throws InvalidArgumentException
761
     */
762
    public function setScope($scope)
763
    {
764
        if (is_null($scope)) {
765
            $this->scope = null;
766
        } elseif (is_string($scope)) {
767
            $this->scope = explode(' ', $scope);
768
        } elseif (is_array($scope)) {
769
            foreach ($scope as $s) {
770
                $pos = strpos($s, ' ');
771
                if ($pos !== false) {
772
                    throw new InvalidArgumentException(
773
                        'array scope values should not contain spaces');
774
                }
775
            }
776
            $this->scope = $scope;
777
        } else {
778
            throw new InvalidArgumentException(
779
                'scopes should be a string or array of strings');
780
        }
781
    }
782
 
783
    /**
784
     * Gets the current grant type.
785
     *
786
     * @return string
787
     */
788
    public function getGrantType()
789
    {
790
        if (!is_null($this->grantType)) {
791
            return $this->grantType;
792
        }
793
 
794
        // Returns the inferred grant type, based on the current object instance
795
        // state.
796
        if (!is_null($this->code)) {
797
            return 'authorization_code';
798
        } elseif (!is_null($this->refreshToken)) {
799
            return 'refresh_token';
800
        } elseif (!is_null($this->username) && !is_null($this->password)) {
801
            return 'password';
802
        } elseif (!is_null($this->issuer) && !is_null($this->signingKey)) {
803
            return self::JWT_URN;
804
        } else {
805
            return null;
806
        }
807
    }
808
 
809
    /**
810
     * Sets the current grant type.
811
     *
812
     * @param $grantType
813
     *
814
     * @throws InvalidArgumentException
815
     */
816
    public function setGrantType($grantType)
817
    {
818
        if (in_array($grantType, self::$knownGrantTypes)) {
819
            $this->grantType = $grantType;
820
        } else {
821
            // validate URI
822
            if (!$this->isAbsoluteUri($grantType)) {
823
                throw new InvalidArgumentException(
824
                    'invalid grant type');
825
            }
826
            $this->grantType = (string)$grantType;
827
        }
828
    }
829
 
830
    /**
831
     * Gets an arbitrary string designed to allow the client to maintain state.
832
     *
833
     * @return string
834
     */
835
    public function getState()
836
    {
837
        return $this->state;
838
    }
839
 
840
    /**
841
     * Sets an arbitrary string designed to allow the client to maintain state.
842
     *
843
     * @param string $state
844
     */
845
    public function setState($state)
846
    {
847
        $this->state = $state;
848
    }
849
 
850
    /**
851
     * Gets the authorization code issued to this client.
852
     */
853
    public function getCode()
854
    {
855
        return $this->code;
856
    }
857
 
858
    /**
859
     * Sets the authorization code issued to this client.
860
     *
861
     * @param string $code
862
     */
863
    public function setCode($code)
864
    {
865
        $this->code = $code;
866
    }
867
 
868
    /**
869
     * Gets the resource owner's username.
870
     */
871
    public function getUsername()
872
    {
873
        return $this->username;
874
    }
875
 
876
    /**
877
     * Sets the resource owner's username.
878
     *
879
     * @param string $username
880
     */
881
    public function setUsername($username)
882
    {
883
        $this->username = $username;
884
    }
885
 
886
    /**
887
     * Gets the resource owner's password.
888
     */
889
    public function getPassword()
890
    {
891
        return $this->password;
892
    }
893
 
894
    /**
895
     * Sets the resource owner's password.
896
     *
897
     * @param $password
898
     */
899
    public function setPassword($password)
900
    {
901
        $this->password = $password;
902
    }
903
 
904
    /**
905
     * Sets a unique identifier issued to the client to identify itself to the
906
     * authorization server.
907
     */
908
    public function getClientId()
909
    {
910
        return $this->clientId;
911
    }
912
 
913
    /**
914
     * Sets a unique identifier issued to the client to identify itself to the
915
     * authorization server.
916
     *
917
     * @param $clientId
918
     */
919
    public function setClientId($clientId)
920
    {
921
        $this->clientId = $clientId;
922
    }
923
 
924
    /**
925
     * Gets a shared symmetric secret issued by the authorization server, which
926
     * is used to authenticate the client.
927
     */
928
    public function getClientSecret()
929
    {
930
        return $this->clientSecret;
931
    }
932
 
933
    /**
934
     * Sets a shared symmetric secret issued by the authorization server, which
935
     * is used to authenticate the client.
936
     *
937
     * @param $clientSecret
938
     */
939
    public function setClientSecret($clientSecret)
940
    {
941
        $this->clientSecret = $clientSecret;
942
    }
943
 
944
    /**
945
     * Gets the Issuer ID when using assertion profile.
946
     */
947
    public function getIssuer()
948
    {
949
        return $this->issuer;
950
    }
951
 
952
    /**
953
     * Sets the Issuer ID when using assertion profile.
954
     *
955
     * @param string $issuer
956
     */
957
    public function setIssuer($issuer)
958
    {
959
        $this->issuer = $issuer;
960
    }
961
 
962
    /**
963
     * Gets the target sub when issuing assertions.
964
     */
965
    public function getSub()
966
    {
967
        return $this->sub;
968
    }
969
 
970
    /**
971
     * Sets the target sub when issuing assertions.
972
     *
973
     * @param string $sub
974
     */
975
    public function setSub($sub)
976
    {
977
        $this->sub = $sub;
978
    }
979
 
980
    /**
981
     * Gets the target audience when issuing assertions.
982
     */
983
    public function getAudience()
984
    {
985
        return $this->audience;
986
    }
987
 
988
    /**
989
     * Sets the target audience when issuing assertions.
990
     *
991
     * @param string $audience
992
     */
993
    public function setAudience($audience)
994
    {
995
        $this->audience = $audience;
996
    }
997
 
998
    /**
999
     * Gets the signing key when using an assertion profile.
1000
     */
1001
    public function getSigningKey()
1002
    {
1003
        return $this->signingKey;
1004
    }
1005
 
1006
    /**
1007
     * Sets the signing key when using an assertion profile.
1008
     *
1009
     * @param string $signingKey
1010
     */
1011
    public function setSigningKey($signingKey)
1012
    {
1013
        $this->signingKey = $signingKey;
1014
    }
1015
 
1016
    /**
1017
     * Gets the signing algorithm when using an assertion profile.
1018
     *
1019
     * @return string
1020
     */
1021
    public function getSigningAlgorithm()
1022
    {
1023
        return $this->signingAlgorithm;
1024
    }
1025
 
1026
    /**
1027
     * Sets the signing algorithm when using an assertion profile.
1028
     *
1029
     * @param string $signingAlgorithm
1030
     */
1031
    public function setSigningAlgorithm($signingAlgorithm)
1032
    {
1033
        if (is_null($signingAlgorithm)) {
1034
            $this->signingAlgorithm = null;
1035
        } elseif (!in_array($signingAlgorithm, self::$knownSigningAlgorithms)) {
1036
            throw new InvalidArgumentException('unknown signing algorithm');
1037
        } else {
1038
            $this->signingAlgorithm = $signingAlgorithm;
1039
        }
1040
    }
1041
 
1042
    /**
1043
     * Gets the set of parameters used by extension when using an extension
1044
     * grant type.
1045
     */
1046
    public function getExtensionParams()
1047
    {
1048
        return $this->extensionParams;
1049
    }
1050
 
1051
    /**
1052
     * Sets the set of parameters used by extension when using an extension
1053
     * grant type.
1054
     *
1055
     * @param $extensionParams
1056
     */
1057
    public function setExtensionParams($extensionParams)
1058
    {
1059
        $this->extensionParams = $extensionParams;
1060
    }
1061
 
1062
    /**
1063
     * Gets the number of seconds assertions are valid for.
1064
     */
1065
    public function getExpiry()
1066
    {
1067
        return $this->expiry;
1068
    }
1069
 
1070
    /**
1071
     * Sets the number of seconds assertions are valid for.
1072
     *
1073
     * @param int $expiry
1074
     */
1075
    public function setExpiry($expiry)
1076
    {
1077
        $this->expiry = $expiry;
1078
    }
1079
 
1080
    /**
1081
     * Gets the lifetime of the access token in seconds.
1082
     */
1083
    public function getExpiresIn()
1084
    {
1085
        return $this->expiresIn;
1086
    }
1087
 
1088
    /**
1089
     * Sets the lifetime of the access token in seconds.
1090
     *
1091
     * @param int $expiresIn
1092
     */
1093
    public function setExpiresIn($expiresIn)
1094
    {
1095
        if (is_null($expiresIn)) {
1096
            $this->expiresIn = null;
1097
            $this->issuedAt = null;
1098
        } else {
1099
            $this->issuedAt = time();
1100
            $this->expiresIn = (int)$expiresIn;
1101
        }
1102
    }
1103
 
1104
    /**
1105
     * Gets the time the current access token expires at.
1106
     *
1107
     * @return int
1108
     */
1109
    public function getExpiresAt()
1110
    {
1111
        if (!is_null($this->expiresAt)) {
1112
            return $this->expiresAt;
1113
        } elseif (!is_null($this->issuedAt) && !is_null($this->expiresIn)) {
1114
            return $this->issuedAt + $this->expiresIn;
1115
        }
1116
 
1117
        return null;
1118
    }
1119
 
1120
    /**
1121
     * Returns true if the acccess token has expired.
1122
     *
1123
     * @return bool
1124
     */
1125
    public function isExpired()
1126
    {
1127
        $expiration = $this->getExpiresAt();
1128
        $now = time();
1129
 
1130
        return !is_null($expiration) && $now >= $expiration;
1131
    }
1132
 
1133
    /**
1134
     * Sets the time the current access token expires at.
1135
     *
1136
     * @param int $expiresAt
1137
     */
1138
    public function setExpiresAt($expiresAt)
1139
    {
1140
        $this->expiresAt = $expiresAt;
1141
    }
1142
 
1143
    /**
1144
     * Gets the time the current access token was issued at.
1145
     */
1146
    public function getIssuedAt()
1147
    {
1148
        return $this->issuedAt;
1149
    }
1150
 
1151
    /**
1152
     * Sets the time the current access token was issued at.
1153
     *
1154
     * @param int $issuedAt
1155
     */
1156
    public function setIssuedAt($issuedAt)
1157
    {
1158
        $this->issuedAt = $issuedAt;
1159
    }
1160
 
1161
    /**
1162
     * Gets the current access token.
1163
     */
1164
    public function getAccessToken()
1165
    {
1166
        return $this->accessToken;
1167
    }
1168
 
1169
    /**
1170
     * Sets the current access token.
1171
     *
1172
     * @param string $accessToken
1173
     */
1174
    public function setAccessToken($accessToken)
1175
    {
1176
        $this->accessToken = $accessToken;
1177
    }
1178
 
1179
    /**
1180
     * Gets the current ID token.
1181
     */
1182
    public function getIdToken()
1183
    {
1184
        return $this->idToken;
1185
    }
1186
 
1187
    /**
1188
     * Sets the current ID token.
1189
     *
1190
     * @param $idToken
1191
     */
1192
    public function setIdToken($idToken)
1193
    {
1194
        $this->idToken = $idToken;
1195
    }
1196
 
1197
    /**
1198
     * Gets the refresh token associated with the current access token.
1199
     */
1200
    public function getRefreshToken()
1201
    {
1202
        return $this->refreshToken;
1203
    }
1204
 
1205
    /**
1206
     * Sets the refresh token associated with the current access token.
1207
     *
1208
     * @param $refreshToken
1209
     */
1210
    public function setRefreshToken($refreshToken)
1211
    {
1212
        $this->refreshToken = $refreshToken;
1213
    }
1214
 
1215
    /**
1216
     * The expiration of the last received token.
1217
     *
1218
     * @return array
1219
     */
1220
    public function getLastReceivedToken()
1221
    {
1222
        if ($token = $this->getAccessToken()) {
1223
            return [
1224
                'access_token' => $token,
1225
                'expires_at' => $this->getExpiresAt(),
1226
            ];
1227
        }
1228
 
1229
        return null;
1230
    }
1231
 
1232
    /**
1233
     * @todo handle uri as array
1234
     *
1235
     * @param string $uri
1236
     *
1237
     * @return null|UriInterface
1238
     */
1239
    private function coerceUri($uri)
1240
    {
1241
        if (is_null($uri)) {
1242
            return;
1243
        }
1244
 
1245
        return Psr7\uri_for($uri);
1246
    }
1247
 
1248
    /**
1249
     * @param string $idToken
1250
     * @param string|array|null $publicKey
1251
     * @param array $allowedAlgs
1252
     *
1253
     * @return object
1254
     */
1255
    private function jwtDecode($idToken, $publicKey, $allowedAlgs)
1256
    {
1257
        if (class_exists('Firebase\JWT\JWT')) {
1258
            return \Firebase\JWT\JWT::decode($idToken, $publicKey, $allowedAlgs);
1259
        }
1260
 
1261
        return \JWT::decode($idToken, $publicKey, $allowedAlgs);
1262
    }
1263
 
1264
    private function jwtEncode($assertion, $signingKey, $signingAlgorithm)
1265
    {
1266
        if (class_exists('Firebase\JWT\JWT')) {
1267
            return \Firebase\JWT\JWT::encode($assertion, $signingKey,
1268
                $signingAlgorithm);
1269
        }
1270
 
1271
        return \JWT::encode($assertion, $signingKey, $signingAlgorithm);
1272
    }
1273
 
1274
    /**
1275
     * Determines if the URI is absolute based on its scheme and host or path
1276
     * (RFC 3986).
1277
     *
1278
     * @param string $uri
1279
     *
1280
     * @return bool
1281
     */
1282
    private function isAbsoluteUri($uri)
1283
    {
1284
        $uri = $this->coerceUri($uri);
1285
 
1286
        return $uri->getScheme() && ($uri->getHost() || $uri->getPath());
1287
    }
1288
 
1289
    /**
1290
     * @param array $params
1291
     *
1292
     * @return array
1293
     */
1294
    private function addClientCredentials(&$params)
1295
    {
1296
        $clientId = $this->getClientId();
1297
        $clientSecret = $this->getClientSecret();
1298
 
1299
        if ($clientId && $clientSecret) {
1300
            $params['client_id'] = $clientId;
1301
            $params['client_secret'] = $clientSecret;
1302
        }
1303
 
1304
        return $params;
1305
    }
1306
}