Skip to contentSkip to navigation

Rate Limiting

Own Auth rate-limits sensitive authentication operations automatically. With the default Postgres setup, there is no middleware to enable and no separate rate-limit service to configure.

Overview

Own Auth rate-limits every sensitive operation automatically. You don't configure individual limits, enable middleware, or write rate-limiting code. It's built in.

Built-in limits

OperationIdentifierDefault limitWindow
Sign upNormalised email510 minutes
Password sign inNormalised email1010 minutes
Change passwordUser ID510 minutes
Magic-link requestNormalised email510 minutes
Email-verification requestNormalised email510 minutes
Password-reset requestNormalised email515 minutes
SMS-code requestNormalised phone number515 minutes
SMS-code verificationNormalised phone number1015 minutes
External-provider sign inProvider account ID2010 minutes
API-key creationUser or organisation owner201 hour
Organisation inviteOrganisation ID101 hour

These limits reduce password guessing, credential stuffing, repeated email and SMS sends, excessive invitations, and excessive key creation.

SMS codes also allow five wrong guesses by default. After that, the code cannot be verified and a new code must be requested. This per-code attempt limit is configured separately through sms.maxAttempts.

What happens when a limit is exceeded

Own Auth throws AuthError with:

  • code: rate_limited
  • statusCode: 429
  • safeMessage: Too many attempts. Try again later.
ts
import { AuthError } from "own-auth";

try {
  await auth.signInEmailPassword({ email, password });
} catch (error) {
  if (error instanceof AuthError && error.code === "rate_limited") {
    return res.status(error.statusCode).json({
      error: error.safeMessage,
    });
  }

  throw error;
}

AuthError does not currently include a retryAfterMs value. Do not read or document that field unless the public error contract adds it.

Default Postgres store

When DATABASE_URL is configured, Own Auth stores counters in own_auth_rate_limits in the same Postgres database as the auth data.

The counters survive server restarts and are shared by every application instance connected to that database. No additional rate-limit configuration is needed for the default setup.

ts
const auth = createOwnAuth({
  tokenPepper: process.env.OWN_AUTH_TOKEN_PEPPER,
});

Configuration

The built-in operation limits and windows are not configurable. This avoids accidentally weakening authentication protections in the current release.

Next step

Set up Audit logs to track auth events, or review the full Security model.