Add e-mail auth docs (#1113)

This commit is contained in:
Mihovil Ilakovac 2023-04-07 16:21:06 +02:00 committed by GitHub
parent 67cffa3d14
commit a2936b78a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 759 additions and 89 deletions

View File

@ -2,6 +2,57 @@
## v0.10.0
### Breaking changes
- we changed `LoginForm` and `SignupForm` to use a named export instead of a default export, this means you must use them like this:
- `import { LoginForm } from '@wasp/auth/forms/Login'`
- `import { SignupForm } from '@wasp/auth/Signup'`
- we renamed `useAuth.js` to `useAuth.ts` and you should import it like this: `import useAuth from '@wasp/auth/useAuth'` (without the `.js` extension)
### Adds support for e-mail authentication
You can now use e-mail authentication in your Wasp app! This means that users can sign up and log in using their e-mail address. You get e-mail verification and password reset out of the box.
```c
app MyApp {
// ...
auth: {
// ...
email: {
fromField: {
name: "ToDO App",
email: "mihovil@ilakovac.com"
},
emailVerification: {
allowUnverifiedLogin: false,
getEmailContentFn: import { getVerificationEmailContent } from "@server/auth/email.js",
clientRoute: EmailVerificationRoute,
},
passwordReset: {
getEmailContentFn: import { getPasswordResetEmailContent } from "@server/auth/email.js",
clientRoute: PasswordResetRoute
},
},
}
}
```
You can only use one of e-mail or username & password authentication in your app. You can't use both at the same time.
### Adds Auth UI components
Wasp now provides a set of UI components for authentication. You can use them to quickly build a login and signup page for your app. The UI changes dynamically based on your Wasp config.
We provide `LoginForm`, `SignupForm`, `ForgotPassworForm`, `ResetPasswordForm` and`VerifyEmailForm` components. You can import them from `@wasp/auth/forms` like:
```js
import { LoginForm } from '@wasp/auth/forms/Login'
import { SignupForm } from '@wasp/auth/forms/Signup'
import { ForgotPasswordForm } from '@wasp/auth/forms/ForgotPassword'
import { ResetPasswordForm } from '@wasp/auth/forms/ResetPassword'
import { VerifyEmailForm } from '@wasp/auth/forms/VerifyEmail'
```
### Adds support for database seeding
You can now define JS/TS functions for seeding the database!

View File

@ -0,0 +1,430 @@
---
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.
![Auth UI](/img/authui/all_screens.gif)
In this guide, we'll go through the easiest way to set up email authentication: using Wasp's Auth UI components.
## 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:
```c title="main.wasp"
// 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:
```c title="main.wasp"
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:
```c title="main.wasp" {4-8}
// 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:
```c title="main.wasp"
// 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](/docs/guides/sending-emails).
To set up SendGrid to send emails, we will add the following to our `main.wasp` file:
```c title="main.wasp"
app myApp {
...
emailSender: {
provider: SendGrid,
}
}
```
... and add the following to our `.env.server` file:
```c title=".env.server"
SENDGRID_API_KEY=<your key>
```
## Using Auth UI
:::info
We are using [Tailwind CSS](https://tailwindcss.com/) to style the page. Read more about how to add it [here](/docs/integrations/css-frameworks#tailwind).
:::
### Signup page
![Auth UI](/img/authui/signup.png)
We are using the `SignupForm` component from `@wasp/auth/forms/Signup` to render the signup form.
```tsx title="client/pages/auth/Signup.tsx"
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
![Auth UI](/img/authui/login.png)
We are using the `LoginForm` component from `@wasp/auth/forms/Login` to render the login form.
```tsx title="client/pages/auth/Login.tsx"
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:
```c title="main.wasp"
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.
```c title="main.wasp"
route EmailVerificationRoute { path: "/email-verification", to: EmailVerificationPage }
page EmailVerificationPage {
component: import { EmailVerification } from "@client/pages/auth/EmailVerification.tsx",
}
```
### Email verification page
![Auth UI](/img/authui/email_verification.png)
This route goes to the `EmailVerification` page, which is defined in the `EmailVerification.tsx` file:
```jsx title="client/pages/auth/EmailVerification.tsx"
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.
```ts title="server/auth/email.ts"
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:
```c title="main.wasp"
passwordReset: {
clientRoute: PasswordResetRoute,
getEmailContentFn: import { getPasswordResetEmailContent } from "@server/auth/email.js",
}
```
### Request password reset page
![Request password reset page](/img/authui/forgot_password_after.png)
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:
```jsx title="client/pages/auth/RequestPasswordReset.tsx"
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.
```ts title="server/auth/email.ts"
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
![Request password reset page](/img/authui/reset_password_after.png)
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.
```c title="main.wasp"
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:
```tsx title="client/pages/auth/PasswordReset.tsx"
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:
```jsx title="client/components/Navbar.tsx"
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](/docs/language/features#accessing-the-currently-logged-in-user) 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`.
```jsx title="client/pages/Profile.tsx"
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](https://discord.gg/rzdnErX).

View File

@ -940,7 +940,8 @@ app MyApp {
userEntity: User,
externalAuthEntity: SocialLogin,
methods: {
usernameAndPassword: {},
usernameAndPassword: {}, // use this or email, not both
email: {}, // use this or usernameAndPassword, not both
google: {},
gitHub: {},
},
@ -954,16 +955,17 @@ app MyApp {
`app.auth` is a dictionary with following fields:
#### `userEntity: entity` (required)
Entity which represents the user (sometimes also referred to as *Principal*).
Entity which represents the user.
#### `externalAuthEntity: entity` (optional)
Entity which associates a user with some external authentication provider. We currently offer support for Google and GitHub. See the sections on [Social Login Providers](#social-login-providers-oauth-20) for more info.
#### `methods: dict` (required)
List of authentication methods that Wasp app supports. Currently supported methods are:
* `usernameAndPassword`: Provides support for authentication with a username and password. See [here](#username-and-password) for more.
* `google`: Provides support for login via Google accounts. See [here](#social-login-providers-oauth-20) for more.
* `gitHub`: Provides support for login via GitHub accounts. See [here](#social-login-providers-oauth-20) for more.
* `usernameAndPassword`: authentication with a username and password. See [here](#username-and-password) for more.
* `email`: authentication with a email and password. See [here](#email-authentication) for more.
* `google`: authentication via Google accounts. See [here](#social-login-providers-oauth-20) for more.
* `gitHub`: authentication via GitHub accounts. See [here](#social-login-providers-oauth-20) for more.
#### `onAuthFailedRedirectTo: String` (required)
Path where an unauthenticated user will be redirected to if they try to access a private page (which is declared by setting `authRequired: true` for a specific page).
@ -1076,7 +1078,7 @@ Validations always run on `create()`, but only when the field mentioned in `vali
#### Specification
### `login()`
#### `login()`
An action for logging in the user.
```js
login(username, password)
@ -1098,7 +1100,7 @@ import login from '@wasp/auth/login.js'
Login is a regular action and can be used directly from the frontend.
### `signup()`
#### `signup()`
An action for signing up the user. This action does not log in the user, you still need to call `login()`.
```js
signup(userFields)
@ -1113,7 +1115,7 @@ import signup from '@wasp/auth/signup.js'
Signup is a regular action and can be used directly from the frontend.
### `logout()`
#### `logout()`
An action for logging out the user.
```js
logout()
@ -1135,9 +1137,6 @@ const SignOut = () => {
}
```
#### Reset password
Coming soon.
#### Updating a user's password
If you need to update user's password, you can do it safely via Prisma client, e.g. within an action:
```js
@ -1154,103 +1153,195 @@ You don't need to worry about hashing the password yourself - if you have an `au
in your `.wasp` file, Wasp already set a middleware on Prisma that makes sure whenever password
is created or updated on the user entity, it is also hashed before it is stored to the database.
### Email authentication
### Accessing the currently logged in user
When authentication is enabled in a Wasp app, we need a way to tell whether a user is logged in and access its data.
With that, we can further implement access control and decide which content is private and which public.
:::info
We have written a step-by-step guide on how to set up the e-mail authentication with Wasp's included Auth UI.
#### On the client
On the client, Wasp provides a React hook you can use in functional components - `useAuth`.
This hook is actually a thin wrapper over Wasp's [`useQuery` hook](http://localhost:3002/docs/language/features#the-usequery-hook) and returns data in the same format.
Read more in the [email authentication guide](/docs/guides/email-auth).
:::
### `useAuth()`
#### `import statement`:
```js
import useAuth from '@wasp/auth/useAuth.js'
```
:::warning
If a user signs up with Google or Github (and you set it up to save their social provider e-mail info on the `User` entity), they'll be able to reset their password and login with e-mail and password.
##### Example of usage:
```js title="src/client/pages/MainPage.js"
import React from 'react'
If a user signs up with the e-mail and password and then tries to login with a social provider (Google or Github), they won't be able to do that.
import { Link } from 'react-router-dom'
import useAuth from '@wasp/auth/useAuth.js'
import logout from '@wasp/auth/logout.js'
import Todo from '../Todo.js'
import '../Main.css'
In the future, we will lift this limitation and enable smarter merging of accounts.
:::
const Main = () => {
const { data: user } = useAuth()
`email` authentication method makes it possible to signup/login into the app by using an e-mail and a password.
if (!user) {
return (
<span>
Please <Link to='/login'>login</Link> or <Link to='/signup'>sign up</Link>.
</span>
)
} else {
return (
<>
<button onClick={logout}>Logout</button>
<Todo />
< />
)
}
```c title="main.wasp"
app MyApp {
title: "My app",
// ...
auth: {
userEntity: User,
methods: {
email: {
// we'll deal with `email` below
},
},
onAuthFailedRedirectTo: "/someRoute"
},
// ...
}
export default Main
// Wasp requires the userEntity to have at least the following fields
entity User {=psl
id Int @id @default(autoincrement())
email String? @unique
password String?
isEmailVerified Boolean @default(false)
emailVerificationSentAt DateTime?
passwordResetSentAt DateTime?
psl=}
```
#### On the server
This method requires that `userEntity` specified in `auth` contains:
When authentication is enabled, all operations (actions and queries) will have access to the `user` through the `context` argument. `context.user` will contain all the fields from the user entity except for the password.
- optional `email` field of type `String`
- optional `password` field of type `String`
- `isEmailVerified` field of type `Boolean` with a default value of `false`
- optional `emailVerificationSentAt` field of type `DateTime`
- optional `passwordResetSentAt` field of type `DateTime`
##### Example of usage:
```js title="src/server/actions.js"
import HttpError from '@wasp/core/HttpError.js'
#### Fields in the `email` dict
export const createTask = async (task, context) => {
if (!context.user) {
throw new HttpError(403)
}
```c title="main.wasp"
app MyApp {
title: "My app",
// ...
const Task = context.entities.Task
return Task.create({
data: {
description: task.description,
user: {
connect: { id: context.user.id }
}
}
})
auth: {
userEntity: User,
methods: {
email: {
fromField: {
name: "My App",
email: "hello@itsme.com"
},
emailVerification: {
allowUnverifiedLogin: false,
clientRoute: EmailVerificationRoute,
getEmailContentFn: import { getVerificationEmailContent } from "@server/auth/email.js",
},
passwordReset: {
clientRoute: PasswordResetRoute
getEmailContentFn: import { getPasswordResetEmailContent } from "@server/auth/email.js",
},
},
},
onAuthFailedRedirectTo: "/someRoute"
},
// ...
}
```
In order to implement access control, each operation is responsible for checking `context.user` and
acting accordingly - e.g. if `context.user` is `undefined` and the operation is private then user
should be denied access to it.
### Validation Error Handling
When creating, updating, or deleting entities, you may wish to handle validation errors. We have exposed a class called `AuthError` for this purpose. This could also be combined with [Prisma Error Helpers](/docs/language/features#prisma-error-helpers).
##### `fromField: EmailFromField` (required)
`fromField` is a dict that specifies the name and e-mail address of the sender of the e-mails sent by Wasp. It is required to be defined. The object has the following fields:
- `name`: name of the sender (optional)
- `email`: e-mail address of the sender
#### `import statement`:
```js
import AuthError from '@wasp/core/AuthError.js'
##### `emailVerification: EmailVerificationConfig` (required)
`emailVerification` is a dict that specifies the e-mail verification process. It is required to be defined.
The object 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.
```js title="src/pages/EmailVerificationPage.jsx"
import { verifyEmail } from '@wasp/auth/email/actions';
...
await verifyEmail({ token });
```
##### Example of usage:
```js
try {
await context.entities.User.update(...)
} catch (e) {
if (e instanceof AuthError) {
throw new HttpError(422, 'Validation failed', { message: e.message })
} else {
throw e
}
}
Read on how to do it the easiest way with Auth UI in the [email authentication guide](/docs/guides/email-auth).
- `getEmailContentFn: ServerImport`: a function that returns the content of the e-mail that is sent to the user. (optional)
Defining `getEmailContentFn` can be done by defining a Javscript or Typescript file in the `server` directory.
```ts title="server/email.ts"
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>
`,
})
```
## Social Login Providers (OAuth 2.0)
- `allowUnverifiedLogin: Boolean`: a boolean that specifies whether the user can login without verifying their e-mail address. (optional)
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.
##### `passwordReset: PasswordResetConfig` (required)
`passwordReset` is a dict that specifies the password reset process. It is required to be defined. The object 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.
```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 })
```
Read on how to do it the easiest way with Auth UI in the [email authentication guide](/docs/guides/email-auth).
- `getEmailContentFn: ServerImport`: a function that returns the content of the e-mail that is sent to the user. (optional)
Defining `getEmailContentFn` is done by defining a function that looks like this:
```ts title="server/email.ts"
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>
`,
})
```
#### Email sender for email authentication
We require that you define an `emailSender`, so that Wasp knows how to send e-mails. Read more about that [here](#email-sender).
#### Validations
We provide basic validations out of the box. The validations are:
- `email`: non-empty, valid e-mail address
- `password`: non-empty, at least 8 characters, and contains a number
Note that `email`s are stored in a case-insensitive manner.
:::info
You don't need to worry about hashing the password yourself! Even when you are using Prisma's client directly and calling `create()` with a plain-text password, Wasp's middleware takes care of hashing it before storing it in the database. An additional middleware also performs field validation.
:::
### Social Login Providers (OAuth 2.0)
Wasp allows you to easily add social login providers to your app.
The following is a list of links to guides that will help you get started with the currently supported providers:
@ -1262,7 +1353,7 @@ When using Social Login Providers, Wasp gives you the following options:
- UI Helpers to make it easy to add social login buttons and actions
- Override settings to customize the behavior of the providers
### Default Settings
#### Default Settings
<Tabs>
@ -1345,7 +1436,7 @@ psl=}
:::note
the same `externalAuthEntity` can be used across different social login providers (e.g., both GitHub and Google can use the same entity).
:::
### UI helpers
#### UI helpers
Wasp provides sign-in buttons, logos and URLs for your login page:
@ -1373,7 +1464,7 @@ export default Login
If you need more customization than what the buttons provide, you can create your own custom components using the `signInUrl`s.
### Overrides
#### Overrides
When a user signs in for the first time, Wasp will create a new User account and link it to the chosen Auth Provider account for future logins. If the `userEntity` contains a `username` field it will default to a random dictionary phrase that does not exist in the database, such as `nice-blue-horse-27160`. This is a historical coupling between auth methods that will be removed over time.
@ -1491,6 +1582,103 @@ This function should return the user fields to use when creating a new user upon
- `generateAvailableDictionaryUsername` generates a random dictionary phrase that is not yet in the database. For example, `nice-blue-horse-27160`.
### Validation Error Handling
When creating, updating, or deleting entities, you may wish to handle validation errors. We have exposed a class called `AuthError` for this purpose. This could also be combined with [Prisma Error Helpers](/docs/language/features#prisma-error-helpers).
#### `import statement`:
```js
import AuthError from '@wasp/core/AuthError.js'
```
##### Example of usage:
```js
try {
await context.entities.User.update(...)
} catch (e) {
if (e instanceof AuthError) {
throw new HttpError(422, 'Validation failed', { message: e.message })
} else {
throw e
}
}
```
## Accessing the currently logged in user
When authentication is enabled in a Wasp app, we need a way to tell whether a user is logged in and access its data.
With that, we can further implement access control and decide which content is private and which public.
#### On the client
On the client, Wasp provides a React hook you can use in functional components - `useAuth`.
This hook is actually a thin wrapper over Wasp's [`useQuery` hook](http://localhost:3002/docs/language/features#the-usequery-hook) and returns data in the same format.
### `useAuth()`
#### `import statement`:
```js
import useAuth from '@wasp/auth/useAuth'
```
##### Example of usage:
```js title="src/client/pages/MainPage.js"
import React from 'react'
import { Link } from 'react-router-dom'
import useAuth from '@wasp/auth/useAuth'
import logout from '@wasp/auth/logout.js'
import Todo from '../Todo.js'
import '../Main.css'
const Main = () => {
const { data: user } = useAuth()
if (!user) {
return (
<span>
Please <Link to='/login'>login</Link> or <Link to='/signup'>sign up</Link>.
</span>
)
} else {
return (
<>
<button onClick={logout}>Logout</button>
<Todo />
< />
)
}
}
export default Main
```
#### On the server
### `context.user`
When authentication is enabled, all operations (actions and queries) will have access to the `user` through the `context` argument. `context.user` will contain all the fields from the user entity except for the password.
##### Example of usage:
```js title="src/server/actions.js"
import HttpError from '@wasp/core/HttpError.js'
export const createTask = async (task, context) => {
if (!context.user) {
throw new HttpError(403)
}
const Task = context.entities.Task
return Task.create({
data: {
description: task.description,
user: {
connect: { id: context.user.id }
}
}
})
}
```
In order to implement access control, each operation is responsible for checking `context.user` and
acting accordingly - e.g. if `context.user` is `undefined` and the operation is private then user
should be denied access to it.
## Client configuration
You can configure the client using the `client` field inside the `app`

View File

@ -53,13 +53,14 @@ module.exports = {
items: [
'integrations/github',
'integrations/google',
'guides/email-auth',
]
},
'integrations/css-frameworks',
'deploying',
'typescript',
'guides/testing',
'guides/sending-emails'
'guides/sending-emails',
],
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB