1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
@license New BSD License - See LICENSE file for details. |
5
|
|
|
@copyright (C) 2022 SURF BV |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
namespace TestServer; |
9
|
|
|
|
10
|
|
|
use Tiqr_AutoLoader; |
11
|
|
|
use Tiqr_OCRAWrapper; |
12
|
|
|
use Tiqr_Service; |
13
|
|
|
use Tiqr_UserStorage; |
14
|
|
|
|
15
|
|
|
class TestServerController |
16
|
|
|
{ |
17
|
|
|
private $tiqrService; |
18
|
|
|
private $userStorage; |
19
|
|
|
private $host_url; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* @param $host_url This is the URL by which the tiqr client can reach this server, including http(s):// and port. |
23
|
|
|
* E.g. 'http://my-laptop.local:8000' |
24
|
|
|
* @param $authProtocol This is the app specific url for authentications of the tiqr client, without '://' |
|
|
|
|
25
|
|
|
* e.g. 'tiqrauth'. This must match what is configured in the tiqr client |
26
|
|
|
* @param $enrollProtocol This is the app specific url for enrolling user accounts in the tiqr client, without '://' |
27
|
|
|
* e.g. 'tiqrenroll'. This must match what is configured in the tiqr client |
28
|
|
|
*/ |
29
|
|
|
function __construct(string $host_url, string $authProtocol, string $enrollProtocol) |
30
|
|
|
{ |
31
|
|
|
$this->host_url = $host_url; |
32
|
|
|
$this->initTiqrLibrary(); |
33
|
|
|
$this->tiqrService = $this->createTiqrService($host_url, $authProtocol, $enrollProtocol); |
34
|
|
|
$this->userStorage = $this->createUserStorage(); |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
/** Initialize the tiqr-server-libphp's autoloader |
38
|
|
|
* @return void |
39
|
|
|
*/ |
40
|
|
|
private function initTiqrLibrary() |
41
|
|
|
{ |
42
|
|
|
// Initialise the tiqr-server-library autoloader |
43
|
|
|
$tiqr_dir = __DIR__ . '/../library/tiqr'; |
44
|
|
|
$vendor_dir = __DIR__ . '/../vendor'; |
45
|
|
|
|
46
|
|
|
require_once $tiqr_dir . '/Tiqr/AutoLoader.php'; |
47
|
|
|
|
48
|
|
|
$autoloader = Tiqr_AutoLoader::getInstance([ |
49
|
|
|
'tiqr.path' => $tiqr_dir, |
50
|
|
|
'phpqrcode.path' => $vendor_dir . '/kairos/phpqrcode', |
51
|
|
|
'zend.path' => $vendor_dir . '/zendframework/zendframework1/library' |
52
|
|
|
]); |
53
|
|
|
$autoloader->setIncludePath(); |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @return string Directory path for storing the server's data |
58
|
|
|
*/ |
59
|
|
|
private function getStorageDir(): string |
60
|
|
|
{ |
61
|
|
|
$storage_dir = __DIR__ . '/storage'; |
62
|
|
|
if (!is_dir($storage_dir)) { |
63
|
|
|
if (false == mkdir($storage_dir)) { |
|
|
|
|
64
|
|
|
TestServerApp::error_exit(500, "Error creating storage directory: $storage_dir"); |
65
|
|
|
} |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
return $storage_dir; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @return Tiqr_Service |
73
|
|
|
*/ |
74
|
|
|
private function createTiqrService($host, $authProtocol, $enrollProtocol) |
75
|
|
|
{ |
76
|
|
|
// Derive the identifier from the host |
77
|
|
|
$identifier = parse_url($host, PHP_URL_HOST); |
78
|
|
|
$storage_dir = $this->getStorageDir(); |
|
|
|
|
79
|
|
|
|
80
|
|
|
$tiqr_service = new Tiqr_Service( |
81
|
|
|
[ |
82
|
|
|
'auth.protocol' => $authProtocol, |
83
|
|
|
'enroll.protocol' => $enrollProtocol, |
84
|
|
|
'ocra.suite' => 'OCRA-1:HOTP-SHA1-6:QH10-S', |
85
|
|
|
'identifier' => $identifier, |
86
|
|
|
'name' => "TestServerController $host", |
87
|
|
|
'logoUrl' => "$host/logoUrl", |
88
|
|
|
'infoUrl' => "$host/infoUrl", |
89
|
|
|
|
90
|
|
|
// 'phpqrcode.path' |
91
|
|
|
// 'apns.path' |
92
|
|
|
// 'apns.certificate' |
93
|
|
|
'apns.environment' => 'sandbox', |
94
|
|
|
|
95
|
|
|
'c2dm.username' => 'test_c2dm_username', |
96
|
|
|
'c2dm.password' => 'test_c2dm_password', |
97
|
|
|
'c2dm.application' => 'org.example.authenticator.test', |
98
|
|
|
|
99
|
|
|
// Session storage, always stored in /tmp/tiqr_state_* |
100
|
|
|
'statestorage' => array( |
101
|
|
|
'type' => 'file', |
102
|
|
|
), |
103
|
|
|
|
104
|
|
|
// Token exchange configuration |
105
|
|
|
'devicestorage' => array( |
106
|
|
|
'type' => 'dummy', |
107
|
|
|
), |
108
|
|
|
] |
109
|
|
|
); |
110
|
|
|
|
111
|
|
|
return $tiqr_service; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
private function createUserStorage() |
115
|
|
|
{ |
116
|
|
|
$storage_dir = $this->getStorageDir(); |
117
|
|
|
$options = array( |
118
|
|
|
'type' => 'file', |
119
|
|
|
'path' => $storage_dir, |
120
|
|
|
); |
121
|
|
|
$secretoptions = array( |
122
|
|
|
'type' => 'file', |
123
|
|
|
'path' => $storage_dir, |
124
|
|
|
); |
125
|
|
|
return $userStorage = Tiqr_UserStorage::getStorage( |
|
|
|
|
126
|
|
|
'file', |
127
|
|
|
$options, |
128
|
|
|
$secretoptions |
129
|
|
|
); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
|
133
|
|
|
public function Route(App $app, string $path) |
134
|
|
|
{ |
135
|
|
|
$view = new TestServerView(); |
136
|
|
|
|
137
|
|
|
$app::log_info("host_url=$this->host_url"); |
138
|
|
|
switch ($path) { |
139
|
|
|
case "/": // Test server home page |
140
|
|
|
$view->ShowRoot(); |
141
|
|
|
break; |
142
|
|
|
|
143
|
|
|
case "/list-users": // page showing currently enrolled user accounts |
144
|
|
|
$this->list_users($app, $view); |
145
|
|
|
break; |
146
|
|
|
|
147
|
|
|
// Enrollment |
148
|
|
|
case "/start-enrollment": // Show enroll page to user |
149
|
|
|
$this->start_enrollment($app, $view); |
150
|
|
|
break; |
151
|
|
|
case "/metadata": // tiqr client gets metadata |
152
|
|
|
$this->metadata($app); |
153
|
|
|
break; |
154
|
|
|
case "/finish-enrollment": // tiqr client posts secret |
155
|
|
|
$this->finish_enrollment($app); |
156
|
|
|
break; |
157
|
|
|
|
158
|
|
|
// Render a QR code |
159
|
|
|
case "/qr": // used from start-enrollment and start_authenticate views |
160
|
|
|
$this->qr($app); |
161
|
|
|
break; |
162
|
|
|
|
163
|
|
|
// Serve test logo |
164
|
|
|
case "/logoUrl": // used by tiqr client to download logo, included in metadata |
165
|
|
|
$this->logo($app); |
166
|
|
|
break; |
167
|
|
|
// case "infoUrl": // user in metadata |
168
|
|
|
|
169
|
|
|
// Authentication |
170
|
|
|
case "/start-authenticate": // Show authenticate page to user |
171
|
|
|
$this->start_authenticate($app, $view); |
172
|
|
|
break; |
173
|
|
|
case "/authentication": // tiqr client posts back response |
174
|
|
|
$this->authentication($app); |
175
|
|
|
break; |
176
|
|
|
|
177
|
|
|
default: |
178
|
|
|
TestServerApp::error_exit(404, "Unknown route '$path'"); |
179
|
|
|
} |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
private function start_enrollment(App $app, TestServerView $view) |
183
|
|
|
{ |
184
|
|
|
// The session ID is used for communicating enrollment status between this tiqr server and |
185
|
|
|
// the web browser displaying the enrollment interface. It is not used between the tiqr client and |
186
|
|
|
// this server. We do not use it. |
187
|
|
|
$session_id = 'session_id_' . time(); |
188
|
|
|
$app::log_info("Created session $session_id"); |
189
|
|
|
|
190
|
|
|
// The user_id to create. Get it from the request, if it is not there use a test user ID. |
191
|
|
|
$user_id = $app->getGET()['user_id'] ?? 'test-user-' . time(); |
192
|
|
|
|
193
|
|
|
if ($this->userStorage->userExists($user_id)) { |
194
|
|
|
$app::log_warning("$user_id already exists"); |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
$user_display_name = $user_id . '\'s display name'; |
198
|
|
|
|
199
|
|
|
// Create enrollemnt key. The display name we set here is returned in the metadata generated by |
200
|
|
|
// getEnrollmentMetadata. |
201
|
|
|
// Note: we create the user in the userStorage later with a different display name so the displayname in the |
202
|
|
|
// App differs from the user's displayname on the server. |
203
|
|
|
$enrollment_key = $this->tiqrService->startEnrollmentSession($user_id, $user_display_name, $session_id); |
204
|
|
|
$app::log_info("Started enrollment session $enrollment_key"); |
205
|
|
|
$metadataUrl = $this->host_url . "/metadata"; |
206
|
|
|
$enroll_string = $this->tiqrService->generateEnrollString($metadataUrl) . "?enrollment_key=$enrollment_key"; |
207
|
|
|
$encoded_enroll_string = htmlentities(urlencode($enroll_string)); |
208
|
|
|
$image_url = "/qr?code=" . $encoded_enroll_string; |
209
|
|
|
|
210
|
|
|
$view->StartEnrollment(htmlentities($enroll_string), $image_url); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
// Generate a png image QR code with whatever string is given in the code HTTP request parameter. |
214
|
|
|
private function qr(App $app): void |
215
|
|
|
{ |
216
|
|
|
$code = $app->getGET()['code'] ?? ''; |
217
|
|
|
if (strlen($code) == 0) { |
218
|
|
|
// http://<server>/qr?code=<the string to encode> |
219
|
|
|
$app::error_exit(404, "qr: 'code' request parameter not set"); |
220
|
|
|
} |
221
|
|
|
$this->tiqrService->generateQR($code); |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
// In response to scanning the enrollment QR code the tiqr client makes a GET request to the URL in |
225
|
|
|
// the QR code. |
226
|
|
|
// This URL is exactly what we put in the QR code we generated in start_enrollment() : |
227
|
|
|
// <host_url>/metadata?enrollment_key=<enrollment_key> |
228
|
|
|
private function metadata(App $app) |
229
|
|
|
{ |
230
|
|
|
// The enrollment key must be there, it binds the user's browser session to the request comming from |
231
|
|
|
// the user's tiqr client |
232
|
|
|
$enrollment_key = $app->getGET()['enrollment_key'] ?? ''; |
233
|
|
|
if (strlen($enrollment_key) == 0) { |
234
|
|
|
// http://<server>/metadata?enrollment_key=<the enrollment key from the QR code> |
235
|
|
|
$app::error_exit(404, "metadata: 'enrollment_key' request parameter not set"); |
236
|
|
|
} |
237
|
|
|
// Generate an enrollment secret to add to the metadata URL |
238
|
|
|
$enrollment_secret = $this->tiqrService->getEnrollmentSecret($enrollment_key); |
239
|
|
|
$app::log_info("Created enrollment secret $enrollment_secret for enrollment key $enrollment_key"); |
240
|
|
|
|
241
|
|
|
// -----BEGIN NOTE----- |
242
|
|
|
// The enrollment_secret must be added manually to the enrollment URL. |
243
|
|
|
// I do not see why getEnrollmentMetadata cannot handle this for us en return the enrollment_secret |
244
|
|
|
// to the tiqr client as part of the $enrollment_metadata. That would make the call to getEnrollmentSecret() |
245
|
|
|
// redundant en the gereration of the metadata simpler. The Tiqr client could then return the enrollment_secret |
246
|
|
|
// in it's POST back to the $enrollment_url. |
247
|
|
|
// -----END NOTE----- |
248
|
|
|
|
249
|
|
|
// Add enrollment_secret to the $enrollment_url |
250
|
|
|
$enrollment_url = $this->host_url . "/finish-enrollment?enrollment_secret=$enrollment_secret"; |
251
|
|
|
$authentication_url = $this->host_url . '/authentication'; |
252
|
|
|
// Get the enrollment data |
253
|
|
|
$enrollment_metadata = $this->tiqrService->getEnrollmentMetadata($enrollment_key, $authentication_url, $enrollment_url); |
254
|
|
|
if (false == $enrollment_metadata) { |
255
|
|
|
// Happens when calling metadata with the same enrollment key a second time |
256
|
|
|
$app::error_exit(500, 'Error getting enrollment metadata'); |
257
|
|
|
} |
258
|
|
|
// Print the generated enrollment data in a more readable form |
259
|
|
|
foreach ($enrollment_metadata as $key1 => $value1) { |
260
|
|
|
if (is_array($value1)) { |
261
|
|
|
foreach ($value1 as $key2 => $value2) { |
262
|
|
|
$app::log_info("Metadata: $key1/$key2=$value2"); |
263
|
|
|
} |
264
|
|
|
} |
265
|
|
|
else { |
266
|
|
|
$app::log_info("Metadata: $key1=$value1"); |
267
|
|
|
} |
268
|
|
|
} |
269
|
|
|
// The enrollment metadata must be returned to the client as JSON |
270
|
|
|
$enrollment_metadata_json = json_encode($enrollment_metadata); |
271
|
|
|
$app::log_info("Return: $enrollment_metadata_json"); |
272
|
|
|
header("content-type: application/json"); |
273
|
|
|
echo $enrollment_metadata_json; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
// After receiving the enrollment metadata the tiqr client generates a secret key and posts |
277
|
|
|
// it to the enrollment URL we specified in the metadata together with the information required for sending |
278
|
|
|
// it push notification and the user's language preference. |
279
|
|
|
private function finish_enrollment(App $app) |
280
|
|
|
{ |
281
|
|
|
$enrollment_secret = $app->getGET()['enrollment_secret'] ?? ''; |
282
|
|
|
if (strlen($enrollment_secret) == 0) { |
283
|
|
|
// http://<server>/finish_enrollment?enrollment_secret=<the enrollment secret from the metadata> |
284
|
|
|
$app::error_exit(404, "enrollment: 'enrollment_secret' request parameter not set"); |
285
|
|
|
} |
286
|
|
|
// Validate the enrollment secret we were sent. In return we get the userid back that we set in |
287
|
|
|
// start_enrollment using startEnrollmentSession |
288
|
|
|
$userid = $this->tiqrService->validateEnrollmentSecret($enrollment_secret); |
289
|
|
|
if (false === $userid) { |
290
|
|
|
$app::error_exit(404, "Invalid enrollment_secret"); |
291
|
|
|
} |
292
|
|
|
$app::log_info("userid: $userid"); |
293
|
|
|
|
294
|
|
|
$secret = $app->getPOST()['secret'] ?? ''; |
295
|
|
|
if (strlen($secret) == 0) { |
296
|
|
|
$app::error_exit(404, "Missing secret is POST"); |
297
|
|
|
} |
298
|
|
|
// This is the hex encoded value of the authentication secret that the tiqr client |
299
|
|
|
// generated |
300
|
|
|
$app::log_info("secret: $secret"); |
301
|
|
|
|
302
|
|
|
$language = $app->getPOST()['language'] ?? ''; |
303
|
|
|
if (strlen($language) == 0) { |
304
|
|
|
$app::log_warning("No language in POST"); |
305
|
|
|
} |
306
|
|
|
// The iso language code e.g. "nl-NL" |
307
|
|
|
$app::log_info("language: $language"); |
308
|
|
|
|
309
|
|
|
$notificationType = $app->getPOST()['notificationType'] ?? ''; |
310
|
|
|
if (strlen($notificationType) == 0) { |
311
|
|
|
$app::log_warning("No notificationType in POST"); |
312
|
|
|
} |
313
|
|
|
// The notification message type (APNS, GCM, FCM ...) |
314
|
|
|
$app::log_info("notificationType: $notificationType"); |
315
|
|
|
|
316
|
|
|
$notificationAddress = $app->getPOST()['notificationAddress'] ?? ''; |
317
|
|
|
if (strlen($notificationAddress) == 0) { |
318
|
|
|
$app::log_warning("No notificationAddress in POST"); |
319
|
|
|
} |
320
|
|
|
// This is the notification address that the Tiqr Client got from the token exchange (e.g. tx.tiqr.org) |
321
|
|
|
$app::log_info("notificationAddress: $notificationAddress"); |
322
|
|
|
|
323
|
|
|
$version = $app->getPOST()['version'] ?? ''; |
324
|
|
|
if (strlen($version) == 0) { |
325
|
|
|
$app::log_warning("No version in POST"); |
326
|
|
|
} |
327
|
|
|
// ? |
328
|
|
|
$app::log_info("version: $version"); |
329
|
|
|
|
330
|
|
|
$operation = $app->getPOST()['operation'] ?? ''; |
331
|
|
|
if (strlen($operation) == 0) { |
332
|
|
|
$app::log_warning("No operation in POST"); |
333
|
|
|
} |
334
|
|
|
// Must be "register" |
335
|
|
|
$app::log_info("operation: $operation"); |
336
|
|
|
if ($operation != 'register') { |
337
|
|
|
$app::error_exit(404, "Invalid operation: '$operation'. Expected 'register'"); |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
// Get User-Agent HTTP header |
341
|
|
|
$user_agent = urldecode($_SERVER['HTTP_USER_AGENT'] ?? ''); |
342
|
|
|
$app::log_info("User-Agent: $user_agent"); |
343
|
|
|
|
344
|
|
|
// Create the user. Use the display name to store the version the client POSTed and the user-agent it sent |
345
|
|
|
// in this POST request's header. |
346
|
|
|
$this->userStorage->createUser($userid, "$version | $user_agent"); |
347
|
|
|
$app::log_info("Created user $userid"); |
348
|
|
|
|
349
|
|
|
// Set the user secret |
350
|
|
|
$this->userStorage->setSecret($userid, $secret); |
351
|
|
|
$app::log_info("Secret for $userid was stored"); |
352
|
|
|
|
353
|
|
|
// Store notification type and the notification address that the client sent us |
354
|
|
|
$this->userStorage->setNotificationType($userid, $notificationType); |
355
|
|
|
$this->userStorage->setNotificationAddress($userid, $notificationAddress); |
356
|
|
|
|
357
|
|
|
// Finalize the enrollemnt |
358
|
|
|
$this->tiqrService->finalizeEnrollment($enrollment_secret); |
359
|
|
|
$app::log_info("Enrollment was finalized"); |
360
|
|
|
|
361
|
|
|
// Must return "OK" to the tiqr client after a successful enrollment |
362
|
|
|
echo "OK"; |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
private function logo(App $app) |
|
|
|
|
366
|
|
|
{ |
367
|
|
|
// Source: https://nl.wikipedia.org/wiki/Bestand:Philips_PM5544.svg |
368
|
|
|
$name = __DIR__ . '/Philips_PM5544.jpg'; |
369
|
|
|
$fp = fopen($name, 'rb'); |
370
|
|
|
header('Content-Type: image/jpeg'); |
371
|
|
|
header('Content-Length: ' . filesize($name)); |
372
|
|
|
fpassthru($fp); |
373
|
|
|
fclose($fp); |
374
|
|
|
} |
375
|
|
|
|
376
|
|
|
function list_users(App $app, TestServerView $view) |
|
|
|
|
377
|
|
|
{ |
378
|
|
|
$storageDir = $this->getStorageDir(); |
379
|
|
|
$users = array(); |
380
|
|
|
foreach (scandir($storageDir) as $filename) { |
381
|
|
|
if (substr($filename, -5, 5) == '.json') { |
382
|
|
|
$user = json_decode(file_get_contents($storageDir . '/' . $filename), true); |
383
|
|
|
if (($user != NULL) && ($user['secret'])) { |
384
|
|
|
foreach ($user as $k => $v) { |
385
|
|
|
$user[$k] = htmlentities($v); |
386
|
|
|
} |
387
|
|
|
$users[] = $user; |
388
|
|
|
} |
389
|
|
|
} |
390
|
|
|
} |
391
|
|
|
$view->ListUsers($users); |
392
|
|
|
} |
393
|
|
|
|
394
|
|
|
private function start_authenticate(App $app, TestServerView $view) |
395
|
|
|
{ |
396
|
|
|
$session_id = 'session_id_' . time(); |
397
|
|
|
$app::log_info("Created session $session_id"); |
398
|
|
|
|
399
|
|
|
// The user_id to authenticate. Get it from the request, if it is not there use an empty user ID |
400
|
|
|
// Both scenario's are support by tiqr: |
401
|
|
|
// 1. No user-id in the authentication url. This is a login scenario. The tiqr client selects the user id |
402
|
|
|
// 2. Specify the user-is in the authentication url. This is the step-up scenario. The tiqr server specifies |
403
|
|
|
// the user-id |
404
|
|
|
|
405
|
|
|
// Get optional user ID |
406
|
|
|
$user_id = $app->getGET()['user_id'] ?? ''; |
407
|
|
|
if (strlen($user_id) > 0) { |
408
|
|
|
$app::log_info("Authenticating user '$user_id'"); |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
if (!$this->userStorage->userExists($user_id)) { |
412
|
|
|
$app::log_warning("'$user_id' is not known on the server"); |
413
|
|
|
} |
414
|
|
|
|
415
|
|
|
// Start authentication session |
416
|
|
|
$session_key = $this->tiqrService->startAuthenticationSession($user_id, $session_id); |
417
|
|
|
$app::log_info('Started authentication session'); |
418
|
|
|
$app::log_info("session_key=$session_key"); |
419
|
|
|
|
420
|
|
|
// Get authentication URL for the tiqr client (to put in the QR code) |
421
|
|
|
$authentication_URL = $this->tiqrService->generateAuthURL($session_key); |
422
|
|
|
$app::log_info('Started authentication URL'); |
423
|
|
|
$app::log_info("authentication_url=$authentication_URL"); |
424
|
|
|
|
425
|
|
|
$image_url = "/qr?code=" . htmlentities(urlencode($authentication_URL)); |
|
|
|
|
426
|
|
|
|
427
|
|
|
$response = ''; |
428
|
|
|
if (strlen($user_id) > 0) { |
429
|
|
|
// Calculate response |
430
|
|
|
$app::log_info("Calculating response for $user_id"); |
431
|
|
|
$secret = $this->userStorage->getSecret($user_id); |
432
|
|
|
$app::log_info("secret=$secret"); |
433
|
|
|
$exploded = explode('/', $authentication_URL); |
|
|
|
|
434
|
|
|
$session_key = $exploded[3]; // hex encoded session |
435
|
|
|
$challenge = $exploded[4]; // 10 digit hex challenge |
436
|
|
|
$app::log_info("challenge=$challenge"); |
437
|
|
|
$ocra = new Tiqr_OCRAWrapper('OCRA-1:HOTP-SHA1-6:QH10-S'); |
438
|
|
|
$response = $ocra->calculateResponse($secret, $challenge, $session_key); |
439
|
|
|
$app::log_info("response=$response"); |
440
|
|
|
} |
441
|
|
|
|
442
|
|
|
$view->StartAuthenticate(htmlentities($authentication_URL), $image_url, $user_id, $response); |
|
|
|
|
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
private function authentication(App $app) |
446
|
|
|
{ |
447
|
|
|
// This should be the session key from the authentication URL that we generated |
448
|
|
|
$sessionKey = $app->getPOST()['sessionKey'] ?? ''; |
449
|
|
|
if (strlen($sessionKey) == 0) { |
450
|
|
|
$app::error_exit(404, "Missing sessionKey is POST"); |
451
|
|
|
} |
452
|
|
|
$app::log_info("sessionKey: $sessionKey"); |
453
|
|
|
|
454
|
|
|
// The userId the client authenticated |
455
|
|
|
$userId = $app->getPOST()['userId'] ?? ''; |
456
|
|
|
if (strlen($userId) == 0) { |
457
|
|
|
$app::error_exit(404, "Missing $userId is POST"); |
458
|
|
|
} |
459
|
|
|
$app::log_info("userId: $userId"); |
460
|
|
|
|
461
|
|
|
// Get version from POST |
462
|
|
|
$version = $app->getPOST()['version'] ?? ''; |
463
|
|
|
if (strlen($version) == 0) { |
464
|
|
|
$app::log_warning("No version in POST"); |
465
|
|
|
} |
466
|
|
|
// ? |
467
|
|
|
$app::log_info("version: $version"); |
468
|
|
|
|
469
|
|
|
// Get operation from POST |
470
|
|
|
$operation = $app->getPOST()['operation'] ?? ''; |
471
|
|
|
if (strlen($operation) == 0) { |
472
|
|
|
$app::log_warning("No operation in POST"); |
473
|
|
|
} |
474
|
|
|
// Must be "login" |
475
|
|
|
$app::log_info("operation: $operation"); |
476
|
|
|
if ($operation != 'login') { |
477
|
|
|
$app::error_exit(404, "Invalid operation: '$operation'. Expected 'login'"); |
478
|
|
|
} |
479
|
|
|
|
480
|
|
|
// Get response from POST |
481
|
|
|
$response = $app->getPOST()['response'] ?? ''; |
482
|
|
|
if (strlen($response) == 0) { |
483
|
|
|
$app::log_warning("No response in POST"); |
484
|
|
|
} |
485
|
|
|
$app::log_info("response: $response"); |
486
|
|
|
|
487
|
|
|
$language = $app->getPOST()['language'] ?? ''; |
488
|
|
|
if (strlen($language) == 0) { |
489
|
|
|
$app::log_warning("No language in POST"); |
490
|
|
|
} |
491
|
|
|
// The iso language code e.g. "nl-NL" |
492
|
|
|
$app::log_info("language: $language"); |
493
|
|
|
|
494
|
|
|
$notificationType = $app->getPOST()['notificationType'] ?? ''; |
495
|
|
|
if (strlen($notificationType) == 0) { |
496
|
|
|
$app::log_warning("No notificationType in POST"); |
497
|
|
|
} |
498
|
|
|
// The notification message type (APNS, GCM, FCM ...) |
499
|
|
|
$app::log_info("notificationType: $notificationType"); |
500
|
|
|
|
501
|
|
|
$notificationAddress = $app->getPOST()['notificationAddress'] ?? ''; |
502
|
|
|
if (strlen($notificationAddress) == 0) { |
503
|
|
|
$app::log_warning("No notificationAddress in POST"); |
504
|
|
|
} |
505
|
|
|
// This is the notification address that the Tiqr Client got from the token exchange (e.g. tx.tiqr.org) |
506
|
|
|
$app::log_info("notificationAddress: $notificationAddress"); |
507
|
|
|
|
508
|
|
|
// Get User-Agent HTTP header |
509
|
|
|
$user_agent = urldecode($_SERVER['HTTP_USER_AGENT'] ?? ''); |
510
|
|
|
$app::log_info("User-Agent: $user_agent"); |
511
|
|
|
|
512
|
|
|
$result = 'ERROR'; // Result for the tiqr Client |
513
|
|
|
// 'OK', 'INVALID_CHALLENGE', 'INVALID_REQUEST', 'INVALID_RESPONSE', 'INVALID_USER' |
514
|
|
|
|
515
|
|
|
if (!$this->userStorage->userExists($userId)) { |
516
|
|
|
$app::log_error("Unknown user: $userId "); |
517
|
|
|
$result = 'INVALID_USER'; |
|
|
|
|
518
|
|
|
} |
519
|
|
|
|
520
|
|
|
// Lookup the secret of the user by ID |
521
|
|
|
$userSecret = $this->userStorage->getSecret($userId); // Assume this works |
522
|
|
|
$app::log_info("userSercret=$userSecret"); |
523
|
|
|
|
524
|
|
|
$app::log_info("Authenticating user"); |
525
|
|
|
$result = $this->tiqrService->authenticate($userId, $userSecret, $sessionKey, $response); |
526
|
|
|
$resultStr = 'ERROR'; |
527
|
|
|
switch ($result) { |
528
|
|
|
case Tiqr_Service::AUTH_RESULT_INVALID_REQUEST: |
529
|
|
|
$resultStr = "INVALID_REQUEST"; |
530
|
|
|
break; |
531
|
|
|
case Tiqr_Service::AUTH_RESULT_AUTHENTICATED: |
532
|
|
|
$resultStr = "OK"; |
533
|
|
|
break; |
534
|
|
|
case Tiqr_Service::AUTH_RESULT_INVALID_RESPONSE: |
535
|
|
|
$resultStr = "INVALID_RESPONSE"; |
536
|
|
|
break; |
537
|
|
|
case Tiqr_Service::AUTH_RESULT_INVALID_CHALLENGE: |
538
|
|
|
$resultStr = "INVALID_CHALLENGE"; |
539
|
|
|
break; |
540
|
|
|
case Tiqr_Service::AUTH_RESULT_INVALID_USERID: |
541
|
|
|
$resultStr = "INVALID_USER"; |
542
|
|
|
break; |
543
|
|
|
} |
544
|
|
|
|
545
|
|
|
$app::log_info("Returning authentication result '$resultStr'"); |
546
|
|
|
echo $resultStr; |
547
|
|
|
} |
548
|
|
|
} |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths