forked from SocialiteProviders/Orcid
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathProvider.php
216 lines (186 loc) · 5.98 KB
/
Provider.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
<?php
namespace SocialiteProviders\Orcid;
use Illuminate\Support\Arr;
use Laravel\Socialite\Two\InvalidStateException;
use SocialiteProviders\Manager\OAuth2\AbstractProvider;
use SocialiteProviders\Manager\OAuth2\User;
class Provider extends AbstractProvider
{
/**
* Unique Provider Identifier.
*/
public const IDENTIFIER = 'ORCID';
/**
* Base URL for ORCID Sandpit Environment.
*/
public const sandboxURL = 'https://sandbox.orcid.org/';
/**
* Base URL for ORCID Production Environment.
*/
public const productionURL = 'https://orcid.org/';
/**
* Profile Data URL for ORCID Sandpit Environment.
*/
public const sandboxProfileURL = 'https://pub.sandbox.orcid.org/v2.1/';
/**
* Profile Data URL for ORCID Production Environment.
*/
public const productionProfileURL = 'https://pub.orcid.org/v2.1/';
/**
* The scopes being requested.
* Others include: '/activities/update','/person/update'.
*
* You can customise the scopes when invoking the ORCID Socialite provider
* if this needs to change
*
* @var array
*/
protected $scopes = ['/authenticate', '/read-limited'];
/**
* The separating character for the requested scopes.
*
* @var string
*/
protected $scopeSeparator = ' ';
/**
* Tests whether we are integrating to the ORCID Sandbox or Production environments
* Change the value of ORCID_ENVIRONMENT in your .env to switch.
*
* @return bool
*/
protected function useSandbox()
{
return env('ORCID_ENVIRONMENT') !== 'production';
}
/**
* Concatenate a base URL for ORCID oAuth requests.
*
* @return string
*/
protected function baseUrl($path)
{
return ($this->useSandbox() ? self::sandboxURL : self::productionURL).$path;
}
/**
* Concatenate a base URL for ORCID profile data requests.
*
* @return string
*/
protected function profileUrl($path)
{
return ($this->useSandbox() ? self::sandboxProfileURL : self::productionProfileURL).$path;
}
/**
* {@inheritdoc}
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase($this->baseUrl('oauth/authorize'), $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return $this->baseUrl('oauth/token');
}
/**
* Assuming authentication and token generation succeeeds, return a User object.
*
* @return $user Laravel\Socialite\Two\User
*/
public function user()
{
if ($this->hasInvalidState()) {
throw new InvalidStateException();
}
$response = $this->getAccessTokenResponse($this->getCode());
$user = $this->mapUserToObject($this->getUserByToken(
$response
));
$token = Arr::get($response, 'access_token');
return $user->setToken($token)
->setRefreshToken(Arr::get($response, 'refresh_token'))
->setExpiresIn(Arr::get($response, 'expires_in'));
}
/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
{
$orcid = Arr::get($token, 'orcid');
$token = Arr::get($token, 'access_token');
$userUrl = $this->profileUrl("{$orcid}/record");
$response = $this->getHttpClient()
->get(
$userUrl,
['headers' => ['Content-Type' => 'application/vnd.orcid+xml',
'Accept' => 'application/json',
'Authorization type' => 'Bearer',
'Access token' => $token, ],
]
);
$user = json_decode($response->getBody()->getContents(), true);
$user['email'] = $this->getEmail($user);
return $user;
}
/**
* Get the email for the given access token.
*
* NOTE: this doesn't alway succeed becuase ORCID gives users the option to keep their email private
* If your app design relies on fetching the user email from ORCID, you should consider checking
* that it exists in your LoginController logic.
*
* @param string $token
*
* @return string|null
*/
protected function getEmail($user)
{
foreach ($user['person']['emails']['email'] as $m) {
if ($m['primary'] === true && $m['verified'] === true) {
return $m['email'];
}
}
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(array $user)
{
return (new User())->setRaw($user)->map([
env('ORCID_UID_FIELDNAME', 'id') => $user['orcid-identifier']['path'],
'nickname' => $user['person']['name']['given-names']['value'],
'name' => sprintf('%s %s', $user['person']['name']['given-names']['value'], $user['person']['name']['family-name']['value']),
'email' => Arr::get($user, 'email'),
]);
}
/**
* Get the access token for the given code.
*
* @param string $code
*
* @return string
*/
public function getAccessToken($code)
{
$s = $this->scopes[0];
$data = "client_id={$this->clientId}&client_secret={$this->clientSecret}&grant_type=client_credentials&scope={$s}";
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
'headers' => ['Accept' => 'application/json', 'Content-Type' => 'application/x-www-form-urlencoded'],
'body' => $data,
]);
return json_decode($response->getBody()->getContents(), true)['access_token'];
}
/**
* Get the POST fields for the token request.
*
* @param string $code
*
* @return array
*/
protected function getTokenFields($code)
{
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code', 'orcid' => 'orcid'];
}
}