Yes, it is possible with your smart phone and your laptop(if fitted with a biometric scanner)
Always keep in mind that Google is your friend, I have found the following article from Vasyl Boroviak who explains the workings behind the scanner with solid code. For future users I will post his code here, I do however suggest to read the entire article to understand the why, who, when and how of using the biometric scanner.
HIS ARTICLE is an indepth description, trust this will work for you -
Here is a server-side pseudo code::
const deviceId = req.cookies["my-long-live-cookie"];
const device = await devices.findById(deviceId);
if (!device) return {};
device.challenge = require("crypto").randomBytes(16).toString("hex")
await device.save();
return { challenge: device.challenge };
The devices table minimum schema looks like this:
_id: String,
challenge: String,
counter: Number,
publicKey: String,
attestationObject: String,
clientDataJSON: String,
userAgent: String,
user: ObjectId,
create-public-key
This is the point when your browser invokes the WebAuthn API to scan user’s finger for the first time. (See explanation below.)
import { decode: base64urlDecode } from "base64url";
const attestation = await navigator.credentials.create({
publicKey: {
authenticatorSelection: {
authenticatorAttachment: "platform",
userVerification: "required"
},
challenge: base64urlDecode(challenge),
rp: { id: document.domain, name: "My Acme Inc" },
user: {
id: base64urlDecode(user.id),
name: user.email,
displayName: user.fullName
},
pubKeyCredParams: [
{ type: "public-key", alg: -7 },
{ type: "public-key", alg: -257 }
]
}
});
navigator.credentials.preventSilentAccess();
import { encode: base64urlEncode } from "base64url";
function publicKeyCredentialToJSON(pubKeyCred) {
if (pubKeyCred instanceof ArrayBuffer) {
return base64urlEncode(pubKeyCred);
} else if (pubKeyCred instanceof Array) {
return pubKeyCred.map(publicKeyCredentialToJSON);
} else if (pubKeyCred instanceof Object) {
const obj = {};
for (let key in pubKeyCred) {
obj[key] = publicKeyCredentialToJSON(pubKeyCred[key]);
}
return obj;
} else return pubKeyCred;
}
const webAuthnAttestation = publicKeyCredentialToJSON(attestation);
fetch("example.com/save-public-key", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: webAuthnAttestation
});
The returned webAuthnAttestation looks like this:
{
id: "a_very_very_long_string",
type: "public-key",
response: {
attestationObject: "even_longer_string",
clientDataJSON: "another_very_long_string"
}
}
Decoding the clientDataJSON string.
const base64url = require("base64url");
clientDataJSON = JSON.parse(base64url.decode(clientDataJSON));
assert(clientDataJSON.challenge === device.challenge)
assert(clientDataJSON.origin === "example.com");
assert(clientDataJSON.type === "webauthn.create");
const base64url = require("base64url");
const cbor = require("cbor");
function parseAttestationObject(attestationObject) {
const buffer = base64url.toBuffer(attestationObject);
return cbor.decodeAllSync(buffer)[0];
}
const makeCredsReponse = parseAttestationObject(attestationObject);
Pseudo code:
device.counter = 0;
device.publicKey = publicKey;
device.type = type;
device.attestationObject = attestationObject;
device.clientDataJSON = clientDataJSON;
device.userAgent = req.headers["user-agent"];
await device.save();
This is for a re-login but can be adjusted to suit your needs i.e. comparison to your already saved prints...