Reputation: 1956
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
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
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