Skip to main content
The usernamePassword plugin lets users pick a username at registration and sign in with it. It is a drop-in alternative to the email-password plugin for apps where email addresses are not the primary identity.

Setup

lib/kavach.ts
import { createKavach } from 'kavachos';
import { usernamePassword } from 'kavachos/auth'; 

const kavach = await createKavach({
  database: { provider: 'postgres', url: process.env.DATABASE_URL! },
  secret: process.env.KAVACH_SECRET!,
  baseUrl: 'https://auth.example.com',
  plugins: [
    usernamePassword({ 
      minUsernameLength: 3, 
      maxUsernameLength: 32, 
      allowedPattern: /^[a-z0-9_-]+$/i, 
    }), 
  ],
});
Mount the handler the same way as any other plugin, see adapters.

Sign up

POST /auth/sign-up
Sign up (client)
const res = await fetch('/auth/sign-up', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    username: 'ada_lovelace', 
    password: 'correct horse battery',
    name: 'Ada Lovelace', // optional display name
  }),
});

const { user, token } = await res.json();
Error codes
CodeStatusMeaning
USERNAME_TAKEN409Username is already registered
USERNAME_INVALID422Does not match allowedPattern
USERNAME_TOO_SHORT422Below minUsernameLength

Sign in

POST /auth/sign-in
Sign in (client)
const res = await fetch('/auth/sign-in', {
  method: 'POST',
  credentials: 'include',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    username: 'ada_lovelace', 
    password: 'correct horse battery', 
  }),
});

const { user, session } = await res.json();
Returns 401 with code INVALID_CREDENTIALS for any bad username/password combination. The response never reveals whether the username exists.

Change username

POST /auth/change-username Requires an active session. Usernames must still be unique after the change.
Change username (client)
const res = await fetch('/auth/change-username', {
  method: 'POST',
  credentials: 'include',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    newUsername: 'ada_v2', 
    password: 'correct horse battery', // current password required
  }),
});

Configuration reference

minUsernameLength
number
default:3
Minimum username length.
maxUsernameLength
number
default:32
Maximum username length.
allowedPattern
RegExp
default:"/^[a-z0-9_-]+$/i"
Regex that the username must match. Applied after length validation.
caseSensitive
boolean
default:"false"
Whether usernames are case-sensitive during lookup.
Last modified on April 18, 2026