Skip to main content
The emailPassword plugin handles user registration, sign-in, email verification, and password management. Passwords are hashed with scrypt (N=16384, r=8, p=1) using Node’s built-in node:crypto module, no extra dependencies.
Because scrypt uses node:crypto, the emailPassword plugin requires a Node.js runtime. It does not run on Cloudflare Workers, Deno Deploy, or Vercel Edge Functions as-is. If you need edge runtime support, replace the hasher with PBKDF2 via the Web Crypto API using the password.hash and password.verify options shown below.
If you prefer username-based auth instead of email, see the username plugin.

Setup

By default, sign-in requires email verification. In development, set requireVerification: false to skip this. Without it, users who sign up cannot sign in until they verify their email.

Dev vs production config

1

Install the plugin

lib/kavach.ts
2

Mount the handler

The plugin registers endpoints automatically. You still need to route incoming requests to KavachOS from your framework adapter.
app/api/auth/[...kavach]/route.ts

Sign up

POST /auth/sign-up Creates a new user account. Returns a session token on success. If requireVerification is true, the user also receives a verification email immediately.
Sign up (client)
Request body
string
required
User email address.
string
required
Password. Must meet the configured length and complexity rules.
string
Display name for the user.
Response 201 Created
You can extend the user record with additional fields (e.g. avatarUrl, role) by hooking into the onUserCreated lifecycle. The name field is the only optional built-in.

Sign in

POST /auth/sign-in Authenticates a user and returns a session token. Always returns a generic “Invalid email or password” message for wrong credentials. The response does not distinguish between a missing account and a wrong password.
Sign in (client)
Request body
string
required
User email address.
string
required
User password.
Response 200 OK
Error codes

Sign out

POST /auth/sign-out Revokes the current session. The session cookie is cleared by the server.
Sign out (client)

Email verification

When requireVerification: true, sign-in returns 401 with code EMAIL_NOT_VERIFIED until the user clicks the link sent at registration. The link hits your app’s callback URL, which should POST the token to /auth/verify-email.

Enable verification

lib/kavach.ts

Handle the callback

Your app receives the token in the URL. Send it to the endpoint:
app/auth/verify-email/page.tsx (Next.js)

Resend verification

If the user lost the email, they can trigger a new one:
Resend verification (client)
The sendVerificationEmail callback runs synchronously during sign-up. If your email provider is slow, use fire-and-forget: wrap the send call in void someEmailFn(...) so it does not block the response. Make sure you have proper error logging so failures are visible.

Email enumeration protection

When requireVerification: true, the sign-up endpoint returns 201 even if the email is already registered. The existing user gets a “someone tried to create an account with your email” notification instead of an error being exposed to the attacker. This prevents an attacker from harvesting valid email addresses by observing which registrations fail with 409 Conflict. The password reset endpoint (/auth/request-reset) follows the same pattern. It always returns 200 regardless of whether the email exists. See OWASP: Prevent Username Enumeration for background.

Password reset

1

Configure the reset email

lib/kavach.ts
2
POST /auth/request-reset
Request reset (client)
3

Submit the new password

POST /auth/reset-passwordYour reset page receives the token from the URL. Collect the new password and post both:
Reset password (client)
On success, all sessions for that user are revoked. They will need to sign in again.
Rate limiting on /auth/request-reset is set to 3 requests per 60 seconds by default. Do not remove it. Without a rate limit, the endpoint can be used to spam users with reset emails.

Change password

POST /auth/change-password Requires an active session. The endpoint reads the user identity from the session, no userId in the request body.
Change password (client)
Request body
string
required
The user’s current password.
string
required
The new password. Must meet configured strength requirements.
The current session stays active after a successful change. Other sessions for the same user are not revoked (unlike a password reset). If you want to sign out other devices, revoke sessions explicitly via the session management API.

Password configuration

Passwords are hashed with scrypt using Node’s built-in node:crypto module (N=16384, r=8, p=1, keylen=64 bytes). These parameters match OWASP interactive login recommendations and require no external dependencies.

Custom strength rules

lib/kavach.ts

Custom hash function

If you need a different algorithm, bring your own hash and verify functions. Edge runtimes (Cloudflare Workers, Deno Deploy, Vercel Edge): use PBKDF2 via the Web Crypto API, which is available everywhere:
lib/kavach.ts
Node.js with argon2id: use @node-rs/argon2 for stronger memory-hard hashing on full Node.js servers:
lib/kavach.ts
The default scrypt implementation is fine for most Node.js deployments. Use a custom hasher when you are migrating from an existing system, need edge runtime support, or want stronger memory-hard guarantees.

Configuration reference

string
required
Base URL for verification and reset links. Links are constructed as appUrl + /auth/verify-email?token=… and appUrl + /auth/reset-password?token=…
required
Called after sign-up with the recipient email, raw token, and fully constructed URL.
required
Called when a user requests a password reset. Same signature as sendVerificationEmail.
boolean
default:"true"
Block sign-in until the user clicks the email verification link.
number
default:"86400 (24h)"
Verification token lifetime in seconds.
number
default:"3600 (1h)"
Password reset token lifetime in seconds.
Last modified on April 18, 2026