Anters Bear
Anters Bear

Reputation: 1956

Storing and Retrieving Data with WebAuthn

I would like to use WebAuthn to securely store and retrieve some senstive information on mobile Safari.

I mention Safari specifically because it is my main target browser and the storage and retrieval of password credentials is unsupported there. Ideally, I would like to use the device biometric sensors on iOS and as I understand it, it's only possible using WebAuthn and a public key credential.

I don't want to have to create a server based service that has to store this sensitive information and to evaluate credential signatures. I would like to implement entirely on the client side.

Is there any straightforward or workaround solution to achieve this?

Upvotes: 0

Views: 292

Answers (2)

Wazime
Wazime

Reputation: 1698

I wrote this demo code for doing it.
So it's possible.

I am still waiting for people to answer the question about how bad is it, and if there is any problem with it.

How bad it is store data into webAuthN userHandle?

[attaching the demo code here for you to try as well]

const LOCAL_STORAGE_KEY = "credentialId";

async function storeSensitiveDataToWebauthN(sensitiveString) {
  let userHandle = new TextEncoder().encode(sensitiveString);

  let createOptions = {
    publicKey: {
      rp: { name: "My local test" },
      user: {
        id: userHandle,
        name: "[email protected]",
        displayName: "Example User",
      },
      challenge: new Uint8Array(32), // In practice, should be generated by the server
      pubKeyCredParams: [{ alg: -7, type: "public-key" }],
    },
  };

  const credential = await navigator.credentials.create(createOptions);

  localStorage.setItem(
    LOCAL_STORAGE_KEY,
    btoa(String.fromCharCode.apply(null, new Uint8Array(credential.rawId)))
  );
}

async function retrieveSensitiveDataFromWebauthN() {
  let credentialId = localStorage.getItem(LOCAL_STORAGE_KEY);

  // Correctly decode the Base64-encoded credential ID before using it

  const decodedCredentialId = Uint8Array.from(atob(credentialId), (c) =>
    c.charCodeAt(0)
  );

  let getCredentialDefaultArgs = {
    publicKey: {
      challenge: new Uint8Array(32), // Should be securely generated
      allowCredentials: [{ id: decodedCredentialId, type: "public-key" }],
    },
  };

  const assertion = await navigator.credentials.get(getCredentialDefaultArgs);

  let userHandle = assertion.response.userHandle
    ? new TextDecoder().decode(assertion.response.userHandle)
    : null;

  return userHandle;
}

export async function testWebAuthN() {
  const sensitiveString = "USER_HASHED_PASSWORD";
  if (localStorage.getItem(LOCAL_STORAGE_KEY) === null) {
    await storeSensitiveDataToWebauthN(sensitiveString);
  }
  const dateRecieved = await retrieveSensitiveDataFromWebauthN();
  if (!dateRecieved === sensitiveString) {
    console.error("Error: WebauthN failed to retrieve the correct data");
  }
  console.log("dateRecieved = ", dateRecieved);
}

Upvotes: 0

Tim
Tim

Reputation: 1240

A passkey is a credential used for phishing-resistant authentication. It is not designed to store arbitrary information nor is WebAuthn designed to store or retrieve arbitrary information.

Upvotes: 4

Related Questions