Skip to content

Commit d2878b7

Browse files
authored
feat: add OpenID Connect support for Slack marketplace compliance (#1381)
1 parent 5ed0b7c commit d2878b7

File tree

1 file changed

+67
-13
lines changed

1 file changed

+67
-13
lines changed

Provider.php

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,12 @@ protected function getCodeFields($state = null)
3939
{
4040
$fields = parent::getCodeFields($state);
4141

42-
$fields['user_scope'] = $this->formatScopes($this->userScopes, $this->scopeSeparator);
43-
$fields['scope'] = $this->formatScopes($this->scopes, $this->scopeSeparator);
42+
if ($this->shouldUseOpenIdConnect()) {
43+
$fields['scope'] = $this->formatScopes($this->userScopes, ' ');
44+
} else {
45+
$fields['user_scope'] = $this->formatScopes($this->userScopes, $this->scopeSeparator);
46+
$fields['scope'] = $this->formatScopes($this->scopes, $this->scopeSeparator);
47+
}
4448

4549
return $fields;
4650
}
@@ -57,7 +61,13 @@ public function user()
5761

5862
$response = $this->getAccessTokenResponse($this->getCode());
5963

60-
$user = $this->getUserByToken(Arr::get($response, 'authed_user.access_token'));
64+
if ($this->shouldUseOpenIdConnect()) {
65+
$token = Arr::get($response, 'access_token');
66+
} else {
67+
$token = Arr::get($response, 'authed_user.access_token');
68+
}
69+
70+
$user = $this->getUserByToken($token);
6171

6272
/** @var User $userInstance */
6373
$userInstance = $this->userInstance($response, $user);
@@ -68,13 +78,25 @@ public function user()
6878

6979
protected function mapUserToObject(array $user)
7080
{
71-
return (new User)->setRaw($user)->map([
72-
'id' => Arr::get($user, 'user.id'),
73-
'name' => Arr::get($user, 'user.name'),
74-
'email' => Arr::get($user, 'user.email'),
75-
'avatar' => Arr::get($user, 'user.image_512'),
76-
'organization_id' => Arr::get($user, 'team.id'),
77-
]);
81+
if ($this->usesOpenIdScopes()) {
82+
$attributes = [
83+
'id' => Arr::get($user, 'sub'),
84+
'name' => Arr::get($user, 'name'),
85+
'email' => Arr::get($user, 'email'),
86+
'avatar' => Arr::get($user, 'picture'),
87+
'organization_id' => Arr::get($user, 'https://slack.com/team_id'),
88+
];
89+
} else {
90+
$attributes = [
91+
'id' => Arr::get($user, 'user.id'),
92+
'name' => Arr::get($user, 'user.name'),
93+
'email' => Arr::get($user, 'user.email'),
94+
'avatar' => Arr::get($user, 'user.image_512'),
95+
'organization_id' => Arr::get($user, 'team.id'),
96+
];
97+
}
98+
99+
return (new User)->setRaw($user)->map($attributes);
78100
}
79101

80102
public function getAccessTokenResponse($code)
@@ -87,13 +109,23 @@ public function getAccessTokenResponse($code)
87109
return json_decode((string) $response->getBody(), true);
88110
}
89111

90-
public function getAuthUrl($state)
112+
public function getAuthUrl($state): string
91113
{
92-
return $this->buildAuthUrlFromBase('https://slack.com/oauth/v2/authorize', $state);
114+
if ($this->shouldUseOpenIdConnect()) {
115+
$url = 'https://slack.com/openid/connect/authorize';
116+
} else {
117+
$url = 'https://slack.com/oauth/v2/authorize';
118+
}
119+
120+
return $this->buildAuthUrlFromBase($url, $state);
93121
}
94122

95123
protected function getTokenUrl(): string
96124
{
125+
if ($this->shouldUseOpenIdConnect()) {
126+
return 'https://slack.com/api/openid.connect.token';
127+
}
128+
97129
return 'https://slack.com/api/oauth.v2.access';
98130
}
99131

@@ -102,10 +134,32 @@ protected function getTokenUrl(): string
102134
*/
103135
protected function getUserByToken($token)
104136
{
105-
$response = $this->getHttpClient()->get('https://slack.com/api/users.identity', [
137+
if ($this->usesOpenIdScopes()) {
138+
$url = 'https://slack.com/api/openid.connect.userInfo';
139+
} else {
140+
$url = 'https://slack.com/api/users.identity';
141+
}
142+
143+
$response = $this->getHttpClient()->get($url, [
106144
RequestOptions::HEADERS => ['Authorization' => 'Bearer '.$token],
107145
]);
108146

109147
return json_decode((string) $response->getBody(), true);
110148
}
149+
150+
protected function shouldUseOpenIdConnect(): bool
151+
{
152+
return $this->usesOpenIdScopes() && empty($this->scopes);
153+
}
154+
155+
protected function usesOpenIdScopes(): bool
156+
{
157+
$openidScopes = ['openid', 'email', 'profile'];
158+
$identityScopes = ['identity.basic', 'identity.email', 'identity.team', 'identity.avatar'];
159+
160+
$hasOpenIdScopes = ! empty(array_intersect($this->userScopes, $openidScopes));
161+
$hasIdentityScopes = ! empty(array_intersect($this->userScopes, $identityScopes));
162+
163+
return $hasOpenIdScopes && ! $hasIdentityScopes;
164+
}
111165
}

0 commit comments

Comments
 (0)