Completed
Push — master ( 2994aa...19d116 )
by Patrick
08:28
created

DropboxAuthHelper   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 240
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 18
lcom 1
cbo 5
dl 0
loc 240
ccs 0
cts 80
cp 0
rs 10
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A getOAuth2Client() 0 4 1
A getRandomStringGenerator() 0 4 1
A getPersistentDataStore() 0 4 1
A getCsrfToken() 0 6 1
A getAuthUrl() 0 29 3
A decodeState() 0 14 2
A validateCSRFToken() 0 17 4
A getAccessToken() 0 25 2
A revokeAccessToken() 0 4 1
A getUrlState() 0 4 1
1
<?php
2
namespace Dropbox\Authentication;
3
4
use Dropbox\Models\AccessToken;
5
use Dropbox\Exceptions\DropboxClientException;
6
use Dropbox\Store\PersistentDataStoreInterface;
7
use Dropbox\Security\RandomStringGeneratorInterface;
8
9
class DropboxAuthHelper
10
{
11
    /**
12
     * The length of CSRF string
13
     *
14
     * @const int
15
     */
16
    const CSRF_LENGTH = 32;
17
18
    /**
19
     * OAuth2 Client
20
     *
21
     * @var \Dropbox\Authentication\OAuth2Client
22
     */
23
    protected $oAuth2Client;
24
25
    /**
26
     * Random String Generator
27
     *
28
     * @var \Dropbox\Security\RandomStringGeneratorInterface
29
     */
30
    protected $randomStringGenerator;
31
32
    /**
33
     * Persistent Data Store
34
     *
35
     * @var \Dropbox\Store\PersistentDataStoreInterface
36
     */
37
    protected $persistentDataStore;
38
39
    /**
40
     * Additional User Provided State
41
     *
42
     * @var string
43
     */
44
    protected $urlState = null;
45
46
    /**
47
     * Create a new DropboxAuthHelper instance
48
     *
49
     * @param \Dropbox\Authentication\OAuth2Client             $oAuth2Client
50
     * @param \Dropbox\Security\RandomStringGeneratorInterface $randomStringGenerator
51
     * @param \Dropbox\Store\PersistentDataStoreInterface      $persistentDataStore
52
     */
53
    public function __construct(
54
        OAuth2Client $oAuth2Client,
55
        RandomStringGeneratorInterface $randomStringGenerator = null,
56
        PersistentDataStoreInterface $persistentDataStore = null
57
        ) {
58
        $this->oAuth2Client = $oAuth2Client;
59
        $this->randomStringGenerator = $randomStringGenerator;
60
        $this->persistentDataStore = $persistentDataStore;
61
    }
62
63
    /**
64
     * Get OAuth2Client
65
     *
66
     * @return \Dropbox\Authentication\OAuth2Client
67
     */
68
    public function getOAuth2Client()
69
    {
70
        return $this->oAuth2Client;
71
    }
72
73
    /**
74
     * Get the Random String Generator
75
     *
76
     * @return \Dropbox\Security\RandomStringGeneratorInterface
77
     */
78
    public function getRandomStringGenerator()
79
    {
80
        return $this->randomStringGenerator;
81
    }
82
83
    /**
84
     * Get the Persistent Data Store
85
     *
86
     * @return \Dropbox\Store\PersistentDataStoreInterface
87
     */
88
    public function getPersistentDataStore()
89
    {
90
        return $this->persistentDataStore;
91
    }
92
93
    /**
94
     * Get CSRF Token
95
     *
96
     * @return string
97
     */
98
    protected function getCsrfToken()
99
    {
100
        $generator = $this->getRandomStringGenerator();
101
102
        return $generator->generateString(static::CSRF_LENGTH);
103
    }
104
105
    /**
106
     * Get Authorization URL
107
     *
108
     * @param  string $redirectUri Callback URL to redirect to after authorization
109
     * @param  array  $params      Additional Params
110
     * @param  string $urlState  Additional User Provided State Data
111
     *
112
     * @link https://www.dropbox.com/developers/documentation/http/documentation#oauth2-authorize
113
     *
114
     * @return string
115
     */
116
    public function getAuthUrl($redirectUri = null, array $params = [], $urlState = null)
117
    {
118
        // If no redirect URI
119
        // is provided, the
120
        // CSRF validation
121
        // is being handled
122
        // explicitly.
123
        $state = null;
124
125
        // Redirect URI is provided
126
        // thus, CSRF validation
127
        // needs to be handled.
128
        if (!is_null($redirectUri)) {
129
            //Get CSRF State Token
130
            $state = $this->getCsrfToken();
131
132
            //Set the CSRF State Token in the Persistent Data Store
133
            $this->getPersistentDataStore()->set('state', $state);
134
135
            //Additional User Provided State Data
136
            if (!is_null($urlState)) {
137
                $state .= "|";
138
                $state .= $urlState;
139
            }
140
        }
141
142
        //Get OAuth2 Authorization URL
143
        return $this->getOAuth2Client()->getAuthorizationUrl($redirectUri, $state, $params);
144
    }
145
146
    /**
147
     * Decode State to get the CSRF Token and the URL State
148
     *
149
     * @param  string $state State
150
     *
151
     * @return array
152
     */
153
    protected function decodeState($state)
154
    {
155
        $csrfToken = $state;
156
        $urlState = null;
157
158
        $splitPos = strpos($state, "|");
159
160
        if ($splitPos !== false) {
161
            $csrfToken = substr($state, 0, $splitPos);
162
            $urlState = substr($state, $splitPos + 1);
163
        }
164
165
        return ['csrfToken' => $csrfToken, 'urlState' => $urlState];
166
    }
167
168
    /**
169
     * Validate CSRF Token
170
     * @param  string $csrfToken CSRF Token
171
     *
172
     * @throws DropboxClientException
173
     *
174
     * @return void
175
     */
176
    protected function validateCSRFToken($csrfToken)
177
    {
178
        $tokenInStore = $this->getPersistentDataStore()->get('state');
179
180
        //Unable to fetch CSRF Token
181
        if (!$tokenInStore || !$csrfToken) {
182
            throw new DropboxClientException("Invalid CSRF Token. Unable to validate CSRF Token.");
183
        }
184
185
        //CSRF Token Mismatch
186
        if ($tokenInStore !== $csrfToken) {
187
            throw new DropboxClientException("Invalid CSRF Token. CSRF Token Mismatch.");
188
        }
189
190
        //Clear the state store
191
        $this->getPersistentDataStore()->clear('state');
192
    }
193
194
    /**
195
     * Get Access Token
196
     *
197
     * @param  string $code        Authorization Code
198
     * @param  string $state       CSRF & URL State
199
     * @param  string $redirectUri Redirect URI used while getAuthUrl
200
     *
201
     * @return \Dropbox\Models\AccessToken
202
     */
203
    public function getAccessToken($code, $state = null, $redirectUri = null)
204
    {
205
        // No state provided
206
        // Should probably be
207
        // handled explicitly
208
        if (!is_null($state)) {
209
            //Decode the State
210
            $state = $this->decodeState($state);
211
212
            //CSRF Token
213
            $csrfToken = $state['csrfToken'];
214
215
            //Set the URL State
216
            $this->urlState = $state['urlState'];
217
218
            //Validate CSRF Token
219
            $this->validateCSRFToken($csrfToken);
220
        }
221
222
        //Fetch Access Token
223
        $accessToken = $this->getOAuth2Client()->getAccessToken($code, $redirectUri);
224
225
        //Make and return the model
226
        return new AccessToken($accessToken);
227
    }
228
229
    /**
230
     * Revoke Access Token
231
     *
232
     * @return void
233
     */
234
    public function revokeAccessToken()
235
    {
236
        $this->getOAuth2Client()->revokeAccessToken();
237
    }
238
239
    /**
240
     * Get URL State
241
     *
242
     * @return string
243
     */
244
    public function getUrlState()
245
    {
246
        return $this->urlState;
247
    }
248
}
249