> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kavachos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Username and password

> Authenticate users with a unique username and password via `usernamePassword`. Covers length limits, pattern validation, and uniqueness enforcement at sign-up.

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

```typescript title="lib/kavach.ts" theme={"system"}
import { createKavach } from 'kavachos';
import { usernamePassword } from 'kavachos/auth'; // [!code highlight]

const kavach = await createKavach({
  database: { provider: 'postgres', url: process.env.DATABASE_URL! },
  secret: process.env.KAVACH_SECRET!,
  baseUrl: 'https://auth.example.com',
  plugins: [
    usernamePassword({ // [!code highlight]
      minUsernameLength: 3, // [!code highlight]
      maxUsernameLength: 32, // [!code highlight]
      allowedPattern: /^[a-z0-9_-]+$/i, // [!code highlight]
    }), // [!code highlight]
  ],
});
```

Mount the handler the same way as any other plugin, see [adapters](/adapters).

## Sign up

`POST /auth/sign-up`

```typescript title="Sign up (client)" theme={"system"}
const res = await fetch('/auth/sign-up', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    username: 'ada_lovelace', // [!code highlight]
    password: 'correct horse battery',
    name: 'Ada Lovelace', // optional display name
  }),
});

const { user, token } = await res.json();
```

**Error codes**

| Code                 | Status | Meaning                         |
| -------------------- | ------ | ------------------------------- |
| `USERNAME_TAKEN`     | 409    | Username is already registered  |
| `USERNAME_INVALID`   | 422    | Does not match `allowedPattern` |
| `USERNAME_TOO_SHORT` | 422    | Below `minUsernameLength`       |

## Sign in

`POST /auth/sign-in`

```typescript title="Sign in (client)" theme={"system"}
const res = await fetch('/auth/sign-in', {
  method: 'POST',
  credentials: 'include',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    username: 'ada_lovelace', // [!code highlight]
    password: 'correct horse battery', // [!code highlight]
  }),
});

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.

```typescript title="Change username (client)" theme={"system"}
const res = await fetch('/auth/change-username', {
  method: 'POST',
  credentials: 'include',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    newUsername: 'ada_v2', // [!code highlight]
    password: 'correct horse battery', // current password required // [!code highlight]
  }),
});
```

## Configuration reference

<ParamField path="minUsernameLength" type="number" default={3}>Minimum username length.</ParamField>
<ParamField path="maxUsernameLength" type="number" default={32}>Maximum username length.</ParamField>
<ParamField path="allowedPattern" type="RegExp" default="/^[a-z0-9_-]+$/i">Regex that the username must match. Applied after length validation.</ParamField>
<ParamField path="caseSensitive" type="boolean" default="false">Whether usernames are case-sensitive during lookup.</ParamField>
