1
|
|
|
<?php |
|
|
|
|
2
|
|
|
/** |
3
|
|
|
* @package OAuth2 |
4
|
|
|
* @category modules |
5
|
|
|
* @author Nazar Mokrynskyi <[email protected]> |
6
|
|
|
* @copyright Copyright (c) 2011-2016, Nazar Mokrynskyi |
7
|
|
|
* @license MIT License, see license.txt |
8
|
|
|
*/ |
9
|
|
|
/** |
10
|
|
|
* Provides next events:<br> |
11
|
|
|
* OAuth2/custom_sign_in_page |
12
|
|
|
* OAuth2/custom_allow_access_page |
13
|
|
|
*/ |
14
|
|
|
namespace cs\modules\OAuth2; |
15
|
|
|
use |
16
|
|
|
h, |
17
|
|
|
cs\Config, |
18
|
|
|
cs\Event, |
19
|
|
|
cs\ExitException, |
20
|
|
|
cs\Language\Prefix, |
21
|
|
|
cs\Page, |
22
|
|
|
cs\Response, |
23
|
|
|
cs\User; |
24
|
|
|
|
25
|
|
|
if (!function_exists(__NAMESPACE__.'\\http_build_url')) { |
26
|
|
|
/** |
27
|
|
|
* @param string $url |
|
|
|
|
28
|
|
|
* @param string[] $parts |
|
|
|
|
29
|
|
|
* |
30
|
|
|
* @return string |
31
|
|
|
*/ |
32
|
|
|
function http_build_url ($url, $parts) { |
33
|
|
|
$url = explode('?', $url, 2); |
34
|
|
|
$params = []; |
35
|
|
|
if (isset($url[1])) { |
36
|
|
|
foreach (explode('&', $url[1]) as $u) { |
37
|
|
|
$params[] = $u; |
38
|
|
|
} |
39
|
|
|
unset($u, $url[1]); |
40
|
|
|
} |
41
|
|
|
$url = $url[0]; |
42
|
|
|
foreach ($parts as $name => $value) { |
43
|
|
|
$params[] = $name.'='.urlencode($value); |
44
|
|
|
} |
45
|
|
|
$params = array_unique($params); |
46
|
|
|
return "$url?".implode('&', $params); |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
function error_redirect ($error, $description) { |
50
|
|
|
Response::instance()->redirect( |
51
|
|
|
http_build_url( |
52
|
|
|
urldecode($_GET['redirect_uri']), |
53
|
|
|
[ |
54
|
|
|
'error' => $error, |
55
|
|
|
'error_description' => $description, |
56
|
|
|
'state' => isset($_GET['state']) ? $_GET['state'] : false |
57
|
|
|
] |
58
|
|
|
), |
59
|
|
|
302 |
60
|
|
|
); |
61
|
|
|
} |
62
|
|
|
} |
63
|
|
|
$OAuth2 = OAuth2::instance(); |
64
|
|
|
$Config = Config::instance(); |
65
|
|
|
$L = new Prefix('oauth2_'); |
66
|
|
|
$Page = Page::instance(); |
67
|
|
|
/** |
68
|
|
|
* Errors processing |
69
|
|
|
*/ |
70
|
|
|
if (!isset($_GET['client_id'])) { |
71
|
|
|
error_redirect('invalid_request', 'client_id parameter required'); |
72
|
|
|
return; |
73
|
|
|
} |
74
|
|
|
$client = $OAuth2->get_client($_GET['client_id']); |
75
|
|
|
if (!$client) { |
76
|
|
|
error_redirect('invalid_request', 'client_id not found'); |
77
|
|
|
return; |
78
|
|
|
} |
79
|
|
|
if (!$client['domain']) { |
80
|
|
|
$e = new ExitException( |
81
|
|
|
[ |
82
|
|
|
'unauthorized_client', |
83
|
|
|
'Request method is not authored' |
84
|
|
|
], |
85
|
|
|
400 |
86
|
|
|
); |
87
|
|
|
$e->setJson(); |
88
|
|
|
throw $e; |
89
|
|
|
} |
90
|
|
|
if (!$client['active']) { |
91
|
|
|
if (!isset($_GET['redirect_uri'])) { |
92
|
|
|
$e = new ExitException( |
93
|
|
|
[ |
94
|
|
|
'invalid_request', |
95
|
|
|
'Inactive client_id, redirect_uri parameter required' |
96
|
|
|
], |
97
|
|
|
400 |
98
|
|
|
); |
99
|
|
|
$e->setJson(); |
100
|
|
|
throw $e; |
101
|
|
|
} else { |
102
|
|
|
if ( |
103
|
|
|
urldecode($_GET['redirect_uri']) != $Config->base_url().'/OAuth2/blank/' && |
104
|
|
|
!preg_match("#^[^/]+://$client[domain]#", urldecode($_GET['redirect_uri'])) |
105
|
|
|
) { |
106
|
|
|
error_redirect('access_denied', 'Inactive client id'); |
107
|
|
|
return; |
108
|
|
|
} else { |
109
|
|
|
$e = new ExitException( |
110
|
|
|
[ |
111
|
|
|
'invalid_request', |
112
|
|
|
'Inactive client_id, redirect_uri parameter required' |
113
|
|
|
], |
114
|
|
|
400 |
115
|
|
|
); |
116
|
|
|
$e->setJson(); |
117
|
|
|
throw $e; |
118
|
|
|
} |
119
|
|
|
} |
120
|
|
|
} |
121
|
|
|
if (!isset($_GET['redirect_uri'])) { |
122
|
|
|
throw new ExitException( |
123
|
|
|
[ |
124
|
|
|
'invalid_request', |
125
|
|
|
'redirect_uri parameter required' |
126
|
|
|
], |
127
|
|
|
400 |
128
|
|
|
); |
129
|
|
|
} elseif ( |
130
|
|
|
urldecode($_GET['redirect_uri']) != $Config->base_url().'/OAuth2/blank/' && |
131
|
|
|
!preg_match("#^[^/]+://$client[domain]#", urldecode($_GET['redirect_uri'])) |
132
|
|
|
) { |
133
|
|
|
throw new ExitException( |
134
|
|
|
[ |
135
|
|
|
'invalid_request', |
136
|
|
|
'redirect_uri parameter invalid' |
137
|
|
|
], |
138
|
|
|
400 |
139
|
|
|
); |
140
|
|
|
} |
141
|
|
|
$redirect_uri = urldecode($_GET['redirect_uri']); |
142
|
|
|
if (!isset($_GET['response_type'])) { |
143
|
|
|
error_redirect('invalid_request', 'response_type parameter required'); |
144
|
|
|
return; |
145
|
|
|
} |
146
|
|
|
$User = User::instance(); |
147
|
|
|
if (!$User->user()) { |
148
|
|
|
if (Event::instance()->fire('OAuth2/custom_sign_in_page')) { |
149
|
|
|
$Page->Content = ''; |
150
|
|
|
$Page->warning($L->you_are_not_logged_in); |
151
|
|
|
} |
152
|
|
|
return; |
153
|
|
|
} |
154
|
|
|
$Response = Response::instance(); |
155
|
|
|
/** |
156
|
|
|
* Authorization processing |
157
|
|
|
*/ |
158
|
|
|
if (isset($_POST['mode'])) { |
159
|
|
|
if ($_POST['mode'] == 'allow') { |
160
|
|
|
$OAuth2->add_access($client['id']); |
161
|
|
|
} else { |
162
|
|
|
error_redirect('access_denied', 'User denied access'); |
163
|
|
|
$Page->Content = ''; |
164
|
|
|
return; |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
if (!$OAuth2->get_access($client['id'])) { |
168
|
|
|
if (Event::instance()->fire('OAuth2/custom_allow_access_page')) { |
169
|
|
|
$Page->success( |
170
|
|
|
$L->client_want_access_your_account($client['name']) |
171
|
|
|
); |
172
|
|
|
$Page->content( |
173
|
|
|
h::form( |
174
|
|
|
h::{'button[is=cs-button][type=submit][name=mode][value=allow]'}($L->allow). |
175
|
|
|
h::{'button[is=cs-button][type=submit][mode=mode][value=deny]'}($L->deny) |
176
|
|
|
) |
177
|
|
|
); |
178
|
|
|
} |
179
|
|
|
return; |
180
|
|
|
} |
181
|
|
|
$code = $OAuth2->add_code($client['id'], $_GET['response_type'], $redirect_uri); |
182
|
|
|
if (!$code) { |
183
|
|
|
error_redirect('server_error', "Server can't generate code, try later"); |
184
|
|
|
return; |
185
|
|
|
} |
186
|
|
|
switch ($_GET['response_type']) { |
187
|
|
|
case 'code': |
188
|
|
|
$Response->redirect( |
189
|
|
|
http_build_url( |
190
|
|
|
$redirect_uri, |
191
|
|
|
[ |
192
|
|
|
'code' => $code, |
193
|
|
|
'state' => isset($_GET['state']) ? $_GET['state'] : false |
194
|
|
|
] |
195
|
|
|
), |
196
|
|
|
302 |
197
|
|
|
); |
198
|
|
|
$Page->Content = ''; |
199
|
|
|
return; |
200
|
|
|
case 'token': |
201
|
|
|
$token_data = $OAuth2->get_code($code, $client['id'], $client['secret'], $redirect_uri); |
202
|
|
|
if ($token_data) { |
203
|
|
|
unset($token_data['refresh_token']); |
204
|
|
|
$url = http_build_url( |
205
|
|
|
$redirect_uri, |
206
|
|
|
array_merge( |
207
|
|
|
$token_data, |
208
|
|
|
[ |
209
|
|
|
'state' => isset($_GET['state']) ? $_GET['state'] : false |
210
|
|
|
] |
211
|
|
|
) |
212
|
|
|
); |
213
|
|
|
$url = implode('#', explode('?', $url, 2)); |
214
|
|
|
$Response->redirect($url, 302); |
215
|
|
|
$Page->Content = ''; |
216
|
|
|
return; |
217
|
|
|
} else { |
218
|
|
|
error_redirect('server_error', "Server can't get token data, try later"); |
219
|
|
|
return; |
220
|
|
|
} |
221
|
|
|
default: |
222
|
|
|
error_redirect('unsupported_response_type', 'Specified response_type is not supported, only "token" or "code" types available'); |
223
|
|
|
} |
224
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.