13 KiB
title |
---|
Email Authentication |
Email Authentication
Overview
Wasp supports e-mail authentication out of the box, along with email verification and "forgot your password?" flows. It provides a set of routes and email templates that you can use to implement it in your app.
In this guide, we'll go through the easiest way to set up email authentication: using Wasp's Auth UI components. Check out the Auth UI guide for more details on how to customize the UI.
Outline of the guide
We'll need to take the following steps to set up email authentication:
- Set up email authentication in
main.wasp
- Add the user entity
- Add the routes and pages
- Set up the email sender in
main.wasp
and.env.server
- Use Auth UI components in our pages
Outline of the Wasp file we'll be working with:
// Configuring e-mail authentication
app myApp { ... }
// Defining User entity
entity User { ... }
// Defining routes and pages
route SignupRoute { ... }
page SignupPage { ... }
// ...
Email authentication in main.wasp
Let's first set up the email authentication by adding the following to our main.wasp
file:
app myApp {
wasp: {
version: "^0.10.0"
},
title: "My App",
auth: {
// 1. Specify the user entity
userEntity: User,
methods: {
// 2. Enable email authentication
email: {
// 3. Specify the email from field
fromField: {
name: "My App Postman",
email: "hello@itsme.com"
},
// 4. Specify the email verification and password reset options
emailVerification: {
clientRoute: EmailVerificationRoute,
getEmailContentFn: import { getVerificationEmailContent } from "@server/auth/email.js",
allowUnverifiedLogin: false,
},
passwordReset: {
clientRoute: PasswordResetRoute,
getEmailContentFn: import { getPasswordResetEmailContent } from "@server/auth/email.js",
},
},
},
onAuthFailedRedirectTo: "/login",
onAuthSucceededRedirectTo: "/profile"
},
}
User entity
Then we'll define the User
entity in our main.wasp
file:
// 5. Define the user entity
entity User {=psl
id Int @id @default(autoincrement())
email String? @unique
password String?
isEmailVerified Boolean @default(false)
emailVerificationSentAt DateTime?
passwordResetSentAt DateTime?
// Add your own fields below
// ...
psl=}
Routes and pages
Next, we need to define the routes and pages for the authentication pages. We'll show the React code later, but for now we'll just define the routes and pages.
We'll add the following to our main.wasp
file:
// 6. Define the routes
route SignupRoute { path: "/signup", to: SignupPage }
page SignupPage {
component: import { Signup } from "@client/pages/auth/Signup.tsx"
}
route LoginRoute { path: "/login", to: LoginPage }
page LoginPage {
component: import { Login } from "@client/pages/auth/Login.tsx"
}
route RequestPasswordResetRoute { path: "/request-password-reset", to: RequestPasswordResetPage }
page RequestPasswordResetPage {
component: import { RequestPasswordReset } from "@client/pages/auth/RequestPasswordReset.tsx",
}
route PasswordResetRoute { path: "/password-reset", to: PasswordResetPage }
page PasswordResetPage {
component: import { PasswordReset } from "@client/pages/auth/PasswordReset.tsx",
}
route EmailVerificationRoute { path: "/email-verification", to: EmailVerificationPage }
page EmailVerificationPage {
component: import { EmailVerification } from "@client/pages/auth/EmailVerification.tsx",
}
Email sender
We'll use SendGrid in this guide to send our e-mails. You can use any of the supported email providers. Read more about setting up the email sender in the email sender setup guide.
To set up SendGrid to send emails, we will add the following to our main.wasp
file:
app myApp {
...
emailSender: {
provider: SendGrid,
}
}
... and add the following to our .env.server
file:
SENDGRID_API_KEY=<your key>
Using Auth UI
:::info We are using Tailwind CSS to style the page. Read more about how to add it here. :::
Signup page
We are using the SignupForm
component from @wasp/auth/forms/Signup
to render the signup form.
import { Link } from 'react-router-dom'
import { SignupForm } from '@wasp/auth/forms/Signup'
export function Signup () {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div>
<SignupForm />
<br />
<span className="text-sm font-medium text-gray-900">
I already have an account (<Link to="/login">go to login</Link>).
</span>
<br />
</div>
</div>
</div>
</div>
)
}
Login page
We are using the LoginForm
component from @wasp/auth/forms/Login
to render the login form.
import { Link } from 'react-router-dom'
import { LoginForm } from '@wasp/auth/forms/Login'
export function Login() {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div>
<LoginForm />
<br />
<span className="text-sm font-medium text-gray-900">
Don't have an account yet? <Link to="/signup">go to signup</Link>.
</span>
<br />
<span className="text-sm font-medium text-gray-900">
Forgot your password?{' '}
<Link to="/request-password-reset">reset it</Link>.
</span>
</div>
</div>
</div>
</div>
)
}
Email verification setup
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:
emailVerification: {
clientRoute: EmailVerificationRoute,
getEmailContentFn: import { getVerificationEmailContent } from "@server/auth/email.js",
allowUnverifiedLogin: false,
}
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.
route EmailVerificationRoute { path: "/email-verification", to: EmailVerificationPage }
page EmailVerificationPage {
component: import { EmailVerification } from "@client/pages/auth/EmailVerification.tsx",
}
Email verification page
This route goes to the EmailVerification
page, which is defined in the EmailVerification.tsx
file:
import { Link } from 'react-router-dom'
import { VerifyEmailForm } from '@wasp/auth/forms/VerifyEmail'
export function EmailVerification() {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div>
<VerifyEmailForm />
<br />
<span className="text-sm font-medium text-gray-900">
If everything is okay, <Link to="/login">go to login</Link>
</span>
</div>
</div>
</div>
</div>
)
}
You'll notice we are using the VerifyEmailForm
component from the @wasp/auth/forms/VerifyEmail
module. This will give a nice-looking form for the user to verify their e-mail.
We will also override the default e-mail content. We are using the getVerificationEmailContent
function from the @server/auth/email.js
file to generate the email content.
import { GetVerificationEmailContentFn } from '@wasp/types'
export const getVerificationEmailContent: GetVerificationEmailContentFn = ({
verificationLink,
}) => ({
subject: 'Verify your email',
text: `Click the link below to verify your email: ${verificationLink}`,
html: `
<p>Click the link below to verify your email</p>
<a href="${verificationLink}">Verify email</a>
`,
})
Password reset setup
Users can request a password and then they'll receive an e-mail with a link to reset their password.
Our setup in main.wasp
looks like this:
passwordReset: {
clientRoute: PasswordResetRoute,
getEmailContentFn: import { getPasswordResetEmailContent } from "@server/auth/email.js",
}
Request password reset page
They request their password to be reset by going to the /request-password-reset
page. We'll add a link to this page in the login page.
That page will look like this:
import { ForgotPasswordForm } from '@wasp/auth/forms/ForgotPassword'
export function RequestPasswordReset() {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div>
<ForgotPasswordForm />
</div>
</div>
</div>
</div>
)
}
We will also override the default e-mail content that's sent. We are using the getVerificationEmailContent
function from the @server/auth/email.js
file to generate the email content.
import { GetPasswordResetEmailContentFn } from '@wasp/types'
export const getPasswordResetEmailContent: GetPasswordResetEmailContentFn = ({
passwordResetLink,
}) => ({
subject: 'Password reset',
text: `Click the link below to reset your password: ${passwordResetLink}`,
html: `
<p>Click the link below to reset your password</p>
<a href="${passwordResetLink}">Reset password</a>
`,
})
Password reset page
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.
route PasswordResetRoute { path: "/password-reset", to: PasswordResetPage }
page PasswordResetPage {
component: import { PasswordReset } from "@client/pages/auth/PasswordReset.tsx",
}
This route goes to the PasswordResetPage
page, which is defined in the PasswordReset.tsx
file. Users can enter their new password here:
import { Link } from 'react-router-dom'
import { ResetPasswordForm } from '@wasp/auth/forms/ResetPassword'
export function PasswordReset() {
return (
<div className="w-full h-full bg-white">
<div className="min-w-full min-h-[75vh] flex items-center justify-center">
<div className="w-full h-full max-w-sm p-5 bg-white">
<div>
<ResetPasswordForm />
<br />
<span className="text-sm font-medium text-gray-900">
If everything is okay, <Link to="/login">go to login</Link>
</span>
</div>
</div>
</div>
</div>
)
}
Logout action
To implement the logout action, you can use the logout
function from the @wasp/auth/logout
module. We can add it for example, to the Navbar
component:
import logout from '@wasp/auth/logout';
export function Navbar() {
return (
<div>
<button onClick={logout}>Logout</button>
</div>
)
}
Getting the user in our client & server code
You read about our useAuth
hook in this section of the docs.
In short, you can use the useAuth
hook in your client code to get the currently logged-in user. If there is no user logged in, it will return null
.
import useAuth from '@wasp/auth'
export function Profile() {
const { data: user } = useAuth()
if (!user) {
return <div>You are not logged in!</div>
}
return (
<div>
Hello, {user.email}!
</div>
)
}
Conclusion
And that's it! We now have a full authentication system in our app. We can register new users, login, logout, verify their e-mail, and reset their password.
We hope you enjoyed this guide and that you learned something new. If you have any questions, feel free to ask them on our Discord server.