# Configure email sign-in

https://docs.serverpod.dev/next/concepts/authentication/providers/email/configuration

This page covers configuration options for the email identity provider beyond the basic setup.

## Configuration options

Below is a non-exhaustive list of some of the most common configuration options. For more details on all options, check the `EmailIdpConfig` in-code documentation.

### Peppering

A pepper is a server-side secret mixed into a password before it is hashed and stored, so a database leak alone is not enough to crack the hashes. See [storing secrets](https://docs.serverpod.dev/next/concepts/authentication/setup.md#storing-secrets) for where to keep it.

The pepper is configured via the `secretHashPepper` property in `EmailIdpConfig`, or read from the `emailSecretHashPepper` password by `EmailIdpConfigFromPasswords`, as described in the [server-side configuration](https://docs.serverpod.dev/next/concepts/authentication/providers/email/setup.md#server-side-configuration) section. Its [recommended pepper length](https://www.ietf.org/archive/id/draft-ietf-kitten-password-storage-04.html#name-storage-2) is 32 bytes.

:::warning
If the pepper is changed, all passwords in the database will need to be re-hashed with the new pepper, or the old pepper needs to be added as a fallback pepper. Store the pepper securely and never expose it to the client.
:::

#### Rotating secret hash peppers

To keep old passwords valid after a pepper change, use the `fallbackSecretHashPeppers` configuration option to add the old pepper as a fallback pepper.

```dart
final emailIdpConfig = EmailIdpConfigFromPasswords(
  fallbackSecretHashPeppers: [
    pod.getPassword('oldEmailSecretHashPepper')!,
  ],
);
```

### Customizing password requirements

By default, the minimum password length is set to 8 characters. If you wish to modify this requirement, you can use the `passwordValidationFunction` configuration option.

```dart
final emailIdpConfig = EmailIdpConfigFromPasswords(
  passwordValidationFunction: (password) {
    // Require at least 12 characters, one uppercase, one lowercase, one number
    if (password.length < 12) return false;
    if (!password.contains(RegExp(r'[A-Z]'))) return false;
    if (!password.contains(RegExp(r'[a-z]'))) return false;
    if (!password.contains(RegExp(r'[0-9]'))) return false;
    return true;
  },
);
```

:::tip
This is useful to ensure password policies on the server-side. It is a best practice to pair it with a configuration on the client-side to provide a better UX when creating a new password. The `EmailSignInWidget` and `EmailAuthController` have a `passwordRequirements` parameter that can be used to configure the password requirements.
:::

### Custom verification code generation

You can customize how verification codes are generated:

```dart
final emailIdpConfig = EmailIdpConfigFromPasswords(
  registrationVerificationCodeGenerator: () {
    // Generate a 6-digit numeric code
    final random = Random.secure();
    return List.generate(6, (_) => random.nextInt(10)).join();
  },
);
```

:::warning
Remember to configure the `verificationCodeConfig` parameter on the `EmailSignInWidget` to match the length and allowed characters of the verification code you generate. Otherwise, users will never be able to enter the verification code correctly. See the [customizing the UI section](https://docs.serverpod.dev/next/concepts/authentication/providers/email/customizing-the-ui.md) for more details.
:::

#### Bypassing verification code in development

To simplify testing, it is possible to bypass the verification code by using a custom verification code generator that always returns the same code. This can be configured only for the development run mode as below:

```dart
pod.initializeAuthServices(
  tokenManagerBuilders: [
    JwtConfigFromPasswords(),
  ],
  identityProviderBuilders: [
    EmailIdpConfigFromPasswords(
      registrationVerificationCodeGenerator: pod.runMode == 'development'
          ? () => '11111111' // Digits only, and the same length as production.
          : defaultVerificationCodeGenerator,
    ),
  ],
);
```

### Reacting to events

Beyond the `sendRegistrationVerificationCode` and `sendPasswordResetVerificationCode` callbacks to send verification codes to the user, there are a few callbacks that can be used to react to events, such as `onAfterAccountCreated` and `onPasswordResetCompleted`. These can be useful for sending emails to the user to communicate the successful operation.

### Rate limiting

The email provider includes built-in rate limiting to prevent abuse, with some sensible defaults:

- **Failed login attempts**: Limited to 5 attempts per 5 minutes
- **Password reset attempts**: Limited to 3 attempts per hour
- **Verification code attempts**: Limited to 3 attempts per code lifetime of 15 minutes

You can customize all rate limits in the `EmailIdpConfig`:

```dart
final emailIdpConfig = EmailIdpConfigFromPasswords(
  registrationVerificationCodeLifetime: Duration(minutes: 15),
  registrationVerificationCodeAllowedAttempts: 3,
  passwordResetVerificationCodeLifetime: Duration(minutes: 15),
  passwordResetVerificationCodeAllowedAttempts: 3,
  failedLoginRateLimit: RateLimit(
    maxAttempts: 10,
    timeframe: Duration(minutes: 10),
  ),
  maxPasswordResetAttempts: RateLimit(
    maxAttempts: 5,
    timeframe: Duration(hours: 2),
  ),
);
```
