Passed
Push — master ( 6a5143...1d5c50 )
by Dzianis
02:14
created

AppIdProvider::setTenantId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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