-
Notifications
You must be signed in to change notification settings - Fork 940
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[5.x] Adds
LinkedInOpenId
provider (#662)
* Adds `LinkedInOpenId` provider * Apply fixes from StyleCI --------- Co-authored-by: StyleCI Bot <[email protected]>
- Loading branch information
1 parent
1e81f39
commit 14acfa3
Showing
3 changed files
with
217 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
<?php | ||
|
||
namespace Laravel\Socialite\Two; | ||
|
||
use GuzzleHttp\RequestOptions; | ||
|
||
class LinkedInOpenIdProvider extends AbstractProvider implements ProviderInterface | ||
{ | ||
/** | ||
* The scopes being requested. | ||
* | ||
* @var array | ||
*/ | ||
protected $scopes = ['openid', 'profile', 'email']; | ||
|
||
/** | ||
* The separating character for the requested scopes. | ||
* | ||
* @var string | ||
*/ | ||
protected $scopeSeparator = ' '; | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function getAuthUrl($state) | ||
{ | ||
return $this->buildAuthUrlFromBase('https://www.linkedin.com/oauth/v2/authorization', $state); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function getTokenUrl() | ||
{ | ||
return 'https://www.linkedin.com/oauth/v2/accessToken'; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function getUserByToken($token) | ||
{ | ||
return $this->getBasicProfile($token); | ||
} | ||
|
||
/** | ||
* Get the basic profile fields for the user. | ||
* | ||
* @param string $token | ||
* @return array | ||
*/ | ||
protected function getBasicProfile($token) | ||
{ | ||
$response = $this->getHttpClient()->get('https://api.linkedin.com/v2/userinfo', [ | ||
RequestOptions::HEADERS => [ | ||
'Authorization' => 'Bearer '.$token, | ||
'X-RestLi-Protocol-Version' => '2.0.0', | ||
], | ||
RequestOptions::QUERY => [ | ||
'projection' => '(sub,email,name,given_name,family_name,picture)', | ||
], | ||
]); | ||
|
||
return (array) json_decode($response->getBody(), true); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function mapUserToObject(array $user) | ||
{ | ||
return (new User)->setRaw($user)->map([ | ||
'id' => $user['sub'], | ||
'nickname' => null, | ||
'name' => $user['name'], | ||
'first_name' => $user['given_name'], | ||
'last_name' => $user['family_name'], | ||
'email' => $user['email'] ?? null, | ||
'avatar' => $user['picture'] ?? null, | ||
'avatar_original' => $user['picture'] ?? null, | ||
]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
<?php | ||
|
||
namespace Laravel\Socialite\Tests; | ||
|
||
use GuzzleHttp\Client; | ||
use GuzzleHttp\RequestOptions; | ||
use Illuminate\Http\Request; | ||
use Laravel\Socialite\Contracts\User as UserContract; | ||
use Laravel\Socialite\Two\LinkedInOpenIdProvider; | ||
use Laravel\Socialite\Two\User; | ||
use Mockery as m; | ||
use PHPUnit\Framework\TestCase; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Http\Message\StreamInterface; | ||
|
||
class LinkedInOpenIdProviderTest extends TestCase | ||
{ | ||
protected function tearDown(): void | ||
{ | ||
parent::tearDown(); | ||
|
||
m::close(); | ||
} | ||
|
||
public function test_response() | ||
{ | ||
$user = $this->fromResponse([ | ||
'sub' => 'asdfgh', | ||
'given_name' => 'Nuno', | ||
'picture' => 'https://media.licdn.com/dms/image/D4D03AQmZFgJNqeNNk', | ||
'name' => 'Nuno Maduro', | ||
'family_name' => 'Maduro', | ||
'email' => '[email protected]', | ||
]); | ||
|
||
$this->assertInstanceOf(User::class, $user); | ||
$this->assertSame('asdfgh', $user->getId()); | ||
$this->assertNull($user->getNickname()); | ||
$this->assertSame('Nuno Maduro', $user->getName()); | ||
$this->assertSame('[email protected]', $user->getEmail()); | ||
$this->assertSame('https://media.licdn.com/dms/image/D4D03AQmZFgJNqeNNk', $user->getAvatar()); | ||
|
||
$this->assertSame([ | ||
'id' => 'asdfgh', | ||
'nickname' => null, | ||
'name' => 'Nuno Maduro', | ||
'first_name' => 'Nuno', | ||
'last_name' => 'Maduro', | ||
'email' => '[email protected]', | ||
'avatar' => 'https://media.licdn.com/dms/image/D4D03AQmZFgJNqeNNk', | ||
'avatar_original' => 'https://media.licdn.com/dms/image/D4D03AQmZFgJNqeNNk', | ||
], $user->attributes); | ||
} | ||
|
||
public function test_missing_email_and_avatar() | ||
{ | ||
$user = $this->fromResponse([ | ||
'sub' => 'asdfgh', | ||
'given_name' => 'Nuno', | ||
'name' => 'Nuno Maduro', | ||
'family_name' => 'Maduro', | ||
]); | ||
|
||
$this->assertInstanceOf(User::class, $user); | ||
$this->assertSame('asdfgh', $user->getId()); | ||
$this->assertNull($user->getNickname()); | ||
$this->assertSame('Nuno Maduro', $user->getName()); | ||
$this->assertNull($user->getEmail()); | ||
$this->assertNull($user->getAvatar()); | ||
|
||
$this->assertSame([ | ||
'id' => 'asdfgh', | ||
'nickname' => null, | ||
'name' => 'Nuno Maduro', | ||
'first_name' => 'Nuno', | ||
'last_name' => 'Maduro', | ||
'email' => null, | ||
'avatar' => null, | ||
'avatar_original' => null, | ||
], $user->attributes); | ||
} | ||
|
||
protected function fromResponse(array $response): UserContract | ||
{ | ||
$request = m::mock(Request::class); | ||
$request->allows('input')->with('code')->andReturns('fake-code'); | ||
|
||
$stream = m::mock(StreamInterface::class); | ||
$stream->allows('__toString')->andReturns(json_encode(['access_token' => 'fake-token'])); | ||
|
||
$accessTokenResponse = m::mock(ResponseInterface::class); | ||
$accessTokenResponse->allows('getBody')->andReturns($stream); | ||
|
||
$basicProfileStream = m::mock(StreamInterface::class); | ||
$basicProfileStream->allows('__toString')->andReturns(json_encode($response)); | ||
|
||
$basicProfileResponse = m::mock(ResponseInterface::class); | ||
$basicProfileResponse->allows('getBody')->andReturns($basicProfileStream); | ||
|
||
$guzzle = m::mock(Client::class); | ||
$guzzle->expects('post')->andReturns($accessTokenResponse); | ||
$guzzle->allows('get')->with('https://api.linkedin.com/v2/userinfo', [ | ||
RequestOptions::HEADERS => [ | ||
'Authorization' => 'Bearer fake-token', | ||
'X-RestLi-Protocol-Version' => '2.0.0', | ||
], | ||
RequestOptions::QUERY => [ | ||
'projection' => '(sub,email,name,given_name,family_name,picture)', | ||
], | ||
])->andReturns($basicProfileResponse); | ||
|
||
$provider = new LinkedInOpenIdProvider($request, 'client_id', 'client_secret', 'redirect'); | ||
$provider->stateless(); | ||
$provider->setHttpClient($guzzle); | ||
|
||
return $provider->user(); | ||
} | ||
} |