-
Notifications
You must be signed in to change notification settings - Fork 97
Description
OK, with a virtual authenticator the demo seems to work, my code is basically that code, but somehow I am getting a challenge mismatch case that prevents registration
My ERROR case:
if ($fn === 'getCreateArgs') {
$createArgs = $WebAuthn->getCreateArgs(\hex2bin($userId), $userName, $userDisplayName, 60*4, $requireResidentKey, $userVerification, $crossPlatformAttachment);
$thisChallenge = $WebAuthn->getChallenge();
// header('Content-Type: application/json');
// print(json_encode($createArgs));
// REFACTORED FOR USE CASE
$this->response->type('application/json');
echo json_encode($createArgs);
$this->log('AUDIT challenge getCreateArgs: '.$thisChallenge,'error');
$this->log('AUDIT $createArgs->publicKey->challenge: '.$createArgs->publicKey->challenge,'error');
// save challange to session. you have to deliver it to processGet later.
// $_SESSION['challenge'] = $WebAuthn->getChallenge();
// REFACTORED FOR USE CASE
$this->loadModel('Crypto');
$this->Crypto->create();
$cryptoKey = $this->genKey();
$crypto['Crypto']['key'] = $cryptoKey;
$crypto['Crypto']['value'] = $WebAuthn->getChallenge();
$this->Crypto->save($crypto,false);
$this->Session->write('User.challengeKey',$cryptoKey);
unset($crypto,$cryptoKey);
// ------------------------------------
// request for get arguments
// ------------------------------------
} used $thisChallenge and $WebAuthn->getChallenge() to sanity verify no one behind but as expect both values MATCH. THE LOG:
2025-08-06 08:24:20 Error: AUDIT challenge getCreateArgs: 08511e9bc45c24a84e025da279c5d36b93e95a95756cc7ee56ab02489ca85e44
2025-08-06 08:24:20 Error: AUDIT $createArgs->publicKey->challenge: 08511e9bc45c24a84e025da279c5d36b93e95a95756cc7ee56ab02489ca85e44
in the processCreate validating MATCH test values
else if ($fn === 'processCreate') {
$clientDataJSON = !empty($post->clientDataJSON) ? base64_decode($post->clientDataJSON) : null;
$crypto = $this->Crypto->find('first',array('recursive'=> -1,'conditions'=>array('Crypto.key'=>$cryptoKey)));
$this->log('AUDIT challenge VALUE crypto: ','error');
$this->log( $crypto,'error');
$this->log( 'client JSON DUMP','error');
$this->log( json_decode($clientDataJSON),'error');LOGGED VALUES:
2025-08-06 08:24:20 Error: AUDIT challenge VALUE crypto:
2025-08-06 08:24:20 Error: Array
(
[Crypto] => Array
(
[id] => 51
[key] => hn756je7b6
[value] => 08511e9bc45c24a84e025da279c5d36b93e95a95756cc7ee56ab02489ca85e44
)
)
2025-08-06 08:24:20 Error: client JSON DUMP
2025-08-06 08:24:20 Error: stdClass Object
(
[type] => webauthn.create
[challenge] => RjXw6VKmqajvMCoAIuPfKxZmuTlUY8Z3QlM9Dn83Cxw
[origin] => [THE ORIGIN URL]
[crossOrigin] =>
)
To check what it is getting modified my local WebAuthn.php for this error case to return the values and logged
WebAuthn.php
// 4. Verify that the value of C.challenge matches the challenge that was sent to the authenticator in the create() call.
if (!\property_exists($clientData, 'challenge') || ByteBuffer::fromBase64Url($clientData->challenge)->getBinaryString() !== $challenge->getBinaryString()) {
// throw new WebAuthnException('invalid challenge', WebAuthnException::INVALID_CHALLENGE);
// DEBUG THIS MIS MATCH
$DEBUGTHISVALUE = ByteBuffer::fromBase64Url($clientData->challenge)->getBinaryString();//trying to show this "STRING" JSON
$debug = array(
"server" => $challenge->getBinaryString(),
"client" => $DEBUGTHISVALUE,
);
return $debug;
}lse if ($fn === 'processCreate') {
$data = $WebAuthn->processCreate($clientDataJSON, $attestationObject, $challenge, $userVerification === 'required', true, false);
$this->log( 'DATA DUMP','error');
$this->log( $data,'error');RESULTING LOG
2025-08-06 08:24:20 Error: DATA DUMP
2025-08-06 08:24:20 Error: Array
(
[server] => 08511e9bc45c24a84e025da279c5d36b93e95a95756cc7ee56ab02489ca85e44
[client] => F5��R����0*�"��+�f�9Tc�wBS=��7��
)
So yes, the challenge do not match, but why? 08511e9bc45c24a84e025da279c5d36b93e95a95756cc7ee56ab02489ca85e44 is the same across all, this client-side JS works on the demo, what is going on here, what do I need to change to keep the MATCH?
Client side the JSON starts as
{
"rp": {
"name": "WebAuthn Library",
"id": "[the origin URL missing the https:// value from the form]"
},
"authenticatorSelection": {
"userVerification": "discouraged"
},
"user": {
"id": "=?BINARY?B?ZGVtb2RlbW8=?=",
"name": "demo",
"displayName": "Demo Demolin"
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -8
},
{
"type": "public-key",
"alg": -7
},
{
"type": "public-key",
"alg": -257
}
],
"attestation": "direct",
"extensions": {
"exts": true
},
"timeout": 240000,
"challenge": "=?BINARY?B?CFEem8RcJKhOAl2iecXTa5PpWpV1bMfuVqsCSJyoXkQ=?=",
"excludeCredentials": []
}Which matched the CFEem8RcJKhOAl2iecXTa5PpWpV1bMfuVqsCSJyoXkQ part of logged CLIENT CFEem8RcJKhOAl2iecXTa5PpWpV1bMfuVqsCSJyoXkQ I understand JSON messes this up, and the reason for recursiveBase64StrToArrayBuffer() is to handle the restore to ArrayBuffer, which seems to be happening
challenge
:
ArrayBuffer(32)
byteLength
:
32
detached
:
false
maxByteLength
:
32
resizable
:
false
[[Prototype]]
:
ArrayBuffer
[[Int8Array]]
:
Int8Array(32)
[[Uint8Array]]
:
Uint8Array(32)
[[Int16Array]]
:
Int16Array(16)
[[Int32Array]]
:
Int32Array(8)
[[ArrayBufferByteLength]]
:
32
[[ArrayBufferData]]
:
603
But, yet this mismatch?