# Test auth flows

Test email and phone sign-in without hitting email and SMS limits

Test sign-up, sign-in, and password recovery as often as you need without waiting on email or SMS limits. Develop against local Supabase to capture every email in Mailpit, and use test phone numbers to sign in with a fixed code.

The local development steps need the Supabase CLI and a project initialized with `supabase init`. To set them up, see [Supabase CLI](https://supabase.com/docs/guides/local-development/cli/getting-started).

## Test email flows locally

The Supabase CLI sends every email from local Auth to Mailpit, an email testing server that runs with your local stack. No email leaves your machine, so you can send to any address. Local Auth has no hourly email limit. For more information about Mailpit, see the [Mailpit repository on GitHub](https://github.com/axllent/mailpit).

1. In your project directory, run `supabase start` to start the local stack.

2. Run `supabase status` to get your local stack's connection details. Copy the following values from the output:
   - **Project URL**: the local API URL, which is `http://127.0.0.1:54321` by default
   - **Publishable**: the local publishable key, which starts with `sb_publishable_`
   - **Mailpit**: the Mailpit URL, which is `http://127.0.0.1:54324` by default

3. Connect your app to the local stack. Your app's Supabase client reads a project URL and a publishable key from environment variables, which usually hold your hosted project's values. Replace them with the **Project URL** and **Publishable** key you copied from `supabase status`:

   ```bash .env.local
   NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
   NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=sb_publishable_...
   ```

   Variable names depend on your framework. To test against your hosted project again, restore its project URL and publishable key.

4. In your app, request a magic link or a password recovery email.

5. In your browser, go to the **Mailpit** URL and open the email to get the link or code.

Local Auth limits how often it sends to the same address. By default, it sends one email per address per second. To change this, set `max_frequency` under `[auth.email]` in `supabase/config.toml`.

Email confirmation is off by default in local development and on by default for hosted projects, so local sign-up doesn't send an email. To test the confirmation email locally, set `enable_confirmations = true` under `[auth.email]`, and then restart the local stack.

## Test email flows on a hosted project

Hosted projects send email through a built-in email service until you set up custom SMTP. The built-in service is for trying out Auth, and it has the following limits:

- It sends only to the email addresses of your organization's team members. Other addresses fail with the error code `email_address_not_authorized`.
- It sends 2 emails per hour across the whole project while **Confirm email** is on. When the project reaches the limit, Auth returns a `429` error with the code `over_email_send_rate_limit` and the message `email rate limit exceeded`.

To send to any address and raise the limit, set up [custom SMTP](https://supabase.com/docs/guides/auth/auth-smtp) or the [Send Email hook](https://supabase.com/docs/guides/auth/auth-hooks/send-email-hook). You can then change the email limit in [**Authentication** > **Rate Limits**](https://supabase.com/dashboard/project/_/auth/rate-limits). For more information about the limits, see [Rate limits](https://supabase.com/docs/guides/auth/rate-limits).

Caution: Turning off **Confirm email** lets anyone sign up with an email address they don't own, and Auth marks that address as confirmed. It also turns off the project's hourly email limit. If you turn it off while testing, turn it back on in [**Authentication** > **Sign In / Providers**](https://supabase.com/dashboard/project/_/auth/providers) before you launch.

## Test phone sign-in with test numbers

With a test phone number, you sign in with a fixed one-time password (OTP) that you choose. Auth doesn't send an SMS to a test number, and it accepts the fixed code as many times as you need.

Test numbers work for phone sign-up, sign-in, and phone number changes. They don't apply to [phone multi-factor authentication](https://supabase.com/docs/guides/auth/auth-mfa/phone), which always sends a real SMS.

Before you add test numbers, enable the phone provider and configure an SMS provider. Auth requires an SMS provider even though it doesn't send anything to test numbers.

Write each phone number in international format, with the country code and without the `+` prefix or spaces. For example, write `+1 415 555 2671` as `14155552671`. Auth compares the number your app sends against this value, so a number without its country code doesn't match.

**Dashboard**

1. Go to [**Authentication** > **Sign In / Providers**](https://supabase.com/dashboard/project/_/auth/providers), and open **Phone**.
2. In **Test Phone Numbers and OTPs**, enter a comma-separated list of `<phone number>=<OTP>` pairs, such as `14155552671=123456`.
3. In **Test OTPs Valid Until**, choose when the test numbers stop working. The Dashboard requires this date.
4. Click **Save**.

**Local development**

1. In `supabase/config.toml`, turn on phone sign-up, enable an SMS provider, and add each test number and its code. These sections exist in the default file, so edit them in place:

   ```toml supabase/config.toml
   [auth.sms]
   enable_signup = true

   [auth.sms.twilio]
   enabled = true
   account_sid = "placeholder"
   message_service_sid = "placeholder"
   auth_token = "placeholder"

   [auth.sms.test_otp]
   14155552671 = "123456"
   ```

   Auth doesn't use the provider credentials for test numbers, so placeholder values work.

2. Restart the local stack:

   ```bash
   supabase stop && supabase start
   ```

Local test numbers don't expire.

**Management API**

Update the project's Auth configuration with the test numbers and the date they stop working. Replace `YYYY-MM-DDTHH:MM:SSZ` with that date in UTC:

```bash
# Get your access token from https://supabase.com/dashboard/account/tokens
export SUPABASE_ACCESS_TOKEN="your-access-token"
export PROJECT_REF="your-project-ref"

curl -X PATCH "https://api.supabase.com/v1/projects/$PROJECT_REF/config/auth" \
  -H "Authorization: Bearer $SUPABASE_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sms_test_otp": "14155552671=123456",
    "sms_test_otp_valid_until": "YYYY-MM-DDTHH:MM:SSZ"
  }'
```

Test numbers don't count toward the project's hourly SMS limit. Auth makes you wait between codes for the same number. The wait defaults to 5 seconds. Locally, change it with `max_frequency` under `[auth.sms]`.

Danger: Anyone who knows a test number and its code can sign in as the user with that number. Remove test numbers before you launch, or set **Test OTPs Valid Until** to a date before launch.

`supabase config push` copies `[auth.sms.test_otp]` from `config.toml` to your hosted project and sets the test numbers to expire after 10 years. Remove the section before you push to a production project.

For a full list of settings to check before launch, see the [Production checklist](https://supabase.com/docs/guides/deployment/going-into-prod).
