forked from joostdekeijzer/mw-oauth2-client-extension
-
Notifications
You must be signed in to change notification settings - Fork 55
/
SpecialOAuth2Client.php
207 lines (179 loc) · 7.4 KB
/
SpecialOAuth2Client.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
<?php
/**
* SpecialOAuth2Client.php
* Based on TwitterLogin by David Raison, which is based on the guideline published by Dave Challis at http://blogs.ecs.soton.ac.uk/webteam/2010/04/13/254/
* @license: LGPL (GNU Lesser General Public License) http://www.gnu.org/licenses/lgpl.html
*
* @file SpecialOAuth2Client.php
* @ingroup OAuth2Client
*
* @author Joost de Keijzer
* @author Nischay Nahata for Schine GmbH
*
* Uses the OAuth2 library https://github.com/vznet/oauth_2.0_client_php
*
*/
if ( !defined( 'MEDIAWIKI' ) ) {
die( 'This is a MediaWiki extension, and must be run from within MediaWiki.' );
}
require __DIR__.'/JsonHelper.php';
class SpecialOAuth2Client extends SpecialPage {
private $_provider;
/**
* Required settings in global $wgOAuth2Client
*
* $wgOAuth2Client['client']['id']
* $wgOAuth2Client['client']['secret']
* //$wgOAuth2Client['client']['callback_url'] // extension should know
*
* $wgOAuth2Client['configuration']['authorize_endpoint']
* $wgOAuth2Client['configuration']['access_token_endpoint']
* $wgOAuth2Client['configuration']['http_bearer_token']
* $wgOAuth2Client['configuration']['query_parameter_token']
* $wgOAuth2Client['configuration']['api_endpoint']
*/
public function __construct() {
parent::__construct('OAuth2Client'); // ???: wat doet dit?
global $wgOAuth2Client, $wgScriptPath;
global $wgServer, $wgArticlePath;
require __DIR__ . '/vendors/oauth2-client/vendor/autoload.php';
$this->_provider = new \League\OAuth2\Client\Provider\GenericProvider([
'clientId' => $wgOAuth2Client['client']['id'], // The client ID assigned to you by the provider
'clientSecret' => $wgOAuth2Client['client']['secret'], // The client password assigned to you by the provider
'redirectUri' => $wgOAuth2Client['configuration']['redirect_uri'],
'urlAuthorize' => $wgOAuth2Client['configuration']['authorize_endpoint'],
'urlAccessToken' => $wgOAuth2Client['configuration']['access_token_endpoint'],
'urlResourceOwnerDetails' => $wgOAuth2Client['configuration']['api_endpoint'],
'scopes' => $wgOAuth2Client['configuration']['scopes']
]);
}
// default method being called by a specialpage
public function execute( $parameter ){
$this->setHeaders();
switch($parameter){
case 'redirect':
$this->_redirect();
break;
case 'callback':
$this->_handleCallback();
break;
default:
$this->_default();
break;
}
}
private function _redirect() {
global $wgRequest, $wgOut;
$wgRequest->getSession()->persist();
$wgRequest->getSession()->set('returnto', $wgRequest->getVal( 'returnto' ));
// Fetch the authorization URL from the provider; this returns the
// urlAuthorize option and generates and applies any necessary parameters
// (e.g. state).
$authorizationUrl = $this->_provider->getAuthorizationUrl();
// Get the state generated for you and store it to the session.
$wgRequest->getSession()->set('oauth2state', $this->_provider->getState());
$wgRequest->getSession()->save();
// Redirect the user to the authorization URL.
$wgOut->redirect( $authorizationUrl );
}
private function _handleCallback(){
global $wgRequest;
try {
$storedState = $wgRequest->getSession()->get('oauth2state');
// Enforce the `state` parameter to prevent clickjacking/CSRF
if(isset($storedState) && $storedState != $_GET['state']) {
if(isset($_GET['state'])) {
throw new UnexpectedValueException("State parameter of callback does not match original state");
} else {
throw new UnexpectedValueException("Required state parameter missing");
}
}
// Try to get an access token using the authorization code grant.
$accessToken = $this->_provider->getAccessToken('authorization_code', [
'code' => $_GET['code']
]);
} catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
exit($e->getMessage()); // Failed to get the access token or user details.
} catch (UnexpectedValueException $e) {
exit($e->getMessage());
}
$resourceOwner = $this->_provider->getResourceOwner($accessToken);
$user = $this->_userHandling( $resourceOwner->toArray() );
$user->setCookies();
global $wgOut, $wgRequest;
$title = null;
$wgRequest->getSession()->persist();
if( $wgRequest->getSession()->exists('returnto') ) {
$title = Title::newFromText( $wgRequest->getSession()->get('returnto') );
$wgRequest->getSession()->remove('returnto');
$wgRequest->getSession()->save();
}
if( !$title instanceof Title || 0 > $title->getArticleID() ) {
$title = Title::newMainPage();
}
$wgOut->redirect( $title->getFullURL() );
return true;
}
private function _default(){
global $wgOAuth2Client, $wgOut, $wgUser, $wgScriptPath, $wgExtensionAssetsPath;
$service_name = ( isset( $wgOAuth2Client['configuration']['service_name'] ) && 0 < strlen( $wgOAuth2Client['configuration']['service_name'] ) ? $wgOAuth2Client['configuration']['service_name'] : 'OAuth2' );
$wgOut->setPagetitle( wfMessage( 'oauth2client-login-header', $service_name)->text() );
if ( !$wgUser->isLoggedIn() ) {
$wgOut->addWikiMsg( 'oauth2client-you-can-login-to-this-wiki-with-oauth2', $service_name );
$wgOut->addWikiMsg( 'oauth2client-login-with-oauth2', $this->getTitle( 'redirect' )->getPrefixedURL(), $service_name );
} else {
$wgOut->addWikiMsg( 'oauth2client-youre-already-loggedin' );
}
return true;
}
/**
* Optional callback settings in global $wgOAuth2Client
*
* Provide a callback and error message in the configuration that evaluates
* a conditional based upon the result of some business logic provided by
* the authorization endpoint response.
* $wgOAuth2Client['configuration']['authz_callback']
* $wgOAuth2Client['configuration']['authz_failure_message']
*/
protected function _userHandling( $response ) {
global $wgOAuth2Client, $wgAuth, $wgRequest;
if (
isset($wgOAuth2Client['configuration']['authz_callback'])
&& false === $wgOAuth2Client['configuration']['authz_callback']($response)
) {
$callback_failure_message = isset($wgOAuth2Client['configuration']['authz_failure_message'])
? $wgOAuth2Client['configuration']['authz_failure_message']
: 'Not authorized';
throw new MWException($callback_failure_message);
}
$username = JsonHelper::extractValue($response, $wgOAuth2Client['configuration']['username']);
$email = JsonHelper::extractValue($response, $wgOAuth2Client['configuration']['email']);
Hooks::run("OAuth2ClientBeforeUserSave", [&$username, &$email, $response]);
$user = User::newFromName($username, 'creatable');
if (!$user) {
throw new MWException('Could not create user with username:' . $username);
die();
}
$user->setRealName($username);
$user->setEmail($email);
$user->load();
if ( !( $user instanceof User && $user->getId() ) ) {
$user->addToDatabase();
// MediaWiki recommends below code instead of addToDatabase to create user but it seems to fail.
// $authManager = MediaWiki\Auth\AuthManager::singleton();
// $authManager->autoCreateUser( $user, MediaWiki\Auth\AuthManager::AUTOCREATE_SOURCE_SESSION );
$user->confirmEmail();
}
$user->setToken();
// Setup the session
$wgRequest->getSession()->persist();
$user->setCookies();
$this->getContext()->setUser( $user );
$user->saveSettings();
global $wgUser;
$wgUser = $user;
$sessionUser = User::newFromSession($this->getRequest());
$sessionUser->load();
return $user;
}
}