Completed
Push — master ( f5d9cb...35a455 )
by Gabriel
02:54
created

AbstractTokenService::refreshToken()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 9
cts 9
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 9
nc 4
nop 0
crap 3
1
<?php
2
3
namespace Somoza\OAuth2Middleware\TokenService;
4
5
use League\OAuth2\Client\Provider\AbstractProvider;
6
use League\OAuth2\Client\Token\AccessToken;
7
use Psr\Http\Message\RequestInterface;
8
9
/**
10
 * @author Gabriel Somoza <[email protected]>
11
 */
12
abstract class AbstractTokenService implements AuthorizesRequests
13
{
14
    /** @string Refresh Token grant */
15
    const GRANT_REFRESH_TOKEN = 'refresh_token';
16
17
    /** @string */
18
    const GRANT_CLIENT_CREDENTIALS = 'client_credentials';
19
20
    /** @var AbstractProvider */
21
    private $provider;
22
23
    /** @var AccessToken */
24
    private $accessToken;
25
26
    /** @var callable */
27
    private $refreshTokenCallback;
28
29
    /**
30
     * @param AbstractProvider $provider A League OAuth2 Client Provider.
31
     * @param null|AccessToken $accessToken Provide an initial (e.g. cached/persisted) access token.
32
     * @param null|callable $refreshTokenCallback Will be called with a new AccessToken as a parameter if the Access
33
     *                                            Token ever needs to be renewed.
34
     */
35 8
    public function __construct(
36
        AbstractProvider $provider,
37
        AccessToken $accessToken = null,
38
        callable $refreshTokenCallback = null
39
    ) {
40 8
        if (null === $accessToken) {
41
            // an empty token that already expired, will trigger a request for a new token
42 3
            $accessToken = new AccessToken([
43 3
                'access_token' => '123',
44 3
                'expires' => time() - 300 // expired 5 minutes ago
45
            ]);
46
        }
47 8
        $this->accessToken = $accessToken;
48
49 8
        $this->provider = $provider;
50 8
        $this->refreshTokenCallback = $refreshTokenCallback;
51 8
    }
52
53
    /**
54
     * @inheritdoc
55
     */
56 6
    final public function authorize(RequestInterface $request): RequestInterface
57
    {
58 6
        if (!$this->isAuthorized($request)) {
59
            try {
60 5
                $hasExpired = $this->getAccessToken()->hasExpired();
61 1
            } catch (\RuntimeException $e) {
62 1
                $hasExpired = false; // token has no "expires" data, so we assume it hasn't expired
63
            }
64
65 5
            if ($hasExpired) {
66 3
                $this->refreshToken();
67
            }
68
69 5
            $request = $this->getAuthorizedRequest($request);
70
        }
71
72 6
        return $request;
73
    }
74
75
    /**
76
     * @return AccessToken
77
     */
78 7
    final protected function getAccessToken(): AccessToken
79
    {
80 7
        return $this->accessToken;
81
    }
82
83
    /**
84
     * Refreshes an existing Access Token. Or requests a new one (using the client_credentials grant) if no token
85
     * is available to the service yet.
86
     *
87
     * @return void
88
     */
89 3
    final protected function refreshToken()
90
    {
91 3
        $oldAccessToken = $this->accessToken;
92
93 3
        if ($this->accessToken->getRefreshToken()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->accessToken->getRefreshToken() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
94 1
            $this->accessToken = $this->provider->getAccessToken(self::GRANT_REFRESH_TOKEN, [
95 1
                'refresh_token' => $this->accessToken->getRefreshToken(),
96
            ]);
97
        } else {
98
            // request a completely new access token
99 2
            $this->accessToken = $this->requestAccessToken();
100
        }
101
102 3
        if ($this->refreshTokenCallback) {
103 1
            call_user_func($this->refreshTokenCallback, $this->accessToken, $oldAccessToken);
104
        }
105 3
    }
106
107
    /**
108
     * Request a new Access Token from the provider
109
     *
110
     * @return AccessToken
111
     */
112
    abstract protected function requestAccessToken(): AccessToken;
113
114
    /**
115
     * Returns an authorized copy of the request. Only gets called when necessary (i.e. not if the request is already
116
     * authorized), and always with a valid (fresh) Access Token. However, it SHOULD be idempotent.
117
     *
118
     * @param RequestInterface $request An unauthorized request
119
     *
120
     * @return RequestInterface An authorized copy of the request
121
     */
122
    abstract protected function getAuthorizedRequest(RequestInterface $request): RequestInterface;
123
124
    /**
125
     * @return AbstractProvider
126
     */
127 2
    final protected function getProvider(): AbstractProvider
128
    {
129 2
        return $this->provider;
130
    }
131
}
132