DEV Community

Cover image for Meaning of passwordless authentication for beginners (and let's make it)
Jahongir Sobirov
Jahongir Sobirov

Posted on

Meaning of passwordless authentication for beginners (and let's make it)

🧠 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:

  1. Your browser talks to your device (called an authenticator).
  2. The device creates a key pair:
    • Public key → sent to the server
    • Private key → safely stored in the device, never leaves it
  3. 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
Enter fullscreen mode Exit fullscreen mode

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}`)))
Enter fullscreen mode Exit fullscreen mode

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
};
Enter fullscreen mode Exit fullscreen mode

Step 4:

We'll make API routes for making passkey. It consisted of two steps:

  1. We'll make passkey and we'll send it client
  2. 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" });
  }
});
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode
<!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>
Enter fullscreen mode Exit fullscreen mode

The result:

The result

Conclusion

I hope you now fully understood about passwordless authentication

Top comments (0)