web-auth / webauthn-framework

FIDO-U2F / FIDO2 / Webauthn Framework
MIT License
421 stars 54 forks source link

"Webauthn\AuthenticatorSelectionCriteria::createFromArray(): Argument #1 ($json) must be of type array. #483

Closed akubima closed 11 months ago

akubima commented 1 year ago

Version(s) affected

4.7.1

Description

"Webauthn\AuthenticatorSelectionCriteria::createFromArray(): Argument #1 ($json) must be of type array, Webauthn\AuthenticatorSelectionCriteria given, called in /var/www/projects/webauthn/vendor/web-auth/webauthn-lib/src/PublicKeyCredentialCreationOptions.php on line 276"

{
    "message": "Request failed with status code 500",
    "name": "AxiosError",
    "stack": "AxiosError: Request failed with status code 500\n    at settle (http://127.0.0.1:5173/node_modules/.vite/deps/axios.js?v=62d26892:1193:12)\n    at XMLHttpRequest.onloadend (http://127.0.0.1:5173/node_modules/.vite/deps/axios.js?v=62d26892:1421:7)",
    "config": {
        "transitional": {
            "silentJSONParsing": true,
            "forcedJSONParsing": true,
            "clarifyTimeoutError": false
        },
        "adapter": "xhr",
        "transformRequest": [
            null
        ],
        "transformResponse": [
            null
        ],
        "timeout": 0,
        "xsrfCookieName": "XSRF-TOKEN",
        "xsrfHeaderName": "X-XSRF-TOKEN",
        "maxContentLength": -1,
        "maxBodyLength": -1,
        "env": {},
        "headers": {
            "Accept": "application/json, text/plain, */*",
            "Content-Type": "application/json",
            "X-Requested-With": "XMLHttpRequest",
            "X-XSRF-TOKEN": "eyJpdiI6IjJ6aSsrVXU1VUNFK0RzU3MzTDBrTnc9PSIsInZhbHVlIjoiWkFUQmFmKzdOV0xXNXg2VmJuK0o1bHhjUlRiTGFvYWtiUGxGOE9TRkV6WUx1YlYwdFBYWDN0RTREa0t6cUhsVGRLdUV5NGtsQVUzd2ZtN1RBQlloeGMrcVFxQlRRT0FrWkpsVGFJNHJBTHFHNGlVeUNFeXQrcUlvUUVqV2p0ZzUiLCJtYWMiOiIzZTRlNTc4MDUwZWI4MTFiZmM5ZjhhZGU2OWYyMzA5NDgxNDFkNWNjNWY1YTMyMmRlYWFiZjcwOGM5OTVlZmVjIiwidGFnIjoiIn0="
        },
        "withCredentials": true,
        "method": "post",
        "url": "/webauthn/keys",
        "data": "{\"id\":\"tKxA0c1nPWEkz-qS3RtxcQ\",\"rawId\":\"tKxA0c1nPWEkz-qS3RtxcQ\",\"response\":{\"attestationObject\":\"o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViU9xbOAAT0jVAxMHqooSIuKuvr9Kzf1O95igTodYA34JdZAAAAAFMRJtbnF0FckyA9mqaYEjkAELSsQNHNZz1hJM_qkt0bcXGlAQIDJiABIVggdd8-Rtqlt2q8lgSkDpQRou15aCHPHBnN_NdrzDoMvqUiWCCphPUnccpPoHXBkeNgaBCfJLZnCDyGYvfwBHqEQkqOJg\",\"clientDataJSON\":\"eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiWVRUTlVaVktuX2FtUVNselVHUXcwTTFnc28zYVRVdzV6RzMzUEZnS3R2TSIsIm9yaWdpbiI6Imh0dHBzOi8vZGV2LnRlc3QiLCJjcm9zc09yaWdpbiI6ZmFsc2V9\",\"transports\":[\"internal\",\"hybrid\"],\"publicKeyAlgorithm\":-7,\"publicKey\":\"pQECAyYgASFYIHXfPkbapbdqvJYEpA6UEaLteWghzxwZzfzXa8w6DL6lIlggqYT1J3HKT6B1wZHjYGgQnyS2Zwg8hmL38AR6hEJKjiY\",\"authenticatorData\":\"9xbOAAT0jVAxMHqooSIuKuvr9Kzf1O95igTodYA34JdZAAAAAFMRJtbnF0FckyA9mqaYEjkAELSsQNHNZz1hJM_qkt0bcXGlAQIDJiABIVggdd8-Rtqlt2q8lgSkDpQRou15aCHPHBnN_NdrzDoMvqUiWCCphPUnccpPoHXBkeNgaBCfJLZnCDyGYvfwBHqEQkqOJg\"},\"type\":\"public-key\",\"clientExtensionResults\":{},\"authenticatorAttachment\":\"platform\",\"name\":\"test key\"}"
    },
    "code": "ERR_BAD_RESPONSE",
    "status": 500
}

