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

# Email and password authentication

> Configure and use password-based authentication in PocketBase

Password authentication is the traditional method where users authenticate with an identity field (typically email or username) and a password.

## Configuration

You can configure password authentication in your auth collection settings:

```go theme={null}
type PasswordAuthConfig struct {
    Enabled        bool     `json:"enabled"`
    IdentityFields []string `json:"identityFields"`
}
```

### Identity fields

Identity fields are the field names that can be used to identify a user during authentication. By default, the `email` field is used, but you can configure any field with a unique index.

<Note>
  Only fields with a single-column UNIQUE index are accepted as identity fields. This ensures that each identity is unique across your collection.
</Note>

**Example configuration:**

```go theme={null}
collection.PasswordAuth.Enabled = true
collection.PasswordAuth.IdentityFields = []string{"email", "username"}
```

With multiple identity fields, users can authenticate using any of them:

```bash theme={null}
# Authenticate with email
curl -X POST http://localhost:8090/api/collections/users/auth-with-password \
  -H "Content-Type: application/json" \
  -d '{
    "identity": "user@example.com",
    "password": "your_password"
  }'

# Authenticate with username
curl -X POST http://localhost:8090/api/collections/users/auth-with-password \
  -H "Content-Type: application/json" \
  -d '{
    "identity": "john_doe",
    "password": "your_password"
  }'
```

## Authentication endpoint

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST http://localhost:8090/api/collections/users/auth-with-password \
    -H "Content-Type: application/json" \
    -d '{
      "identity": "test@example.com",
      "password": "secure_password_123"
    }'
  ```

  ```javascript JavaScript theme={null}
  const pb = new PocketBase('http://localhost:8090');

  const authData = await pb.collection('users').authWithPassword(
    'test@example.com',
    'secure_password_123'
  );

  console.log(authData.token);
  console.log(authData.record);
  ```

  ```go Go theme={null}
  record, err := app.FindAuthRecordByEmail("users", "test@example.com")
  if err != nil {
      return err
  }

  if !record.ValidatePassword("secure_password_123") {
      return errors.New("invalid credentials")
  }

  token, err := record.NewAuthToken()
  ```
</CodeGroup>

**Request body:**

```go theme={null}
type authWithPasswordForm struct {
    Identity      string `json:"identity"`      // Required: 1-255 characters
    Password      string `json:"password"`      // Required: 1-255 characters
    IdentityField string `json:"identityField"` // Optional: specific field to search
}
```

<Info>
  Leave `identityField` empty for automatic detection, or specify a particular field from your `identityFields` configuration.
</Info>

**Successful response (200):**

```json theme={null}
{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "record": {
    "id": "RECORD_ID",
    "collectionId": "_pb_users_auth_",
    "collectionName": "users",
    "email": "test@example.com",
    "emailVisibility": false,
    "verified": true,
    "created": "2024-01-01 00:00:00.000Z",
    "updated": "2024-01-01 00:00:00.000Z"
  }
}
```

## Password validation

PocketBase provides built-in password validation through the Record model:

```go theme={null}
// From core/record_model_auth.go
func (m *Record) ValidatePassword(password string) bool {
    pv, ok := m.GetRaw(FieldNamePassword).(*PasswordFieldValue)
    if !ok {
        return false
    }
    return pv.Validate(password)
}
```

## Setting passwords

You can set passwords programmatically using the Record methods:

```go theme={null}
// Set a user-provided password
record.SetPassword("user_password")

