-
Notifications
You must be signed in to change notification settings - Fork 939
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[5.x] Add Slack OpenID provider (#704)
* feat(slack): add separate openid provider * feat(slack): add create openid driver method * feat(slack): specifically specify nickname as `null` * style(slack): remove native return types to keep in style of the entire package * style(slack): remove declare strict types to keep in style of the entire package * style(slack): do not get contents before decoding * test(slack): add tests for Slack OpenID provider * formatting --------- Co-authored-by: Taylor Otwell <[email protected]>
- Loading branch information
1 parent
146052c
commit c7b0193
Showing
3 changed files
with
191 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,66 @@ | ||
<?php | ||
|
||
namespace Laravel\Socialite\Two; | ||
|
||
use GuzzleHttp\RequestOptions; | ||
use Illuminate\Support\Arr; | ||
|
||
class SlackOpenIdProvider extends AbstractProvider implements ProviderInterface | ||
{ | ||
/** | ||
* The scopes being requested. | ||
* | ||
* @var array | ||
*/ | ||
protected $scopes = ['openid', 'email', 'profile']; | ||
|
||
/** | ||
* The separating character for the requested scopes. | ||
* | ||
* @var string | ||
*/ | ||
protected $scopeSeparator = ' '; | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function getAuthUrl($state) | ||
{ | ||
return $this->buildAuthUrlFromBase('https://slack.com/openid/connect/authorize', $state); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function getTokenUrl() | ||
{ | ||
return 'https://slack.com/api/openid.connect.token'; | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function getUserByToken($token) | ||
{ | ||
$response = $this->getHttpClient()->get('https://slack.com/api/openid.connect.userInfo', [ | ||
RequestOptions::HEADERS => ['Authorization' => 'Bearer '.$token], | ||
]); | ||
|
||
return json_decode($response->getBody(), true); | ||
} | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function mapUserToObject(array $user) | ||
{ | ||
return (new User)->setRaw($user)->map([ | ||
'id' => Arr::get($user, 'sub'), | ||
'nickname' => null, | ||
'name' => Arr::get($user, 'name'), | ||
'email' => Arr::get($user, 'email'), | ||
'avatar' => Arr::get($user, 'picture'), | ||
'organization_id' => Arr::get($user, 'https://slack.com/team_id'), | ||
]); | ||
} | ||
} |
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,110 @@ | ||
<?php | ||
|
||
use GuzzleHttp\Client; | ||
use GuzzleHttp\RequestOptions; | ||
use Illuminate\Http\Request; | ||
use Laravel\Socialite\Contracts\User as UserContract; | ||
use Laravel\Socialite\Two\SlackOpenIdProvider; | ||
use Laravel\Socialite\Two\User; | ||
use Mockery as m; | ||
use PHPUnit\Framework\TestCase; | ||
use Psr\Http\Message\ResponseInterface; | ||
use Psr\Http\Message\StreamInterface; | ||
|
||
class SlackOpenIdProviderTest extends TestCase | ||
{ | ||
protected function tearDown(): void | ||
{ | ||
parent::tearDown(); | ||
|
||
m::close(); | ||
} | ||
|
||
public function test_response() | ||
{ | ||
$user = $this->fromResponse([ | ||
'sub' => 'U1Q2W3E4R5T', | ||
'given_name' => 'Maarten', | ||
'picture' => 'https://secure.gravatar.com/avatar/qwerty-123.jpg?s=512', | ||
'name' => 'Maarten Paauw', | ||
'family_name' => 'Paauw', | ||
'email' => '[email protected]', | ||
'https://slack.com/team_id' => 'T0P9O8I7U6Y', | ||
]); | ||
|
||
$this->assertInstanceOf(User::class, $user); | ||
$this->assertSame('U1Q2W3E4R5T', $user->getId()); | ||
$this->assertNull($user->getNickname()); | ||
$this->assertSame('Maarten Paauw', $user->getName()); | ||
$this->assertSame('[email protected]', $user->getEmail()); | ||
$this->assertSame('https://secure.gravatar.com/avatar/qwerty-123.jpg?s=512', $user->getAvatar()); | ||
|
||
$this->assertSame([ | ||
'id' => 'U1Q2W3E4R5T', | ||
'nickname' => null, | ||
'name' => 'Maarten Paauw', | ||
'email' => '[email protected]', | ||
'avatar' => 'https://secure.gravatar.com/avatar/qwerty-123.jpg?s=512', | ||
'organization_id' => 'T0P9O8I7U6Y', | ||
], $user->attributes); | ||
} | ||
|
||
public function test_missing_email_and_avatar() | ||
{ | ||
$user = $this->fromResponse([ | ||
'sub' => 'U1Q2W3E4R5T', | ||
'given_name' => 'Maarten', | ||
'name' => 'Maarten Paauw', | ||
'family_name' => 'Paauw', | ||
'https://slack.com/team_id' => 'T0P9O8I7U6Y', | ||
]); | ||
|
||
$this->assertInstanceOf(User::class, $user); | ||
$this->assertSame('U1Q2W3E4R5T', $user->getId()); | ||
$this->assertNull($user->getNickname()); | ||
$this->assertSame('Maarten Paauw', $user->getName()); | ||
$this->assertNull($user->getEmail()); | ||
$this->assertNull($user->getAvatar()); | ||
|
||
$this->assertSame([ | ||
'id' => 'U1Q2W3E4R5T', | ||
'nickname' => null, | ||
'name' => 'Maarten Paauw', | ||
'email' => null, | ||
'avatar' => null, | ||
'organization_id' => 'T0P9O8I7U6Y', | ||
], $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://slack.com/api/openid.connect.userInfo', [ | ||
RequestOptions::HEADERS => [ | ||
'Authorization' => 'Bearer fake-token', | ||
], | ||
])->andReturns($basicProfileResponse); | ||
|
||
$provider = new SlackOpenIdProvider($request, 'client_id', 'client_secret', 'redirect'); | ||
$provider->stateless(); | ||
$provider->setHttpClient($guzzle); | ||
|
||
return $provider->user(); | ||
} | ||
} |