Skip to content

Commit

Permalink
fix: slack bot provider (#1136)
Browse files Browse the repository at this point in the history
  • Loading branch information
atymic committed Jan 5, 2024
1 parent c8b8536 commit b156cf0
Showing 1 changed file with 64 additions and 94 deletions.
158 changes: 64 additions & 94 deletions Provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,138 +2,108 @@

namespace SocialiteProviders\Slack;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\RequestOptions;
use Illuminate\Support\Arr;
use Psr\Http\Message\ResponseInterface;
use Laravel\Socialite\Two\InvalidStateException;
use SocialiteProviders\Manager\OAuth2\AbstractProvider;
use SocialiteProviders\Manager\OAuth2\User;

class Provider extends AbstractProvider
{
public const IDENTIFIER = 'SLACK';

/**
* {@inheritdoc}
*/
public function getScopes()
protected $scopes = [];

protected array $userScopes = ['identity.basic', 'identity.email', 'identity.team', 'identity.avatar'];

public function scopes($scopes)
{
if (count($this->scopes) > 0) {
return $this->scopes;
}
$this->userScopes = array_unique(array_merge($this->userScopes, (array) $scopes));

// Provide some default scopes if the user didn't define some.
// See: https://github.com/SocialiteProviders/Providers/pull/53
return ['identity.basic', 'identity.email', 'identity.team', 'identity.avatar'];
return $this;
}

/**
* Middleware that throws exceptions for non successful slack api calls
* "http_error" request option is set to true.
*
* @return callable Returns a function that accepts the next handler.
*/
private function getSlackApiErrorMiddleware()
public function botScopes($scopes)
{
return fn (callable $handler) => function ($request, array $options) use ($handler) {
if (empty($options['http_errors'])) {
return $handler($request, $options);
}

return $handler($request, $options)->then(
function (ResponseInterface $response) use ($request) {
$body = json_decode((string) $response->getBody(), true);
$response->getBody()->rewind();

if ($body['ok']) {
return $response;
}

throw RequestException::create($request, $response);
}
);
};
$this->scopes = array_unique(array_merge($this->scopes, (array) $scopes));

return $this;
}

/**
* {@inheritdoc}
*/
protected function getHttpClient()
protected function getCodeFields($state = null)
{
$handler = HandlerStack::create();
$handler->push($this->getSlackApiErrorMiddleware(), 'slack_api_errors');
$fields = parent::getCodeFields($state);

if ($this->httpClient === null) {
$this->httpClient = new Client(['handler' => $handler]);
$fields['user_scope'] = $this->formatScopes($this->userScopes, $this->scopeSeparator);
$fields['scope'] = $this->formatScopes($this->scopes, $this->scopeSeparator);

return $fields;
}

public function user()
{
if ($this->user) {
return $this->user;
}

if ($this->hasInvalidState()) {
throw new InvalidStateException;
}

return $this->httpClient;
$response = $this->getAccessTokenResponse($this->getCode());

$user = $this->getUserByToken(Arr::get($response, 'authed_user.access_token'));

/** @var User $userInstance */
$userInstance = $this->userInstance($response, $user);
$userInstance->setAccessTokenResponseBody($response);

return $userInstance;
}

/**
* {@inheritdoc}
*/
protected function getAuthUrl($state)
protected function mapUserToObject(array $user)
{
return $this->buildAuthUrlFromBase(
'https://slack.com/oauth/authorize',
$state
);
return (new User())->setRaw($user)->map([
'id' => Arr::get($user, 'user.id'),
'name' => Arr::get($user, 'user.name'),
'email' => Arr::get($user, 'user.email'),
'avatar' => Arr::get($user, 'user.image_512'),
'organization_id' => Arr::get($user, 'team.id'),
]);
}

/**
* {@inheritdoc}
*/
protected function getTokenUrl()
public function getAccessTokenResponse($code)
{
return 'https://slack.com/api/oauth.access';
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
RequestOptions::HEADERS => $this->getTokenHeaders($code),
RequestOptions::FORM_PARAMS => $this->getTokenFields($code),
]);

return json_decode($response->getBody(), true);
}

public function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase('https://slack.com/oauth/v2/authorize', $state);
}

/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
protected function getTokenUrl()
{
try {
$response = $this->getHttpClient()->get(
'https://slack.com/api/users.identity',
[
RequestOptions::HEADERS => [
'Authorization' => 'Bearer '.$token,
],
]
);
} catch (RequestException $exception) {
// Getting user informations requires the "identity.*" scopes, however we might want to not add them to the
// scope list for various reasons. Instead of throwing an exception on this error, we return an empty user.

if ($exception->hasResponse()) {
$data = json_decode((string) $exception->getResponse()->getBody(), true);

if (Arr::get($data, 'error') === 'missing_scope') {
return [];
}
}

throw $exception;
}

return json_decode((string) $response->getBody(), true);
return 'https://slack.com/api/oauth.v2.access';
}

/**
* {@inheritdoc}
*/
protected function mapUserToObject(array $user)
protected function getUserByToken($token)
{
return (new User())->setRaw($user)->map([
'id' => Arr::get($user, 'user.id'),
'name' => Arr::get($user, 'user.name'),
'email' => Arr::get($user, 'user.email'),
'avatar' => Arr::get($user, 'user.image_192'),
'organization_id' => Arr::get($user, 'team.id'),
$response = $this->getHttpClient()->get('https://slack.com/api/users.identity', [
RequestOptions::HEADERS => ['Authorization' => 'Bearer ' . $token],
]);

return json_decode($response->getBody(), true);
}
}

0 comments on commit b156cf0

Please sign in to comment.