AppIdProvider::validateAccessToken()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 2
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 2
rs 10
1
<?php
2
3
namespace Jampire\OAuth2\Client\Provider;
4
5
use League\OAuth2\Client\OptionProvider\HttpBasicAuthOptionProvider;
6
use League\OAuth2\Client\Provider\AbstractProvider;
7
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
8
use League\OAuth2\Client\Provider\ResourceOwnerInterface;
9
use League\OAuth2\Client\Token\AccessToken;
10
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
11
use Psr\Http\Message\ResponseInterface;
12
use UnexpectedValueException;
13
14
/**
15
 * Class AppIdProvider
16
 *
17
 * @author  Dzianis Kotau <[email protected]>
18
 * @package Jampire\OAuth2\Client\Provider
19
 */
20
class AppIdProvider extends AbstractProvider
21
{
22
    use BearerAuthorizationTrait;
23
24
    const IDP_SAML = 'saml';
25
26
    /** @var string */
27
    protected $baseAuthUri;
28
29
    /** @var string */
30
    protected $tenantId;
31
32
    /** @var string */
33
    protected $idp;
34
35
    /** @var string */
36
    protected $redirectRoute;
37
38
    /**
39
     * AppIdProvider constructor.
40
     *
41
     * @inheritDoc
42
     *
43
     * @author Dzianis Kotau <[email protected]>
44
     * @throws AppIdException
45
     */
46 23
    public function __construct(array $options = [], array $collaborators = [])
47
    {
48 23
        if (empty($options['baseAuthUri']) || empty($options['tenantId'])) {
49 1
            throw new AppIdException('Required fields ("baseAuthUri" or "tenantId") are missing.');
50
        }
51
52 23
        if (empty($options['idp'])) {
53 23
            $options['idp'] = self::IDP_SAML;
54
        }
55
56 23
        $collaborators['optionProvider'] = new HttpBasicAuthOptionProvider();
57
58 23
        parent::__construct($options, $collaborators);
59 23
    }
60
61
    /**
62
     * Returns the base URL for authorizing a client.
63
     *
64
     * Eg. https://oauth.service.com/authorize
65
     *
66
     * @return string
67
     */
68 4
    public function getBaseAuthorizationUrl(): string
69
    {
70 4
        return $this->getBaseAuthUri() . '/' . $this->getTenantId() . '/authorization';
71
    }
72
73
    /**
74
     * Returns the base URL for requesting an access token.
75
     *
76
     * Eg. https://oauth.service.com/token
77
     *
78
     * @param array $params
79
     *
80
     * @return string
81
     */
82 11
    public function getBaseAccessTokenUrl(array $params): string
83
    {
84 11
        return $this->getBaseAuthUri() . '/' . $this->getTenantId() . '/token';
85
    }
86
87
    /**
88
     * Returns the URL for requesting the resource owner's details.
89
     *
90
     * @param AccessToken $token
91
     *
92
     * @return string
93
     */
94 4
    public function getResourceOwnerDetailsUrl(AccessToken $token): string
95
    {
96 4
        return $this->getBaseAuthUri() . '/' . $this->getTenantId() . '/userinfo';
97
    }
98
99
    /**
100
     * Returns the URL for introspecting and validating App ID tokens.
101
     *
102
     * @return string
103
     * @author Dzianis Kotau <[email protected]>
104
     */
105 3
    public function getIntrospectUrl(): string
106
    {
107 3
        return $this->getBaseAuthUri() . '/' . $this->getTenantId() . '/introspect';
108
    }
109
110
    /**
111
     * Returns the URL for revoking App ID tokens.
112
     *
113
     * @return string
114
     * @author Dzianis Kotau <[email protected]>
115
     */
116 3
    public function getRevokeUrl(): string
117
    {
118 3
        return $this->getBaseAuthUri() . '/' . $this->getTenantId() . '/revoke';
119
    }
120
121
    /**
122
     * Introspects and validates App ID token.
123
     *
124
     * @param AccessToken $token
125
     *
126
     * @return bool
127
     * @throws IdentityProviderException
128
     * @author Dzianis Kotau <[email protected]>
129
     */
130 2
    public function validateAccessToken(AccessToken $token): bool
131
    {
132 2
        $body = $this->fetchIntrospect($token);
133
134 1
        return is_array($body) && !empty($body['active']);
135
    }
136
137
    /**
138
     * Revokes refresh token.
139
     *
140
     * @param AccessToken $token
141
     *
142
     * @throws IdentityProviderException
143
     * @return bool True if revoke was successful, false otherwise
144
     * @author Dzianis Kotau <[email protected]>
145
     */
146 2
    public function revokeRefreshToken(AccessToken $token): bool
147
    {
148 2
        $body = $this->fetchRevoke($token);
149
150 1
        return $body === 'OK';
151
    }
152
153
    /**
154
     * @author Dzianis Kotau <[email protected]>
155
     * @return string
156
     */
157 18
    public function getBaseAuthUri(): string
158
    {
159 18
        return $this->baseAuthUri;
160
    }
161
162
    /**
163
     * @author Dzianis Kotau <[email protected]>
164
     * @return string
165
     */
166 18
    public function getTenantId(): string
167
    {
168 18
        return $this->tenantId;
169
    }
170
171
    /**
172
     * @author Dzianis Kotau <[email protected]>
173
     * @return string
174
     */
175 1
    public function getIdp(): string
176
    {
177 1
        return $this->idp;
178
    }
179
180
    /**
181
     * @author Dzianis Kotau <[email protected]>
182
     * @return string|null
183
     */
184 2
    public function getRedirectRoute()
185
    {
186 2
        return $this->redirectRoute;
187
    }
188
189
    /**
190
     * Returns the default scopes used by this provider.
191
     *
192
     * This should only be the scopes that are required to request the details
193
     * of the resource owner, rather than all the available scopes.
194
     *
195
     * @return array
196
     */
197 4
    protected function getDefaultScopes(): array
198
    {
199 4
        return ['openid'];
200
    }
201
202
    /**
203
     * Checks a provider response for errors.
204
     *
205
     * @param ResponseInterface $response
206
     * @param array|string      $data Parsed response data
207
     *
208
     * @return void
209
     * @throws IdentityProviderException
210
     */
211 10
    protected function checkResponse(ResponseInterface $response, $data)
212
    {
213 10
        if (!empty($data['error'])) {
214 1
            $error = $data['error'] . ': ' . $data['error_description'];
215 1
            throw new IdentityProviderException($error, 0, $data);
216
        }
217 9
    }
218
219
    /**
220
     * Generates a resource owner object from a successful resource owner
221
     * details request.
222
     *
223
     * @param array       $response
224
     * @param AccessToken $token
225
     *
226
     * @author Dzianis Kotau <[email protected]>
227
     * @return ResourceOwnerInterface
228
     */
229 3
    protected function createResourceOwner(array $response, AccessToken $token): ResourceOwnerInterface
230
    {
231 3
        return new AppIdResourceOwner($response);
232
    }
233
234
    /**
235
     * Requests introspect details.
236
     *
237
     * @param AccessToken $token
238
     *
239
     * @return array Introspect details ['active' => true/false]
240
     * @throws IdentityProviderException
241
     * @author Dzianis Kotau <[email protected]>
242
     */
243 2
    protected function fetchIntrospect(AccessToken $token): array
244
    {
245 2
        $url = $this->getIntrospectUrl();
246
        $params = [
247 2
            'client_id'     => $this->clientId,
248 2
            'client_secret' => $this->clientSecret,
249 2
            'token'         => $token->getToken(),
250
        ];
251 2
        $options = $this->optionProvider->getAccessTokenOptions(self::METHOD_POST, $params);
252 2
        $request = $this->getRequest(self::METHOD_POST, $url, $options);
253 2
        $response = $this->getParsedResponse($request);
254
255 2
        if (is_array($response) === false) {
256 1
            throw new UnexpectedValueException(
257 1
                'Invalid response received from Authorization Server. Expected JSON.'
258
            );
259
        }
260
261 1
        return $response;
262
    }
263
264
    /**
265
     * @param AccessToken $token
266
     *
267
     * @return string 'OK' if revoke was successful
268
     * @throws IdentityProviderException
269
     * @author Dzianis Kotau <[email protected]>
270
     */
271 2
    protected function fetchRevoke(AccessToken $token): string
272
    {
273 2
        $url = $this->getRevokeUrl();
274
        $params = [
275 2
            'client_id'     => $this->clientId,
276 2
            'client_secret' => $this->clientSecret,
277 2
            'token'         => $token->getToken(),
278
        ];
279 2
        $options = $this->optionProvider->getAccessTokenOptions(self::METHOD_POST, $params);
280 2
        $request = $this->getRequest(self::METHOD_POST, $url, $options);
281 2
        $response = $this->getParsedResponse($request);
282
283 2
        if (is_string($response) === false) {
284 1
            throw new UnexpectedValueException(
285 1
                'Invalid response received from Authorization Server. Expected "OK".'
286
            );
287
        }
288
289 1
        return $response;
290
    }
291
292
    /**
293
     * @inheritDoc
294
     * @author Dzianis Kotau <[email protected]>
295
     */
296 4
    protected function getAuthorizationParameters(array $options): array
297
    {
298 4
        if (empty($options['idp'])) {
299 4
            $options['idp'] = $this->idp = self::IDP_SAML;
300
        }
301
302 4
        return parent::getAuthorizationParameters($options);
303
    }
304
}
305