Skip to content

Commit

Permalink
username
Browse files Browse the repository at this point in the history
allow to set a user name at the demo app.
  • Loading branch information
lbuchs committed Oct 7, 2021
1 parent d84f661 commit a387f37
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 8 deletions.
33 changes: 32 additions & 1 deletion _test/client.html
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,10 @@
url += '&fmt_tpm=' + (document.getElementById('fmt_tpm').checked ? '1' : '0');
url += '&rpId=' + encodeURIComponent(document.getElementById('rpId').value);

url += '&userId=' + encodeURIComponent(document.getElementById('userId').value);
url += '&userName=' + encodeURIComponent(document.getElementById('userName').value);
url += '&userDisplayName=' + encodeURIComponent(document.getElementById('userDisplayName').value);

if (document.getElementById('userVerification_required').checked) {
url += '&userVerification=required';

Expand Down Expand Up @@ -346,7 +350,10 @@ <h1 style="margin: 40px 10px 2px 0;">lbuchs/WebAuthn</h1>

<div>
<input type="checkbox" id="requireResidentKey" name="requireResidentKey">
<label for="requireResidentKey">Use Client-side discoverable Credentials</label>
<label for="requireResidentKey">Use Client-side discoverable Credentials
<i style="font-size: 0.8em;">Warning: most client-side modules and authenticators don't allow to delete single client side credentials.
You have to reset the full device to remove the credentials again.</i>
</label>
</div>

<div>&nbsp;</div>
Expand All @@ -358,6 +365,27 @@ <h1 style="margin: 40px 10px 2px 0;">lbuchs/WebAuthn</h1>
<input type="text" id="rpId" name="rpId" value="">
</div>

<div>&nbsp;</div>
<div style="font-weight: bold">User</div>
<div style="margin-bottom:12px">
<label for="userId">User ID (Hex):</label>
<input type="text" id="userId" name="userId" value="64656d6f64656d6f" required pattern="[0-9a-fA-F]{2,}">
<i style="font-size: 0.8em;">You get the user ID back when checking registration (as userHandle), if you're using client-side discoverable credentials.
You can identify with this ID the user who wants to login.
A user handle is an opaque byte sequence with a maximum size of 64 bytes, and is not meant to be displayed to the user.
The user handle MUST NOT contain personally identifying information about the user, such as a username or e-mail address.</i>
</div>
<div style="margin-bottom:12px">
<label for="userName">User Name:</label>
<input type="text" id="userName" name="userName" value="demo" required pattern="[0-9a-zA-Z]{2,}">
<i style="font-size: 0.8em;">only for display, i.e., aiding the user in determining the difference between user accounts with similar display names.</i>
</div>
<div style="margin-bottom:6px">
<label for="userDisplayName">User Display Name:</label>
<input type="text" id="userDisplayName" name="userDisplayName" value="Demo Demolin" required>
<i style="font-size: 0.8em;">A human-palatable name for the user account, intended only for display.</i>
</div>

<div>&nbsp;</div>
<div style="font-weight: bold">attestation statement format</div>
<div>
Expand Down Expand Up @@ -484,6 +512,9 @@ <h1 style="margin: 40px 10px 2px 0;">lbuchs/WebAuthn</h1>
the client may change the assertion from the authenticator (for instance, using an anonymization CA),<br>
the browser may not warn about providing informations about your device.
</div>
<div style="margin-top:20px;font-size: 0.7em;font-style: italic">
Copyright &copy; 2021 Lukas Buchs - <a href="https://raw.githubusercontent.com/lbuchs/WebAuthn/master/LICENSE">license therms</a>
</div>

</div>
</div>
Expand Down
45 changes: 38 additions & 7 deletions _test/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@
$fn = filter_input(INPUT_GET, 'fn');
$requireResidentKey = !!$_GET['requireResidentKey'];
$userVerification = filter_input(INPUT_GET, 'userVerification', FILTER_SANITIZE_SPECIAL_CHARS);

$userId = filter_input(INPUT_GET, 'userId', FILTER_SANITIZE_SPECIAL_CHARS);
$userName = filter_input(INPUT_GET, 'userName', FILTER_SANITIZE_SPECIAL_CHARS);
$userDisplayName = filter_input(INPUT_GET, 'userDisplayName', FILTER_SANITIZE_SPECIAL_CHARS);

$userId = preg_replace('/[^0-9a-f]/i', '', $userId);
$userName = preg_replace('/[^0-9a-z]/i', '', $userName);
$userDisplayName = preg_replace('/[^0-9a-z öüäéèàÖÜÄÉÈÀÂÊÎÔÛâêîôû]/i', '', $userDisplayName);

$post = trim(file_get_contents('php://input'));
if ($post) {
$post = json_decode($post);
Expand Down Expand Up @@ -134,8 +143,9 @@
// ------------------------------------

if ($fn === 'getCreateArgs') {
$createArgs = $WebAuthn->getCreateArgs('demo', 'demo', 'Demo Demolin', 20, $requireResidentKey, $userVerification, $crossPlatformAttachment);
$createArgs = $WebAuthn->getCreateArgs(\hex2bin($userId), $userName, $userDisplayName, 20, $requireResidentKey, $userVerification, $crossPlatformAttachment);

header('Content-Type: application/json');
print(json_encode($createArgs));

// save challange to session. you have to deliver it to processGet later.
Expand All @@ -161,17 +171,20 @@
// from the database.
if (is_array($_SESSION['registrations'])) {
foreach ($_SESSION['registrations'] as $reg) {
$ids[] = $reg->credentialId;
if ($reg->userId === $userId) {
$ids[] = $reg->credentialId;
}
}
}

if (count($ids) === 0) {
throw new Exception('no registrations in session.');
throw new Exception('no registrations in session for userId ' . $userId);
}
}

$getArgs = $WebAuthn->getGetArgs($ids, 20, $typeUsb, $typeNfc, $typeBle, $typeInt, $userVerification);

header('Content-Type: application/json');
print(json_encode($getArgs));

// save challange to session. you have to deliver it to processGet later.
Expand All @@ -194,6 +207,11 @@
// with the user name.
$data = $WebAuthn->processCreate($clientDataJSON, $attestationObject, $challenge, $userVerification === 'required', true, false);

// add user infos
$data->userId = $userId;
$data->userName = $userName;
$data->userDisplayName = $userDisplayName;

if (!array_key_exists('registrations', $_SESSION) || !is_array($_SESSION['registrations'])) {
$_SESSION['registrations'] = array();
}
Expand All @@ -207,6 +225,8 @@
$return = new stdClass();
$return->success = true;
$return->msg = $msg;

header('Content-Type: application/json');
print(json_encode($return));


Expand All @@ -224,10 +244,6 @@
$challenge = $_SESSION['challenge'];
$credentialPublicKey = null;

if ($userHandle && $userHandle !== 'demo') {
throw new \Exception('Invalid user: not «demo» but «' . htmlspecialchars($userHandle) . '»!');
}

// looking up correspondending public key of the credential id
// you should also validate that only ids of the given user name
// are taken for the login.
Expand All @@ -244,11 +260,18 @@
throw new Exception('Public Key for credential ID not found!');
}

// if we have resident key, we have to verify that the userHandle is the provided userId at registration
if ($requireResidentKey && $userHandle !== hex2bin($reg->userId)) {
throw new \Exception('userId doesnt match (is ' . bin2hex($userHandle) . ' but expect ' . $reg->userId . ')');
}

// process the get request. throws WebAuthnException if it fails
$WebAuthn->processGet($clientDataJSON, $authenticatorData, $signature, $credentialPublicKey, $challenge, null, $userVerification === 'required');

$return = new stdClass();
$return->success = true;

header('Content-Type: application/json');
print(json_encode($return));

// ------------------------------------
Expand All @@ -262,6 +285,8 @@
$return = new stdClass();
$return->success = true;
$return->msg = 'all registrations deleted';

header('Content-Type: application/json');
print(json_encode($return));

// ------------------------------------
Expand Down Expand Up @@ -298,6 +323,8 @@
$html .= '<p>There are no registrations in this session.</p>';
}
$html .= '</body></html>';

header('Content-Type: text/html');
print $html;

// ------------------------------------
Expand Down Expand Up @@ -325,12 +352,16 @@
$return = new stdClass();
$return->success = $success;
$return->msg = $msg;

header('Content-Type: application/json');
print(json_encode($return));
}

} catch (Throwable $ex) {
$return = new stdClass();
$return->success = false;
$return->msg = $ex->getMessage();

header('Content-Type: application/json');
print(json_encode($return));
}

0 comments on commit a387f37

Please sign in to comment.