Wasp supports e-mail authentication out of the box, along with email verification and "forgot your password?" flows. It provides you with the server-side implementation and email templates for all of these flows.
We'll use the `Dummy` provider to speed up the setup. It just logs the emails to the console instead of sending them. You can use any of the [supported email providers](../advanced/email#providers).
Running `wasp db migrate-dev` and then `wasp start` should give you a working app with email authentication. If you want to put some of the pages behind authentication, read the [auth overview](../auth/overview).
If logging in with an unverified email is _allowed_, the user will be able to login with an unverified email address. If logging in with an unverified email is _not allowed_, the user will be shown an error message.
Read more about the `allowUnverifiedLogin` option [here](#allowunverifiedlogin-bool-specifies-whether-the-user-can-login-without-verifying-their-e-mail-address).
### Signup
![Auth UI](/img/authui/signup.png)
Some of the behavior you get out of the box:
1. Rate limiting
We are limiting the rate of sign-up requests to **1 request per minute** per email address. This is done to prevent spamming.
2. Preventing user email leaks
If somebody tries to signup with an email that already exists and it's verified, we _pretend_ that the account was created instead of saying it's an existing account. This is done to prevent leaking the user's email address.
3. Allowing registration for unverified emails
If a user tries to register with an existing but **unverified** email, we'll allow them to do that. This is done to prevent bad actors from locking out other users from registering with their email address.
By default, Wasp requires the e-mail to be verified before allowing the user to log in. This is done by sending a verification email to the user's email address and requiring the user to click on a link in the email to verify their email address.
Our setup looks like this:
<TabsgroupId="js-ts">
<TabItemvalue="js"label="JavaScript">
```wasp title="main.wasp"
// ...
emailVerification: {
clientRoute: EmailVerificationRoute,
}
```
</TabItem>
<TabItemvalue="ts"label="TypeScript">
```wasp title="main.wasp"
// ...
emailVerification: {
clientRoute: EmailVerificationRoute,
}
```
</TabItem>
</Tabs>
When the user receives an e-mail, they receive a link that goes to the client route specified in the `clientRoute` field. In our case, this is the `EmailVerificationRoute` route we defined in the `main.wasp` file.
The content of the e-mail can be customized, read more about it [here](#emailverification-emailverificationconfig-).
### Email Verification Page
We defined our email verification page in the `auth.{jsx,tsx}` file.
![Auth UI](/img/authui/email_verification.png)
## Password Reset Flow
Users can request a password and then they'll receive an e-mail with a link to reset their password.
Some of the behavior you get out of the box:
1. Rate limiting
We are limiting the rate of sign-up requests to **1 request per minute** per email address. This is done to prevent spamming.
2. Preventing user email leaks
If somebody requests a password reset with an unknown email address, we'll give back the same response as if the user requested a password reset successfully. This is done to prevent leaking information.
Our setup in `main.wasp` looks like this:
<TabsgroupId="js-ts">
<TabItemvalue="js"label="JavaScript">
```wasp title="main.wasp"
// ...
passwordReset: {
clientRoute: PasswordResetRoute,
}
```
</TabItem>
<TabItemvalue="ts"label="TypeScript">
```wasp title="main.wasp"
// ...
passwordReset: {
clientRoute: PasswordResetRoute,
}
```
</TabItem>
</Tabs>
### Request Password Reset Page
Users request their password to be reset by going to the `/request-password-reset` route. We defined our request password reset page in the `auth.{jsx,tsx}` file.
When the user receives an e-mail, they receive a link that goes to the client route specified in the `clientRoute` field. In our case, this is the `PasswordResetRoute` route we defined in the `main.wasp` file.
We don't recommend creating a custom sign-up action unless you have a good reason to do so. It is a complex process and you can easily make a mistake that will compromise the security of your app.
:::
The code of your custom sign-up action can look like this:
text: `Click the link below to verify your email: ${verificationLink}`,
html: `
<p>Click the link below to verify your email</p>
<ahref="${verificationLink}">Verify email</a>
`,
}
);
} catch (e: unknown) {
console.error("Failed to send email verification email:", e);
throw new HttpError(500, "Failed to send email verification email.");
}
}
} catch (e) {
return {
success: false,
message: e.message,
}
}
// Your custom code after sign-up.
// ...
return {
success: true,
message: 'User created successfully',
}
}
```
</TabItem>
</Tabs>
We suggest using the built-in field validators for your authentication flow. You can import them from `@wasp/auth/validation.js`. These are the same validators that Wasp uses internally for the default authentication flow.
#### Email
-`ensureValidEmail(args)`
Checks if the email is valid and throws an error if it's not. Read more about the validation rules [here](../auth/overview#default-validations).
#### Password
-`ensurePasswordIsPresent(args)`
Checks if the password is present and throws an error if it's not.
-`ensureValidPassword(args)`
Checks if the password is valid and throws an error if it's not. Read more about the validation rules [here](../auth/overview#default-validations).
## Using Auth
To read more about how to set up the logout button and how to get access to the logged-in user in our client and server code, read the [auth overview docs](../auth/overview).
### `getEmail`
If you are looking to access the user's email in your code, you can do that by accessing the info about the user that is stored in the `user.auth.identities` array.
To make things a bit easier for you, Wasp offers the `getEmail` helper.
`emailVerification` is a dict that specifies the details of the e-mail verification process.
It has the following fields:
-`clientRoute: Route`: a route that is used for the user to verify their e-mail address. <Required/>
Client route should handle the process of taking a token from the URL and sending it to the server to verify the e-mail address. You can use our `verifyEmail` action for that.
<TabsgroupId="js-ts">
<TabItemvalue="js"label="JavaScript">
```js title="src/pages/EmailVerificationPage.jsx"
import { verifyEmail } from '@wasp/auth/email/actions';
...
await verifyEmail({ token });
```
</TabItem>
<TabItemvalue="ts"label="TypeScript">
```ts title="src/pages/EmailVerificationPage.tsx"
import { verifyEmail } from '@wasp/auth/email/actions';
...
await verifyEmail({ token });
```
</TabItem>
</Tabs>
:::note
We used Auth UI above to avoid doing this work of sending the token to the server manually.
:::
-`getEmailContentFn: ServerImport`: a function that returns the content of the e-mail that is sent to the user.
Defining `getEmailContentFn` can be done by defining a file in the `server` directory.
`passwordReset` is a dict that specifies the password reset process.
It has the following fields:
-`clientRoute: Route`: a route that is used for the user to reset their password. <Required/>
Client route should handle the process of taking a token from the URL and a new password from the user and sending it to the server. You can use our `requestPasswordReset` and `resetPassword` actions to do that.
<TabsgroupId="js-ts">
<TabItemvalue="js"label="JavaScript">
```js title="src/pages/ForgotPasswordPage.jsx"
import { requestPasswordReset } from '@wasp/auth/email/actions';
...
await requestPasswordReset({ email });
```
```js title="src/pages/PasswordResetPage.jsx"
import { resetPassword } from '@wasp/auth/email/actions';
...
await resetPassword({ password, token })
```
</TabItem>
<TabItemvalue="ts"label="TypeScript">
```ts title="src/pages/ForgotPasswordPage.tsx"
import { requestPasswordReset } from '@wasp/auth/email/actions';
...
await requestPasswordReset({ email });
```
```ts title="src/pages/PasswordResetPage.tsx"
import { resetPassword } from '@wasp/auth/email/actions';
...
await resetPassword({ password, token })
```
</TabItem>
</Tabs>
:::note
We used Auth UI above to avoid doing this work of sending the password request and the new password to the server manually.
:::
-`getEmailContentFn: ServerImport`: a function that returns the content of the e-mail that is sent to the user.
Defining `getEmailContentFn` is done by defining a function that looks like this:
text: `Click the link below to reset your password: ${passwordResetLink}`,
html: `
<p>Click the link below to reset your password</p>
<ahref="${passwordResetLink}">Reset password</a>
`,
})
```
</TabItem>
</Tabs>
<small>This is the default content of the e-mail, you can customize it to your liking.</small>
#### `allowUnverifiedLogin: bool`: specifies whether the user can login without verifying their e-mail address
It defaults to `false`. If `allowUnverifiedLogin` is set to `true`, the user can login without verifying their e-mail address, otherwise users will receive a `401` error when trying to login without verifying their e-mail address.
Sometimes you want to allow unverified users to login to provide them a different onboarding experience. Some of the pages can be viewed without verifying the e-mail address, but some of them can't. You can use the `isEmailVerified` field on the user entity to check if the user has verified their e-mail address.
If you have any questions, feel free to ask them on [our Discord server](https://discord.gg/rzdnErX).