ORCID   A
last analyzed

Complexity

Total Complexity 25

Size/Duplication

Total Lines 204
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 25
eloc 68
c 3
b 0
f 0
dl 0
loc 204
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getScopeSeparator() 0 3 1
A getDefaultScopes() 0 3 1
A getBaseAccessTokenUrl() 0 5 2
A getDefaultHeaders() 0 3 1
A getBaseAuthorizationUrl() 0 5 2
B checkResponse() 0 26 7
A getResourceOwnerDetailsUrl() 0 8 3
A createResourceOwner() 0 22 5
A getAccessToken() 0 14 1
A getAccessTokenRequest() 0 9 2
1
<?php
2
3
/**
4
 * This file is part of the cilogon/oauth2-orcid library.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 *
9
 * @author    Terry Fleury <[email protected]>
10
 * @copyright 2021 University of Illinois
11
 * @license   https://opensource.org/licenses/NCSA NCSA
12
 * @link      https://github.com/cilogon/oauth2-orcid GitHub
13
 */
14
15
namespace CILogon\OAuth2\Client\Provider;
16
17
use League\OAuth2\Client\Provider\AbstractProvider;
18
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
19
use League\OAuth2\Client\Token\AccessToken;
20
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
21
use Psr\Http\Message\ResponseInterface;
22
use Lcobucci\JWT\Parser;
23
24
class ORCID extends AbstractProvider
25
{
26
    use BearerAuthorizationTrait;
27
28
    /**
29
     * @var string Key used in the access token response to identify the resource owner.
30
     */
31
    public const ACCESS_TOKEN_RESOURCE_OWNER_ID = 'orcid';
32
33
    /**
34
     * @var string
35
     */
36
    public $sandbox = false;
37
38
    /**
39
     * @var string
40
     */
41
    public $member = false;
42
43
    /**
44
     * Returns the base URL for authorizing a client.
45
     *
46
     * @return string
47
     */
48
    public function getBaseAuthorizationUrl()
49
    {
50
        return 'https://' .
51
            (($this->sandbox) ? 'sandbox.' : '') .
52
            'orcid.org/oauth/authorize';
53
    }
54
55
    /**
56
     * Returns the base URL for requesting an access token.
57
     *
58
     * @param array $params
59
     * @return string
60
     */
61
    public function getBaseAccessTokenUrl(array $params)
62
    {
63
        return 'https://' .
64
            (($this->sandbox) ? 'sandbox.' : '') .
65
            'orcid.org/oauth/token';
66
    }
67
68
    /**
69
     * Returns the URL for requesting the resource owner's details.
70
     *
71
     * @param AccessToken $token
72
     *
73
     * @return string
74
     */
75
    public function getResourceOwnerDetailsUrl(AccessToken $token)
76
    {
77
        return 'https://' .
78
            (($this->member) ? 'api.' : 'pub.') .
79
            (($this->sandbox) ? 'sandbox.' : '') .
80
            'orcid.org/v2.0/' .
81
            $token->getResourceOwnerId() .
82
            '/record';
83
    }
84
85
    /**
86
     * Returns the default scopes used by this provider.
87
     *
88
     * This should only be the scopes that are required to request the details
89
     * of the resource owner, rather than all the available scopes.
90
     *
91
     * @return array
92
     */
93
    protected function getDefaultScopes()
94
    {
95
        return [ '/authenticate' ];
96
    }
97
98
    /**
99
     * Returns the string that should be used to separate scopes when building
100
     * the URL for requesting an access token.
101
     *
102
     * @return string Scope separator, defaults to ','
103
     */
104
    protected function getScopeSeparator()
105
    {
106
        return ' ';
107
    }
108
109
    /**
110
     * Returns the default headers used by this provider.
111
     *
112
     * Typically this is used to set 'Accept' or 'Content-Type' headers.
113
     *
114
     * @return array
115
     */
116
    protected function getDefaultHeaders()
117
    {
118
        return [ 'Accept' => 'application/json' ];
119
    }
120
121
122
    /**
123
     * Check a provider response for errors.
124
     *
125
     * @throws IdentityProviderException
126
     * @param  ResponseInterface $response
127
     * @param  string $data Parsed response data
128
     * @return void
129
     */
130
    protected function checkResponse(ResponseInterface $response, $data)
131
    {
132
        $error = false;
133
        $errcode = 0;
134
        $errmsg = '';
135
136
        if (!empty($data['error'])) {
137
            $error = true;
138
            $errmsg = $data['error'];
139
            if (!empty($data['error_description'])) {
140
                $errmsg .= ': ' . $data['error_description'];
141
            }
142
        } elseif (!empty($data['error-code'])) {
143
            $error = true;
144
            $errcode = (int)$data['error-code'];
145
            if (!empty($data['developer-message'])) {
146
                $errmsg = $data['developer-message'];
147
            }
148
        } elseif ($response->getStatusCode() >= 400) {
149
            $error = true;
150
            $errcode = $response->getStatusCode();
151
            $errmsg = $response->getReasonPhrase();
152
        }
153
154
        if ($error) {
155
            throw new IdentityProviderException($errmsg, $errcode, $data);
156
        }
157
    }
158
159
    /**
160
     * Generate a user object from a successful user details request.
161
     *
162
     * @param object $response
163
     * @param AccessToken $token
164
     * @return ORCIDResourceOwner
165
     */
166
    protected function createResourceOwner(array $response, AccessToken $token)
167
    {
168
        // Attempt to extract 'amr' (AuthnMethodRef) from the id_token
169
        // and add it to the response. Note that 'amr' is available only
170
        // when using the Member API.
171
        $values = $token->getValues();
172
        if (array_key_exists('id_token', $values)) {
173
            $jwt = $values['id_token'];
174
            try {
175
                $read = (new Parser())->parse((string) $jwt);
176
                if ($read->hasClaim('amr')) {
0 ignored issues
show
Deprecated Code introduced by
The function Lcobucci\JWT\Token::hasClaim() has been deprecated: This method has been removed from the interface in v4.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

176
                if (/** @scrutinizer ignore-deprecated */ $read->hasClaim('amr')) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
177
                    $amr = $read->getClaim('amr');
0 ignored issues
show
Deprecated Code introduced by
The function Lcobucci\JWT\Token::getClaim() has been deprecated: This method has been removed from the interface in v4.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

177
                    $amr = /** @scrutinizer ignore-deprecated */ $read->getClaim('amr');

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
178
                    if (strlen($amr) > 0) {
179
                        $response['amr'] = $amr;
180
                    }
181
                }
182
            } catch (\Exception $e) {
183
                // Do not set 'amr' in case of errors.
184
            }
185
        }
186
187
        return new ORCIDResourceOwner($response);
188
    }
189
190
    /**
191
     * Returns a prepared request for requesting an access token.
192
     *
193
     * @param array $params Query string parameters
194
     * @return \GuzzleHttp\Psr7\Request
195
     */
196
    protected function getAccessTokenRequest(array $params, $accesstoken = null)
197
    {
198
        $method  = $this->getAccessTokenMethod();
199
        $url     = $this->getAccessTokenUrl($params);
200
        $options = $this->optionProvider->getAccessTokenOptions($method, $params);
201
        if (is_null($accesstoken)) {
202
            return $this->getRequest($method, $url, $options);
203
        } else {
204
            return $this->getAuthenticatedRequest($method, $url, $accesstoken, $options);
205
        }
206
    }
207
    /**
208
     * Requests an access token using a specified grant and option set.
209
     *
210
     * @param  mixed $grant
211
     * @param  array $options
212
     * @return AccessToken
213
     */
214
    public function getAccessToken($grant, array $options = [], $accesstoken = null)
215
    {
216
        $grant = $this->verifyGrant($grant);
217
        $params = [
218
            'client_id'     => $this->clientId,
219
            'client_secret' => $this->clientSecret,
220
            'redirect_uri'  => $this->redirectUri,
221
        ];
222
        $params   = $grant->prepareRequestParameters($params, $options);
223
        $request  = $this->getAccessTokenRequest($params, $accesstoken);
224
        $response = $this->getParsedResponse($request);
225
        $prepared = $this->prepareAccessTokenResponse($response);
226
        $token    = $this->createAccessToken($prepared, $grant);
227
        return $token;
228
    }
229
}
230