Completed
Pull Request — master (#184)
by
unknown
06:02
created

DropboxAuthHelper::setAccessTokenType()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 1
crap 12
1
<?php
2
namespace Kunnu\Dropbox\Authentication;
3
4
use Kunnu\Dropbox\Models\AccessToken;
5
use Kunnu\Dropbox\Exceptions\DropboxClientException;
6
use Kunnu\Dropbox\Store\PersistentDataStoreInterface;
7
use Kunnu\Dropbox\Security\RandomStringGeneratorInterface;
8
9
class DropboxAuthHelper
10
{
11
    /**
12
     * Access token type online param will returns a short lived access token that
13
     * require the user to reauthenticate once expired.
14
     *
15
     * @var string
16
     */
17
    CONST TOKEN_ACCESS_TYPE_ONLINE = 'online';
18
19
    /**
20
     * Access token type offline returns short lived access token and a refresh token that can be used
21
     * to generate a new access token once the original is expired
22
     *
23
     */
24
    CONST TOKEN_ACCESS_TYPE_OFFLINE = 'offline';
25
26
    /**
27
     * Grant type used for fetching new access token
28
     */
29
    CONST GRANT_TYPE_AUTHORIZATION_CODE = 'authorization_code';
30
31
    /**
32
     * Grant type for fetching a new token based on an existing refresh token
33
     */
34
    CONST GRANT_TYPE_REFRESH_TOKEN = 'refresh_token';
35
36
    /**
37
     * The length of CSRF string
38
     *
39
     * @const int
40
     */
41
    const CSRF_LENGTH = 32;
42
43
    /**
44
     * OAuth2 Client
45
     *
46
     * @var \Kunnu\Dropbox\Authentication\OAuth2Client
47
     */
48
    protected $oAuth2Client;
49
50
    /**
51
     * Random String Generator
52
     *
53
     * @var \Kunnu\Dropbox\Security\RandomStringGeneratorInterface
54
     */
55
    protected $randomStringGenerator;
56
57
    /**
58
     * Persistent Data Store
59
     *
60
     * @var \Kunnu\Dropbox\Store\PersistentDataStoreInterface
61
     */
62
    protected $persistentDataStore;
63
64
    /**
65
     * Additional User Provided State
66
     *
67
     * @var string
68
     */
69
    protected $urlState = null;
70
71
    /**
72
     * The selected access token type to be used when authenticating.
73
     *
74
     * If omitted (null), Dropbox  response will default to returning a long-lived access_token
75
     * if they are allowed in the app console.
76
     * If long-lived access tokens are disabled in the app console, this parameter defaults to online.
77
     *
78
     * @var string $accessTokeenType
79
     */
80
    protected $accessTokenType = null;
81
82
    /**
83
     * Create a new DropboxAuthHelper instance
84
     *
85
     * @param \Kunnu\Dropbox\Authentication\OAuth2Client             $oAuth2Client
86
     * @param \Kunnu\Dropbox\Security\RandomStringGeneratorInterface $randomStringGenerator
87
     * @param \Kunnu\Dropbox\Store\PersistentDataStoreInterface      $persistentDataStore
88
     */
89
    public function __construct(
90
        OAuth2Client $oAuth2Client,
91
        RandomStringGeneratorInterface $randomStringGenerator = null,
92
        PersistentDataStoreInterface $persistentDataStore = null
93
        ) {
94
        $this->oAuth2Client = $oAuth2Client;
95
        $this->randomStringGenerator = $randomStringGenerator;
96
        $this->persistentDataStore = $persistentDataStore;
97
    }
98
99
    /**
100
     * Get OAuth2Client
101
     *
102
     * @return \Kunnu\Dropbox\Authentication\OAuth2Client
103
     */
104
    public function getOAuth2Client()
105
    {
106
        return $this->oAuth2Client;
107
    }
108
109
    /**
110
     * Get the Random String Generator
111
     *
112
     * @return \Kunnu\Dropbox\Security\RandomStringGeneratorInterface
113
     */
114
    public function getRandomStringGenerator()
115
    {
116
        return $this->randomStringGenerator;
117
    }
118
119
    /**
120
     * Get the Persistent Data Store
121
     *
122
     * @return \Kunnu\Dropbox\Store\PersistentDataStoreInterface
123
     */
124
    public function getPersistentDataStore()
125
    {
126
        return $this->persistentDataStore;
127
    }
128
129
    /**
130
     * Get CSRF Token
131
     *
132
     * @return string
133
     */
134
    protected function getCsrfToken()
135
    {
136
        $generator = $this->getRandomStringGenerator();
137
138
        return $generator->generateString(static::CSRF_LENGTH);
139
    }
140
141
    /**
142
     * Get Authorization URL
143
     *
144
     * @param  string $redirectUri Callback URL to redirect to after authorization
145
     * @param  array  $params      Additional Params
146
     * @param  string $urlState  Additional User Provided State Data
147
     *
148
     * @link https://www.dropbox.com/developers/documentation/http/documentation#oauth2-authorize
149
     *
150
     * @return string
151
     */
152
    public function getAuthUrl($redirectUri = null, array $params = [], $urlState = null)
153
    {
154
        // If no redirect URI
155
        // is provided, the
156
        // CSRF validation
157
        // is being handled
158
        // explicitly.
159
        $state = null;
160
161
        // Redirect URI is provided
162
        // thus, CSRF validation
163
        // needs to be handled.
164
        if (!is_null($redirectUri)) {
165
            //Get CSRF State Token
166
            $state = $this->getCsrfToken();
167
168
            //Set the CSRF State Token in the Persistent Data Store
169
            $this->getPersistentDataStore()->set('state', $state);
170
171
            //Additional User Provided State Data
172
            if (!is_null($urlState)) {
173
                $state .= "|";
174
                $state .= $urlState;
175
            }
176
        }
177
178
        // Adding the token access type
179
        if ($this->accessTokenType != null && !array_key_exists('token_access_type', $params))
180
            $params['token_access_type'] = $this->accessTokenType;
181
182
        //Get OAuth2 Authorization URL
183
        return $this->getOAuth2Client()->getAuthorizationUrl($redirectUri, $state, $params);
184
    }
185
186
    /**
187
     * Decode State to get the CSRF Token and the URL State
188
     *
189
     * @param  string $state State
190
     *
191
     * @return array
192
     */
193
    protected function decodeState($state)
194
    {
195
        $csrfToken = $state;
196
        $urlState = null;
197
198
        $splitPos = strpos($state, "|");
199
200
        if ($splitPos !== false) {
201
            $csrfToken = substr($state, 0, $splitPos);
202
            $urlState = substr($state, $splitPos + 1);
203
        }
204
205
        return ['csrfToken' => $csrfToken, 'urlState' => $urlState];
206
    }
207
208
    /**
209
     * Validate CSRF Token
210
     * @param  string $csrfToken CSRF Token
211
     *
212
     * @throws DropboxClientException
213
     *
214
     * @return void
215
     */
216
    protected function validateCSRFToken($csrfToken)
217
    {
218
        $tokenInStore = $this->getPersistentDataStore()->get('state');
219
220
        //Unable to fetch CSRF Token
221
        if (!$tokenInStore || !$csrfToken) {
222
            throw new DropboxClientException("Invalid CSRF Token. Unable to validate CSRF Token.");
223
        }
224
225
        //CSRF Token Mismatch
226
        if ($tokenInStore !== $csrfToken) {
227
            throw new DropboxClientException("Invalid CSRF Token. CSRF Token Mismatch.");
228
        }
229
230
        //Clear the state store
231
        $this->getPersistentDataStore()->clear('state');
232
    }
233
234
    /**
235
     * Get Access Token
236
     *
237
     * @param  string $code        Authorization Code or refresh token (when grantType is DropboxAuthHelper::GRANT_TYPE_REFRESH_TOKEN
238
     * @param  string $state       CSRF & URL State
239
     * @param  string $redirectUri Redirect URI used while getAuthUrl
240
     * @param  string $grantType   Use either DropboxAuthHelper::GRANT_TYPE_AUTHORIZATION_CODE for new token
241
     *                             or DropboxAuthHelper::GRANT_TYPE_REFRESH_TOKEN for refreshing offline token.
242
     *
243
     * @return \Kunnu\Dropbox\Models\AccessToken
244
     */
245
    public function getAccessToken($code, $state = null, $redirectUri = null, $grantType = DropboxAuthHelper::GRANT_TYPE_AUTHORIZATION_CODE)
246
    {
247
        // No state provided
248
        // Should probably be
249
        // handled explicitly
250
        if (!is_null($state)) {
251
            //Decode the State
252
            $state = $this->decodeState($state);
253
254
            //CSRF Token
255
            $csrfToken = $state['csrfToken'];
256
257
            //Set the URL State
258
            $this->urlState = $state['urlState'];
259
260
            //Validate CSRF Token
261
            $this->validateCSRFToken($csrfToken);
262
        }
263
264
        //Fetch Access Token
265
        $accessToken = $this->getOAuth2Client()->getAccessToken($code, $redirectUri, $grantType);
266
267
        //Make and return the model
268
        return new AccessToken($accessToken);
269
    }
270
271
    /**
272
     * Revoke Access Token
273
     *
274
     * @return void
275
     */
276
    public function revokeAccessToken()
277
    {
278
        $this->getOAuth2Client()->revokeAccessToken();
279
    }
280
281
    /**
282
     * Get URL State
283
     *
284
     * @return string
285
     */
286
    public function getUrlState()
287
    {
288
        return $this->urlState;
289
    }
290
291
    /**
292
     * Set the access token type to be either
293
     * DropboxAuthHelper::TOKEN_ACCESS_TYPE_ONLINE (default) or DropboxAuthHelper::TOKEN_ACCESS_TYPE_OFFLINE
294
     *
295
     * @param $tokenType string
296
     */
297
    public function setAccessTokenType($tokenType = self::TOKEN_ACCESS_TYPE_ONLINE) {
298
        if ($tokenType != self::TOKEN_ACCESS_TYPE_ONLINE && $tokenType != self::TOKEN_ACCESS_TYPE_OFFLINE)
299
            throw new \InvalidArgumentException("TokenType parameter must be either DropboxAuthHelper::TOKEN_ACCESS_TYPE_ONLINE or DropboxAuthHelper::TOKEN_ACCESS_TYPE_OFFLINE");
300
301
        $this->accessTokenType = $tokenType;
302
    }
303
}
304