---
title: Password Plugin
description: Password-based authentication with bcrypt or argon2id hashing, password policies, breach checking, and account lifecycle management.
---

The `password` plugin provides email/password authentication with a full account lifecycle: sign-up, sign-in, forgot password, reset password, change password, and email verification. It is the most commonly used plugin and is typically the first one added to any Authsome engine.

## Setup

```go
import (
    "github.com/xraph/authsome"
    "github.com/xraph/authsome/plugins/password"
)

eng, err := authsome.NewEngine(
    authsome.WithStore(store),
    authsome.WithPlugin(password.New()),
    authsome.WithConfig(authsome.Config{
        AppID: "myapp",
        Password: authsome.PasswordConfig{
            MinLength:        10,
            RequireUppercase: true,
            RequireLowercase: true,
            RequireDigit:     true,
            RequireSpecial:   false,
            Algorithm:        "bcrypt",
            BcryptCost:       12,
            CheckBreached:    true,
        },
    }),
)
```

The plugin constructor accepts optional per-plugin configuration:

```go
password.New(password.Config{
    AllowedDomains: []string{"mycompany.com", "partner.com"},
})
```

## Implemented interfaces

| Interface | Purpose |
|-----------|---------|
| `Plugin` | Base plugin identity (`"password"`) |
| `OnInit` | Captures the store reference from the engine |
| `BeforeSignUp` | Validates email domain restrictions before account creation |
| `RouteProvider` | Core auth routes are handled by the engine API handler |
| `AuthMethodContributor` | Reports `"password"` as a linked auth method if the user has a password hash |

## Sign-up flow

`POST /v1/auth/signup`

```json
{
  "email": "alice@example.com",
  "password": "Secure!Pass99",
  "username": "alice",
  "name": "Alice Liddell",
  "app_id": "myapp"
}
```

The engine performs these checks before creating the user:

1. Validates the password against the policy (length, complexity)
2. Checks HaveIBeenPwned if `CheckBreached: true`
3. Validates email domain against `AllowedDomains` (if configured)
4. Verifies email uniqueness within the app
5. Hashes the password with the configured algorithm
6. Calls `BeforeSignUp` plugin hooks
7. Creates the user record and session

## Sign-in flow

`POST /v1/auth/signin`

```json
{
  "email": "alice@example.com",
  "password": "Secure!Pass99",
  "app_id": "myapp"
}
```

The engine checks in order: account lockout status, user existence, ban status, password verification, password expiry, and transparent rehash if the algorithm has changed.

## Password hashing

### bcrypt (default)

```go
Password: authsome.PasswordConfig{
    Algorithm:  "bcrypt",
    BcryptCost: 12,
}
```

Each cost increment doubles computation time. A cost of 12 takes approximately 250ms on modern hardware.

### argon2id

```go
Password: authsome.PasswordConfig{
    Algorithm: "argon2id",
    Argon2: authsome.Argon2Config{
        Memory:      65536,
        Iterations:  3,
        Parallelism: 2,
        SaltLength:  16,
        KeyLength:   32,
    },
}
```

argon2id is memory-hard, making GPU brute-force attacks significantly more expensive.

### Transparent algorithm migration

When you change `Algorithm`, existing users are migrated automatically on their next successful sign-in. The engine detects the old hash format and re-hashes with the new algorithm -- no migration scripts needed.

## Password policies

```go
Password: authsome.PasswordConfig{
    MinLength:        12,
    RequireUppercase: true,
    RequireLowercase: true,
    RequireDigit:     true,
    RequireSpecial:   true,
    CheckBreached:    true,
    HistoryCount:     5,
    MaxAgeDays:       90,
}
```

| Option | Description |
|--------|-------------|
| `MinLength` | Minimum password length |
| `RequireUppercase` | Require at least one uppercase letter |
| `RequireLowercase` | Require at least one lowercase letter |
| `RequireDigit` | Require at least one digit |
| `RequireSpecial` | Require at least one special character |
| `CheckBreached` | Check against HaveIBeenPwned k-anonymity API |
| `HistoryCount` | Prevent reuse of last N passwords |
| `MaxAgeDays` | Force password change after N days |

## Password reset flow

**Request reset**: `POST /v1/auth/forgot-password`

```json
{"email": "alice@example.com", "app_id": "myapp"}
```

The engine creates a reset token with 1-hour expiry. The response is always `200 OK` regardless of whether the email exists, preventing email enumeration.

**Confirm reset**: `POST /v1/auth/reset-password`

```json
{
  "token": "the-reset-token",
  "new_password": "NewSecure!Pass99"
}
```

The engine validates the token, enforces the password policy, updates the hash, consumes the token, and revokes all existing sessions.

## Change password flow

`POST /v1/auth/change-password`

```json
{
  "current_password": "Secure!Pass99",
  "new_password": "EvenBetter!Pass123"
}
```

Requires a valid session token in the `Authorization: Bearer` header.

## API routes

| Method | Path | Description |
|--------|------|-------------|
| `POST` | `/signup` | Create user account and return session |
| `POST` | `/signin` | Authenticate with email/password |
| `POST` | `/signout` | Revoke current session |
| `POST` | `/refresh` | Exchange refresh token for new access token |
| `POST` | `/forgot-password` | Request password reset token |
| `POST` | `/reset-password` | Confirm reset with token and new password |
| `POST` | `/change-password` | Change password (requires current password) |
