@fullstacktechnyc/passwordless-core
v1.3.0
Published
Runtime-agnostic passwordless auth core: passkeys, magic links, email OTP 2FA, sessions. No platform owner.
Downloads
743
Maintainers
Readme
@fullstacktechnyc/passwordless-core
The runtime-agnostic engine: passkeys + magic links with email-OTP 2FA,
optional TOTP, and revocable server-side sessions. One createAuth() object
exposes the whole auth surface as a standard fetch handler — it runs unchanged
on Cloudflare Workers, Node, Bun, and Deno.
Sovereign by construction: the only third-party code is @simplewebauthn/server
(WebAuthn) and otpauth (TOTP), each touched by a single module. Everything else
— sessions, magic-link tokens, OTPs, HMAC — is WebCrypto.
Install
npm i @fullstacktechnyc/passwordless-core
# plus a storage + email adapter:
npm i @fullstacktechnyc/passwordless-storage-sql @fullstacktechnyc/passwordless-emailUse
import { createAuth } from "@fullstacktechnyc/passwordless-core";
import { createSqlStorage, d1Driver } from "@fullstacktechnyc/passwordless-storage-sql";
import { cloudflareEmail } from "@fullstacktechnyc/passwordless-email";
const auth = createAuth({
storage: createSqlStorage(d1Driver(env.DB)),
email: cloudflareEmail(env.EMAIL, { from: "[email protected]" }),
secret: env.AUTH_SECRET, // 32+ random chars
rpID: "example.com", // WebAuthn relying-party id
rpName: "Example",
origin: "https://example.com",
magicLink: { from: "[email protected]" },
secondFactor: "otp", // email code as the magic-link 2FA step
allowedEmails: ["@example.com"], // optional private gate
});
// One handler owns /api/auth/*; return null-fallthrough to your app.
export default {
fetch: async (req) => (await auth.handler(req)) ?? serveApp(req),
};
// Anywhere else:
const who = await auth.getSession(req); // { session, user } | nullRoutes owned by auth.handler
| Method + path | Purpose |
| --- | --- |
| POST /api/auth/magic-link/start | Email a sign-in link |
| GET /api/auth/magic-link/verify | Consume the link → session (+ OTP 2FA) |
| POST /api/auth/otp/verify | Submit the emailed 2FA code |
| POST /api/auth/passkey/authenticate/{start,finish} | Usernameless passkey login |
| POST /api/auth/passkey/register/{start,finish} | Add a passkey (authed) |
| GET /api/auth/passkey/list · POST /passkey/{rename,delete} | Manage passkeys (authed) |
| POST /api/auth/totp/{enroll/start,enroll/verify,verify} | Authenticator-app 2FA |
| POST /api/auth/recovery/{generate,redeem} · GET /recovery/status | Account recovery codes |
| GET /api/auth/session · POST /api/auth/logout · POST /api/auth/logout-all | Session state / revoke one / revoke all |
The browser side of passkeys/magic-link/OTP is handled for you by
@fullstacktechnyc/passwordless-react (or its headless ./client).
Security defaults
Single-use, attempt-capped codes · constant-time comparisons · opaque hashed sessions (revocable) · rate limiting · no account enumeration · HttpOnly+Secure cookies. See the source — it's small and meant to be read.
