1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the overtrue/socialite. |
5
|
|
|
* |
6
|
|
|
* (c) overtrue <[email protected]> |
7
|
|
|
* |
8
|
|
|
* This source file is subject to the MIT license that is bundled |
9
|
|
|
* with this source code in the file LICENSE. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Overtrue\Socialite\Providers; |
13
|
|
|
|
14
|
|
|
use Overtrue\Socialite\AccessTokenInterface; |
15
|
|
|
use Overtrue\Socialite\ProviderInterface; |
16
|
|
|
use Overtrue\Socialite\User; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Class LinkedinProvider. |
20
|
|
|
* |
21
|
|
|
* @see https://developer.linkedin.com/docs/oauth2 [Authenticating with OAuth 2.0] |
22
|
|
|
*/ |
23
|
|
|
class LinkedinProvider extends AbstractProvider implements ProviderInterface |
24
|
|
|
{ |
25
|
|
|
/** |
26
|
|
|
* The scopes being requested. |
27
|
|
|
* |
28
|
|
|
* @var array |
29
|
|
|
*/ |
30
|
|
|
protected $scopes = ['r_liteprofile', 'r_emailaddress']; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* {@inheritdoc} |
34
|
|
|
*/ |
35
|
|
|
protected function getAuthUrl($state) |
36
|
|
|
{ |
37
|
|
|
return $this->buildAuthUrlFromBase('https://www.linkedin.com/oauth/v2/authorization', $state); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Get the access token for the given code. |
42
|
|
|
* |
43
|
|
|
* @param string $code |
44
|
|
|
* |
45
|
|
|
* @return \Overtrue\Socialite\AccessToken |
46
|
|
|
*/ |
47
|
|
View Code Duplication |
public function getAccessToken($code) |
|
|
|
|
48
|
|
|
{ |
49
|
|
|
$response = $this->getHttpClient() |
50
|
|
|
->post($this->getTokenUrl(), ['form_params' => $this->getTokenFields($code)]); |
51
|
|
|
|
52
|
|
|
return $this->parseAccessToken($response->getBody()); |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* {@inheritdoc} |
57
|
|
|
*/ |
58
|
|
|
protected function getTokenUrl() |
59
|
|
|
{ |
60
|
|
|
return 'https://www.linkedin.com/oauth/v2/accessToken'; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* Get the POST fields for the token request. |
65
|
|
|
* |
66
|
|
|
* @param string $code |
67
|
|
|
* |
68
|
|
|
* @return array |
69
|
|
|
*/ |
70
|
|
|
protected function getTokenFields($code) |
71
|
|
|
{ |
72
|
|
|
return parent::getTokenFields($code) + ['grant_type' => 'authorization_code']; |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* {@inheritdoc} |
77
|
|
|
*/ |
78
|
|
|
protected function getUserByToken(AccessTokenInterface $token) |
79
|
|
|
{ |
80
|
|
|
$basicProfile = $this->getBasicProfile($token); |
|
|
|
|
81
|
|
|
$emailAddress = $this->getEmailAddress($token); |
|
|
|
|
82
|
|
|
|
83
|
|
|
return array_merge($basicProfile, $emailAddress); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* Get the basic profile fields for the user. |
88
|
|
|
* |
89
|
|
|
* @param string $token |
90
|
|
|
* @return array |
91
|
|
|
*/ |
92
|
|
|
protected function getBasicProfile($token) |
93
|
|
|
{ |
94
|
|
|
$url = 'https://api.linkedin.com/v2/me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))'; |
95
|
|
|
|
96
|
|
|
$response = $this->getHttpClient()->get($url, [ |
97
|
|
|
'headers' => [ |
98
|
|
|
'Authorization' => 'Bearer '.$token, |
99
|
|
|
'X-RestLi-Protocol-Version' => '2.0.0', |
100
|
|
|
], |
101
|
|
|
]); |
102
|
|
|
|
103
|
|
|
return (array) json_decode($response->getBody(), true); |
104
|
|
|
} |
105
|
|
|
/** |
106
|
|
|
* Get the email address for the user. |
107
|
|
|
* |
108
|
|
|
* @param string $token |
109
|
|
|
* @return array |
110
|
|
|
*/ |
111
|
|
View Code Duplication |
protected function getEmailAddress($token) |
|
|
|
|
112
|
|
|
{ |
113
|
|
|
$url = 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))'; |
114
|
|
|
|
115
|
|
|
$response = $this->getHttpClient()->get($url, [ |
116
|
|
|
'headers' => [ |
117
|
|
|
'Authorization' => 'Bearer '.$token, |
118
|
|
|
'X-RestLi-Protocol-Version' => '2.0.0', |
119
|
|
|
], |
120
|
|
|
]); |
121
|
|
|
|
122
|
|
|
return (array) $this->arrayItem(json_decode($response->getBody(), true), 'elements.0.handle~'); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
/** |
126
|
|
|
* {@inheritdoc} |
127
|
|
|
*/ |
128
|
|
|
protected function mapUserToObject(array $user) |
129
|
|
|
{ |
130
|
|
|
$preferredLocale = $this->arrayItem($user, 'firstName.preferredLocale.language').'_'.$this->arrayItem($user, 'firstName.preferredLocale.country'); |
131
|
|
|
$firstName = $this->arrayItem($user, 'firstName.localized.'.$preferredLocale); |
132
|
|
|
$lastName = $this->arrayItem($user, 'lastName.localized.'.$preferredLocale); |
133
|
|
|
$name = $firstName.' '.$lastName; |
134
|
|
|
|
135
|
|
|
$images = (array) $this->arrayItem($user, 'profilePicture.displayImage~.elements', []); |
136
|
|
|
$avatars = array_filter($images, function ($image) { |
137
|
|
|
return $image['data']['com.linkedin.digitalmedia.mediaartifact.StillImage']['storageSize']['width'] === 100; |
138
|
|
|
}); |
139
|
|
|
$avatar = array_shift($avatars); |
140
|
|
|
$originalAvatars = array_filter($images, function ($image) { |
141
|
|
|
return $image['data']['com.linkedin.digitalmedia.mediaartifact.StillImage']['storageSize']['width'] === 800; |
142
|
|
|
}); |
143
|
|
|
$originalAvatar = array_shift($originalAvatars); |
144
|
|
|
|
145
|
|
|
return new User([ |
146
|
|
|
'id' => $this->arrayItem($user, 'id'), |
147
|
|
|
'nickname' => $name, |
148
|
|
|
'name' => $name, |
149
|
|
|
'email' => $this->arrayItem($user, 'emailAddress'), |
150
|
|
|
'avatar' => $this->arrayItem($avatar, 'identifiers.0.identifier'), |
151
|
|
|
'avatar_original' => $this->arrayItem($originalAvatar, 'identifiers.0.identifier'), |
152
|
|
|
]); |
153
|
|
|
} |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Set the user fields to request from LinkedIn. |
157
|
|
|
* |
158
|
|
|
* @param array $fields |
159
|
|
|
* |
160
|
|
|
* @return $this |
161
|
|
|
*/ |
162
|
|
|
public function fields(array $fields) |
163
|
|
|
{ |
164
|
|
|
$this->fields = $fields; |
|
|
|
|
165
|
|
|
|
166
|
|
|
return $this; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* Determine if the provider is operating as stateless. |
171
|
|
|
* |
172
|
|
|
* @return bool |
173
|
|
|
*/ |
174
|
|
|
protected function isStateless() |
175
|
|
|
{ |
176
|
|
|
return true; |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.