Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/auth/provider/PhabricatorOAuth2AuthProvider.php
12256 views
1
<?php
2
3
abstract class PhabricatorOAuth2AuthProvider
4
extends PhabricatorOAuthAuthProvider {
5
6
const PROPERTY_APP_ID = 'oauth:app:id';
7
const PROPERTY_APP_SECRET = 'oauth:app:secret';
8
9
protected function getIDKey() {
10
return self::PROPERTY_APP_ID;
11
}
12
13
protected function getSecretKey() {
14
return self::PROPERTY_APP_SECRET;
15
}
16
17
18
protected function configureAdapter(PhutilOAuthAuthAdapter $adapter) {
19
$config = $this->getProviderConfig();
20
$adapter->setClientID($config->getProperty(self::PROPERTY_APP_ID));
21
$adapter->setClientSecret(
22
new PhutilOpaqueEnvelope(
23
$config->getProperty(self::PROPERTY_APP_SECRET)));
24
$adapter->setRedirectURI(PhabricatorEnv::getURI($this->getLoginURI()));
25
return $adapter;
26
}
27
28
protected function renderLoginForm(AphrontRequest $request, $mode) {
29
$adapter = $this->getAdapter();
30
$adapter->setState($this->getAuthCSRFCode($request));
31
32
$scope = $request->getStr('scope');
33
if ($scope) {
34
$adapter->setScope($scope);
35
}
36
37
$attributes = array(
38
'method' => 'GET',
39
'uri' => $adapter->getAuthenticateURI(),
40
);
41
42
return $this->renderStandardLoginButton($request, $mode, $attributes);
43
}
44
45
public function processLoginRequest(
46
PhabricatorAuthLoginController $controller) {
47
48
$request = $controller->getRequest();
49
$adapter = $this->getAdapter();
50
$account = null;
51
$response = null;
52
53
$error = $request->getStr('error');
54
if ($error) {
55
$response = $controller->buildProviderErrorResponse(
56
$this,
57
pht(
58
'The OAuth provider returned an error: %s',
59
$error));
60
61
return array($account, $response);
62
}
63
64
$this->verifyAuthCSRFCode($request, $request->getStr('state'));
65
66
$code = $request->getStr('code');
67
if (!strlen($code)) {
68
$response = $controller->buildProviderErrorResponse(
69
$this,
70
pht(
71
'The OAuth provider did not return a "code" parameter in its '.
72
'response.'));
73
74
return array($account, $response);
75
}
76
77
$adapter->setCode($code);
78
79
// NOTE: As a side effect, this will cause the OAuth adapter to request
80
// an access token.
81
82
try {
83
$identifiers = $adapter->getAccountIdentifiers();
84
} catch (Exception $ex) {
85
// TODO: Handle this in a more user-friendly way.
86
throw $ex;
87
}
88
89
if (!$identifiers) {
90
$response = $controller->buildProviderErrorResponse(
91
$this,
92
pht(
93
'The OAuth provider failed to retrieve an account ID.'));
94
95
return array($account, $response);
96
}
97
98
$account = $this->newExternalAccountForIdentifiers($identifiers);
99
100
return array($account, $response);
101
}
102
103
public function processEditForm(
104
AphrontRequest $request,
105
array $values) {
106
107
return $this->processOAuthEditForm(
108
$request,
109
$values,
110
pht('Application ID is required.'),
111
pht('Application secret is required.'));
112
}
113
114
public function extendEditForm(
115
AphrontRequest $request,
116
AphrontFormView $form,
117
array $values,
118
array $issues) {
119
120
return $this->extendOAuthEditForm(
121
$request,
122
$form,
123
$values,
124
$issues,
125
pht('OAuth App ID'),
126
pht('OAuth App Secret'));
127
}
128
129
public function renderConfigPropertyTransactionTitle(
130
PhabricatorAuthProviderConfigTransaction $xaction) {
131
132
$author_phid = $xaction->getAuthorPHID();
133
$old = $xaction->getOldValue();
134
$new = $xaction->getNewValue();
135
$key = $xaction->getMetadataValue(
136
PhabricatorAuthProviderConfigTransaction::PROPERTY_KEY);
137
138
switch ($key) {
139
case self::PROPERTY_APP_ID:
140
if (strlen($old)) {
141
return pht(
142
'%s updated the OAuth application ID for this provider from '.
143
'"%s" to "%s".',
144
$xaction->renderHandleLink($author_phid),
145
$old,
146
$new);
147
} else {
148
return pht(
149
'%s set the OAuth application ID for this provider to '.
150
'"%s".',
151
$xaction->renderHandleLink($author_phid),
152
$new);
153
}
154
case self::PROPERTY_APP_SECRET:
155
if (strlen($old)) {
156
return pht(
157
'%s updated the OAuth application secret for this provider.',
158
$xaction->renderHandleLink($author_phid));
159
} else {
160
return pht(
161
'%s set the OAuth application secret for this provider.',
162
$xaction->renderHandleLink($author_phid));
163
}
164
case self::PROPERTY_NOTE:
165
if (strlen($old)) {
166
return pht(
167
'%s updated the OAuth application notes for this provider.',
168
$xaction->renderHandleLink($author_phid));
169
} else {
170
return pht(
171
'%s set the OAuth application notes for this provider.',
172
$xaction->renderHandleLink($author_phid));
173
}
174
175
}
176
177
return parent::renderConfigPropertyTransactionTitle($xaction);
178
}
179
180
protected function synchronizeOAuthAccount(
181
PhabricatorExternalAccount $account) {
182
$adapter = $this->getAdapter();
183
184
$oauth_token = $adapter->getAccessToken();
185
$account->setProperty('oauth.token.access', $oauth_token);
186
187
if ($adapter->supportsTokenRefresh()) {
188
$refresh_token = $adapter->getRefreshToken();
189
$account->setProperty('oauth.token.refresh', $refresh_token);
190
} else {
191
$account->setProperty('oauth.token.refresh', null);
192
}
193
194
$expires = $adapter->getAccessTokenExpires();
195
$account->setProperty('oauth.token.access.expires', $expires);
196
}
197
198
public function getOAuthAccessToken(
199
PhabricatorExternalAccount $account,
200
$force_refresh = false) {
201
202
if ($account->getProviderConfigPHID() !== $this->getProviderConfigPHID()) {
203
throw new Exception(pht('Account does not match provider!'));
204
}
205
206
if (!$force_refresh) {
207
$access_expires = $account->getProperty('oauth.token.access.expires');
208
$access_token = $account->getProperty('oauth.token.access');
209
210
// Don't return a token with fewer than this many seconds remaining until
211
// it expires.
212
$shortest_token = 60;
213
if ($access_token) {
214
if ($access_expires === null ||
215
$access_expires > (time() + $shortest_token)) {
216
return $access_token;
217
}
218
}
219
}
220
221
$refresh_token = $account->getProperty('oauth.token.refresh');
222
if ($refresh_token) {
223
$adapter = $this->getAdapter();
224
if ($adapter->supportsTokenRefresh()) {
225
$adapter->refreshAccessToken($refresh_token);
226
227
$this->synchronizeOAuthAccount($account);
228
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
229
$account->save();
230
unset($unguarded);
231
232
return $account->getProperty('oauth.token.access');
233
}
234
}
235
236
return null;
237
}
238
239
public function willRenderLinkedAccount(
240
PhabricatorUser $viewer,
241
PHUIObjectItemView $item,
242
PhabricatorExternalAccount $account) {
243
244
// Get a valid token, possibly refreshing it. If we're unable to refresh
245
// it, render a message to that effect. The user may be able to repair the
246
// link by manually reconnecting.
247
248
$is_invalid = false;
249
try {
250
$oauth_token = $this->getOAuthAccessToken($account);
251
} catch (Exception $ex) {
252
$oauth_token = null;
253
$is_invalid = true;
254
}
255
256
$item->addAttribute(pht('OAuth2 Account'));
257
258
if ($oauth_token) {
259
$oauth_expires = $account->getProperty('oauth.token.access.expires');
260
if ($oauth_expires) {
261
$item->addAttribute(
262
pht(
263
'Active OAuth Token (Expires: %s)',
264
phabricator_datetime($oauth_expires, $viewer)));
265
} else {
266
$item->addAttribute(
267
pht('Active OAuth Token'));
268
}
269
} else if ($is_invalid) {
270
$item->addAttribute(pht('Invalid OAuth Access Token'));
271
} else {
272
$item->addAttribute(pht('No OAuth Access Token'));
273
}
274
275
parent::willRenderLinkedAccount($viewer, $item, $account);
276
}
277
278
public function supportsAutoLogin() {
279
return true;
280
}
281
282
public function getAutoLoginURI(AphrontRequest $request) {
283
$csrf_code = $this->getAuthCSRFCode($request);
284
285
$adapter = $this->getAdapter();
286
$adapter->setState($csrf_code);
287
288
return $adapter->getAuthenticateURI();
289
}
290
291
}
292
293