🧠 What does passwordless mean?
Passwordless authentication means the user doesn’t type or even have a password.
Instead, they log in using a secure credential stored on their device — for example:
- a biometric (fingerprint, Face ID),
- a PIN, or
- a hardware key (like YubiKey). So the “something you know” (password) is replaced by something you have or are.
🔐 What is WebAuthn?
WebAuthn (Web Authentication API) is a modern W3C web standard created by FIDO Alliance and major browsers (Google, Apple, Mozilla).
It allows websites to use:
- Public-key cryptography instead of passwords.
- Secure devices (like your phone or laptop’s TPM or Secure Enclave) to generate and store credentials.
👉 When you register or log in with WebAuthn:
- Your browser talks to your device (called an authenticator).
- The device creates a key pair:
- Public key → sent to the server
- Private key → safely stored in the device, never leaves it
- When logging in, your device signs a challenge with the private key — proving it’s really you.
🧩 In short:
Passwordless with WebAuthn = login using cryptographic keys stored securely on your device, verified by your biometrics — no password ever.
It’s the same tech behind Passkeys, which are basically user-friendly WebAuthn credentials synced through your OS (Apple, Google, Microsoft).
Coding time
Let's make something like passwordless login and registration system in Node.js. For this we need a auth-verify (library for making passkeys use it for passwordless logins)
Step 1:
Let's install essential packages:
npm i express auth-verify path
Step 2:
Making web app for asking user getting passkeys:
const express = require('express');
const path = require('path');
const AuthVerify = require('auth-verify');
const app = express();
const PORT = 3000;
// Middleware
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));
// Serve main page
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'index.html'));
});
// Start server
app.listen(PORT, ()=> console.log(`🚀 Server running at http://localhost:${PORT}`)))
Step 3:
Let's initialize auth-verify for making passkeys
// Create AuthVerify instance
const auth = new AuthVerify({
passExp: "2m", // Challenge expiration
rpName: "MyApp", // Display name of your app
storeTokens: "memory", // Use in-memory store (good for dev)
});
// Example user (you can use a database later)
const user = {
id: "user1",
username: "john_doe",
credentials: [], // stores WebAuthn credentials
};
Step 4:
We'll make API routes for making passkey. It consisted of two steps:
- We'll make passkey and we'll send it client
- Client respond our passkey option request and if it successful we can save it in db or memory for user data.
// 👉 Step 1: Start registration
app.post('/api/register/start', async (req, res) => {
await auth.passkey.register(user);
// get WebAuthn options for the client
const options = auth.passkey.getOptions();
console.log(options);
// Send to frontend (don’t wrap inside `{ options }`)
res.json(options);
});
// 👉 Step 2: Finish registration
app.post('/api/register/finish', async (req, res) => {
const clientResponse = req.body; // frontend sends credential data
try {
const result = await auth.passkey.finish(clientResponse, user);
if (result.success) {
// Save the credential to user’s list
user.credentials.push(result.credential);
return res.json({ success: true, message: "Passkey registered successfully!" });
}
res.status(400).json({ success: false, message: "Verification failed" });
} catch (err) {
console.error("Error verifying passkey:", err);
res.status(500).json({ success: false, message: "Server error" });
}
});
Step 5:
Now we'll handle frontend (client) part because when we send passkey options browser should ask where to save our passkey (maybe in flash drive or phone). For handling this we need a auth-verify client we can simply import by source file:
<!-- ✅ Load auth-verify client -->
<script src="https://cdn.jsdelivr.net/gh/jahongir2007/auth-verify/auth-verify.client.js"></script>
<!DOCTYPE html>
<html>
<head>
<title>Getting passkey</title>
</head>
<body>
<h1>Getting passkey</h1>
<button id="register">Register Passkey</button>
<!-- ✅ Load client helper -->
<script src="https://cdn.jsdelivr.net/gh/jahongir2007/auth-verify/auth-verify.client.js"></script>
<script>
const auth = new AuthVerify({
apiBase: "http://localhost:3000"
});
const registerBtn = document.querySelector("#register");
registerBtn.addEventListener('click', async () => {
try {
const publicKey = await auth.post('/api/register/start').data(); // getting passkey from backend
console.log(publicKey);
const issuedPublicKey = await auth.issue(publicKey); // browser here asks from user where to save the passkey
const result = await auth.post('/api/register/finish').data(issuedPublicKey); //sending passkey new data to backend
alert(result.message || "Registration complete!");
} catch (err) {
console.error(err);
alert("Something went wrong: " + err.message);
}
});
</script>
</body>
</html>
The result:
Conclusion
I hope you now fully understood about passwordless authentication

Top comments (0)