Skip to main content
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:
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.
Only fields with a single-column UNIQUE index are accepted as identity fields. This ensures that each identity is unique across your collection.
Example configuration:
collection.PasswordAuth.Enabled = true
collection.PasswordAuth.IdentityFields = []string{"email", "username"}
With multiple identity fields, users can authenticate using any of them:
# 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

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"
  }'
Request body:
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
}
Leave identityField empty for automatic detection, or specify a particular field from your identityFields configuration.
Successful response (200):
{
  "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:
// 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:
// Set a user-provided password
record.SetPassword("user_password")

// Set a random password (for OAuth2/OTP users)
randomPassword := record.SetRandomPassword()
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.

Password reset flow

PocketBase provides a secure two-step password reset process:
1

Request password reset

User submits their email to request a password reset.
curl -X POST http://localhost:8090/api/collections/users/request-password-reset \
  -H "Content-Type: application/json" \
  -d '{"email": "test@example.com"}'
The endpoint always returns 204 No Content to prevent email enumeration attacks.
2

Receive reset email

User receives an email with a password reset link containing a token.
3

Confirm password reset

User submits the token and new password to complete the reset.
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"
  }'

Rate limiting

Password reset requests are rate-limited to prevent abuse. Users can only request a password reset once every 2 minutes:
// 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:
curl -X POST http://localhost:8090/api/collections/users/request-verification \
  -H "Content-Type: application/json" \
  -d '{"email": "test@example.com"}'

Email change flow

Authenticated users can change their email address:
1

Request email change

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"}'
This endpoint requires authentication. Include the auth token in the Authorization header.
2

Confirm email change

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"
  }'

Implementation details

The password authentication implementation in PocketBase follows this logic:
// 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:
// 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

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

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:
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()
    },
})

OAuth2 authentication

Set up social login as an alternative

OTP authentication

Enable passwordless authentication

MFA

Add multi-factor authentication

API rules

Configure access control rules