// Set a random password (for OAuth2/OTP users)
randomPassword := record.SetRandomPassword()
```

<Note>
  `SetRandomPassword()` generates a \~30 character password and sets it directly as a hash, bypassing field validators. This is useful for OAuth2 or OTP user flows where a password is needed but won't be used for authentication.
</Note>

## Password reset flow

PocketBase provides a secure two-step password reset process:

<Steps>
  <Step title="Request password reset">
    User submits their email to request a password reset.

    ```bash theme={null}
    curl -X POST http://localhost:8090/api/collections/users/request-password-reset \
      -H "Content-Type: application/json" \
      -d '{"email": "test@example.com"}'
    ```

    <Info>
      The endpoint always returns 204 No Content to prevent email enumeration attacks.
    </Info>
  </Step>

  <Step title="Receive reset email">
    User receives an email with a password reset link containing a token.
  </Step>

  <Step title="Confirm password reset">
    User submits the token and new password to complete the reset.

    ```bash theme={null}
    curl -X POST http://localhost:8090/api/collections/users/confirm-password-reset \
      -H "Content-Type: application/json" \
      -d '{
        "token": "RESET_TOKEN_FROM_EMAIL",
        "password": "new_secure_password",
        "passwordConfirm": "new_secure_password"
      }'
    ```
  </Step>
</Steps>

### Rate limiting

Password reset requests are rate-limited to prevent abuse. Users can only request a password reset once every 2 minutes:

```go theme={null}
// From record_auth_password_reset_request.go:63
time.AfterFunc(2*time.Minute, func() {
    app.Store().Remove(resendKey)
})
```

## Email verification

Users can verify their email addresses through a similar two-step process:

<Tabs>
  <Tab title="Request verification">
    ```bash theme={null}
    curl -X POST http://localhost:8090/api/collections/users/request-verification \
      -H "Content-Type: application/json" \
      -d '{"email": "test@example.com"}'
    ```
  </Tab>

  <Tab title="Confirm verification">
    ```bash theme={null}
    curl -X POST http://localhost:8090/api/collections/users/confirm-verification \
      -H "Content-Type: application/json" \
      -d '{"token": "VERIFICATION_TOKEN_FROM_EMAIL"}'
    ```
  </Tab>
</Tabs>

## Email change flow

Authenticated users can change their email address:

<Steps>
  <Step title="Request email change">
    ```bash theme={null}
    curl -X POST http://localhost:8090/api/collections/users/request-email-change \
      -H "Authorization: Bearer YOUR_AUTH_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"newEmail": "newemail@example.com"}'
    ```

    <Warning>
      This endpoint requires authentication. Include the auth token in the Authorization header.
    </Warning>
  </Step>

  <Step title="Confirm email change">
    ```bash theme={null}
    curl -X POST http://localhost:8090/api/collections/users/confirm-email-change \
      -H "Content-Type: application/json" \
      -d '{
        "token": "EMAIL_CHANGE_TOKEN",
        "password": "current_password"
      }'
    ```
  </Step>
</Steps>

## Implementation details

The password authentication implementation in PocketBase follows this logic:

```go theme={null}
// From apis/record_auth_with_password.go
func recordAuthWithPassword(e *core.RequestEvent) error {
    collection, err := findAuthCollection(e)
    if err != nil {
        return err
    }

    if !collection.PasswordAuth.Enabled {
        return e.ForbiddenError(
            "The collection is not configured to allow password authentication.", nil)
    }

    // Bind and validate form data
    form := &authWithPasswordForm{}
    if err = e.BindBody(form); err != nil {
        return e.BadRequestError(
            "An error occurred while loading the submitted data.", err)
    }

    // Find record by identity field
    var foundRecord *core.Record
    for _, name := range collection.PasswordAuth.IdentityFields {
        foundRecord, err = findRecordByIdentityField(
            e.App, collection, name, form.Identity)
        if err == nil {
            break
        }
    }

    // Validate password
    if foundRecord == nil || !foundRecord.ValidatePassword(form.Password) {
        return e.BadRequestError(
            "Failed to authenticate.", errors.New("invalid login credentials"))
    }

    return RecordAuthResponse(e, foundRecord, core.MFAMethodPassword, nil)
}
```

### Case-insensitive identity lookup

PocketBase supports case-insensitive identity field lookup based on the index collation:

```go theme={null}
// From apis/record_auth_with_password.go:129
if strings.EqualFold(index.Columns[0].Collate, "nocase") {
    // Case-insensitive search
    expr = dbx.NewExp("[[" + field + "]] = {:identity} COLLATE NOCASE",
        dbx.Params{"identity": value})
} else {
    expr = dbx.HashExp{field: value}
}
```

## Security considerations

<Warning>
  Password authentication endpoints are rate-limited to prevent brute force attacks. Implement additional security measures like account lockouts for production applications.
</Warning>

### Best practices

1. **Require email verification**: Set your collection's `authRule` to `"verified = true"` to allow only verified users to authenticate.

2. **Use strong password requirements**: Configure password field validators with minimum length and complexity requirements.

3. **Enable MFA**: For sensitive applications, enable multi-factor authentication to add an extra layer of security.

4. **Monitor failed login attempts**: Use the activity log to track failed authentication attempts.

5. **Rotate token secrets**: Periodically update your token configuration secrets for enhanced security.

## Custom authentication hooks

You can customize the authentication behavior using event hooks:

```go theme={null}
app.OnRecordAuthWithPasswordRequest().Bind(&hook.Handler[*core.RecordAuthWithPasswordRequestEvent]{
    Func: func(e *core.RecordAuthWithPasswordRequestEvent) error {
        // Custom validation logic
        if e.Record.GetBool("banned") {
            return e.ForbiddenError("Account is banned", nil)
        }
        
        // Track login attempts
        log.Printf("User %s attempting login", e.Identity)
        
        return e.Next()
    },
})
```

## Related topics

<CardGroup cols={2}>
  <Card title="OAuth2 authentication" icon="globe" href="/auth/oauth2">
    Set up social login as an alternative
  </Card>

  <Card title="OTP authentication" icon="key" href="/auth/otp">
    Enable passwordless authentication
  </Card>

  <Card title="MFA" icon="shield-halved" href="/auth/mfa">
    Add multi-factor authentication
  </Card>

  <Card title="API rules" icon="shield" href="/auth/api-rules">
    Configure access control rules
  </Card>
</CardGroup>
