mdn / browser-compat-data

This repository contains compatibility data for Web technologies as displayed on MDN
https://developer.mozilla.org
Creative Commons Zero v1.0 Universal
5k stars 2.01k forks source link

api.CredentialsContainer.get.publicKey_option.extensions - largeBlob not working on Chrome Android (130) #25002

Open flAIght-dev opened 2 weeks ago

flAIght-dev commented 2 weeks ago

What type of issue is this?

Incorrect support data (example: BrowserX says "86" but support was added in "40")

What information was incorrect, unhelpful, or incomplete?

largeBlob extension is reported as supported on Android Chrome since 113. Testing it on 130 I am not able to use it.

What browsers does this problem apply to, if applicable?

Chromium (Chrome, Edge 79+, Opera, Samsung Internet)

What did you expect to see?

I expected to be able to read and write a blob

Did you test this? If so, how?

The credential is created without errors. On the write call "written" is undefined. No errors in the read call but the blob is (obviously) empty.

Here is the test code:


  let username = "test"
  let name = "test name"

  const firstSalt = new Uint8Array(new Array(32).fill(1)).buffer;

  navigator.credentials.create({
    publicKey: {
      challenge: Uint8Array.from([1, 2, 3, 4]),
        rp: {
            name: "Example RP",
            id: "api.test.org",
        },
      user: {
        id: Uint8Array.from(username.split("").map(c => c.codePointAt(0))),
        name: name,
        displayName: name,
      },
        pubKeyCredParams: [
            { alg: -8, type: "public-key" },   // Ed25519
            { alg: -7, type: "public-key" },   // ES256
            { alg: -257, type: "public-key" }, // RS256                        
        ],
        authenticatorSelection: {
            userVerification: "required",
            residentKey: "required",
            //requireResidentKey: true
        },

      extensions: {
        prf: {
            eval: {
                first: firstSalt,
            },
        },
        largeBlob: {
          support: "required",
        },
        "credProps": true
      }
    }
  }).then(credential => {
    console.log(credential);
    console.log(credential.getClientExtensionResults());
    window.localStorage.credentialId = arrayBufferToBase64(credential.rawId);

    console.log("Credential successfully created! Try logging in.");

    console.log("extension results " + JSON.stringify(credential.getClientExtensionResults()));

  }).catch(error => {
    console.log(error.toString());
  });

Read blob

 navigator.credentials.get({

    publicKey: {
        challenge: new Uint8Array([1, 2, 3, 4]), // Example value
        rp: {
            name: "Example RP",
            id: "api.test.org",
        },
        user: {
            id: Uint8Array.from(username, c => c.charCodeAt(0)), // User-provided ID
            name: username,
            displayName: name,
        },
        pubKeyCredParams: [
            { alg: -8, type: "public-key" },   // Ed25519
            { alg: -7, type: "public-key" },   // ES256
            { alg: -257, type: "public-key" }, // RS256                        
        ],
        authenticatorSelection: {
            userVerification: "required",
            residentKey: "required"
        },
      extensions: {
        largeBlob: {
          read: true
        }
      }
    }

  }).then(assertion => {
    console.log("Blob read", assertion);
    console.log(assertion.getClientExtensionResults());

    console.log("assertion " + assertion);
    console.log("extension results " + JSON.stringify(assertion.getClientExtensionResults()));

    if (assertion.getClientExtensionResults().largeBlob) {
      console.log("Blob found");
      let blobContent = assertion.getClientExtensionResults().largeBlob;
      console.log("blobContent " + blobContent);
      console.log("blobContent type " + (typeof blobContent));
      console.log("blobContent type 2 " + Object.prototype.toString.call(blobContent));
      console.log("blobContent stringify " + JSON.stringify(blobContent));
      let blobString = new TextDecoder().decode(blobContent.blob);
      console.log("blobString: " + blobString);
      console.log("Blob: ", blobString);
      console.log("Blob read successfully! Content: " + blobString);
    } else {
      console.log("No blob found.");
    }
  }).catch(error => {
    console.log(error.toString());
  });

Write blob


  let blobValue = "test blob value"

  const blob = new TextEncoder().encode(blobValue);

  console.log("Blob write content: " + blobValue);
  console.log("Blob write content length: " + blob.length);
  console.log("Blob write content encoded: " + Object.prototype.toString.call(blob));

  navigator.credentials.get({
    publicKey: {
        challenge: new Uint8Array([1, 2, 3, 4]), // Example value
        rp: {
            name: "Example RP",
            id: "api.test.org",
        },
        user: {
            id: Uint8Array.from(username, c => c.charCodeAt(0)), // User-provided ID
            name: username,
            displayName: name,
        },
        pubKeyCredParams: [
            { alg: -8, type: "public-key" },   // Ed25519
            { alg: -7, type: "public-key" },   // ES256
            { alg: -257, type: "public-key" }, // RS256                        
        ],
        authenticatorSelection: {
            userVerification: "required",
            residentKey: "required"
        },
      extensions: {
        largeBlob: {
          write: blob
        }
      }
    }
  }).then(assertion => {
    console.log("Blob write attempt", assertion);
    console.log(assertion.getClientExtensionResults());

    if (assertion.getClientExtensionResults().largeBlob.written) {
      console.log("Blob written");
      console.log("Blob written successfully!");

      console.log("write blob stringify " + JSON.stringify(assertion.getClientExtensionResults()));

    } else {
      console.log("Blob not written");
      console.log("Failed to write blob.");
    }
  }).catch(error => {
    console.log(error.toString());
  });

Can you link to any release notes, bugs, pull requests, or MDN pages related to this?

No response

Do you have anything more you want to share?

The same code works fine with Chrome on iOS.

MDN URL

https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API/WebAuthn_extensions

MDN metadata

MDN page report details * Query: `api.CredentialsContainer.get.publicKey_option.extensions` * Report started: 2024-11-08T12:29:50.653Z
IanNicki commented 4 days ago

Just found another problem on the iOS side in combination with Chrome > 130:

As reported by @flAIght-dev, reading and writing to LargeBlobs on iOS devices using Chrome is possible. The problem is that only a passkey stored in the iCloud keychain is supported. Selecting a FIDO2 key as "Other Sign In Option" fails with the error message "Large blob is undefined -- your browser probably doesn't know about it" on the testing website.

I can't tell if this is due to Chrome on iOS or the iOS CTAP implementation. Does anyone know how to find this out?

By the way, the Passkey Spotlight Week has just started for Android and Chrome development. I have already posted a question concerning the LargeBlob support topic in Android. Maybe you should post a short question on this topic as well. They will be answering some of them this Thursday (21/11/24) on their Youtube Livestream Channel. Questions can be submitted in the comments or via their feedback form.