How to reproduce

I use https://github.com/asbiin/laravel-webauthn (but I believed that this error comes from the webauth-lib) and https://simplewebauthn.dev/docs/packages/browser want to register a new key but got that error (must be type of array).

Here is my JavaScript code

let attResp;
        async function func_registerkey () {
            try {
                const webauthn_options = await axios.post('/webauthn/keys/options');
                console.log(webauthn_options);
                attResp = await startRegistration(webauthn_options.data.publicKey);
                attResp.name = "test key";
                console.log(attResp);
                const webauthn_store = await axios.post('/webauthn/keys', attResp); // <--- Error here.
                console.log(webauthn_store);
            } catch (error) {
                console.log(error);
                if (error.name === 'InvalidStateError') {
                    toast.error('Error: Authenticator was probably already registered by user');
                } else {
                    toast.error(error);
                }

                throw error;
            }
        }

Additional Context

image image

rico commented 1 year ago

@ppmint1 it looks like some serialization does not happen automatically anymore in 4.7.1

I my case, doing it manually gets the things working again.

In the registration controller, where I create the PublicKeyCredentialCreationOptions and serialize them:

$serializedOptions = $publicKeyCredentialCreationOptions->jsonSerialize()

I have to serialize the 'parts' as follows:

$serializedOptions['rp'] = $serializedOptions['rp']->jsonSerialize();
$serializedOptions['user'] = $serializedOptions['user']->jsonSerialize();
$serializedOptions['extensions'] = $serializedOptions['extensions']->jsonSerialize();
$serializedOptions['authenticatorSelection'] = $serializedOptions['authenticatorSelection']->jsonSerialize();

In the login controller, I had to do basically the same with the PublicKeyCredentialDescriptor:

$allowedCredentials = [
  ...$authenticators,
  ...[
    PublicKeyCredentialSource::createFromArray($publicKey)->getPublicKeyCredentialDescriptor()->jsonSerialize()
  ]
];

$publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::create(
  challenge: $challenge,
  allowCredentials: $allowedCredentials,
);

$serializedOptions = $publicKeyCredentialRequestOptions->jsonSerialize();
Spomky commented 1 year ago

$serializedOptions = $publicKeyCredentialCreationOptions->jsonSerialize()

🤔 humm. Why are you using $serializedOptions->jsonSerialize(); instead of json_encode($serializedOptions);? Edit: The goal of the method jsonSerialize that comes from the interface JsonSerializable is to customize the result of the method json_encode. It is not supposed to but called directly.

rico commented 1 year ago

Why are you using $serializedOptions->jsonSerialize(); instead of json_encode($serializedOptions);?

Hmmm, never thought about it to be honest. Started with an implementation where jsonSerialize() was used ....

But good point, thanks!

Spomky commented 11 months ago

Closing as answered. Feel free to open it again if necessary. Regards.

github-actions[bot] commented 10 months ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.