updated based on feedback from GalacticHypernova

This commit is contained in:
David Nahodyl 2024-06-16 13:04:49 -04:00
parent e9e6c1b142
commit f7ac32f509
No known key found for this signature in database

View File

@ -7,15 +7,13 @@ description: "User registration and authentication is an extremely common requir
## Introduction ## Introduction
In this recipe we'll be setting up user registration, login, sessions, and authentication in a full-stack Nuxt app. In this recipe we'll be setting up user registration, login, sessions, and authentication in a full-stack Nuxt app.
We'll be using [Nuxt Auth Utils](https://github.com/Atinux/nuxt-auth-utils) by [Altinux (Sébastien Chopin)](https://github.com/Atinux) which provides convenient utilities for managing front-end and back-end session data. We'll install and use this to get the core session management functionality we're going to need to manage user logins. For the database ORM we'll be using [Drizzle](https://orm.drizzle.team/) with [db0](https://db0.unjs.io/), but you can use any ORM or database connection strategy you prefer. We'll be using [Nuxt Auth Utils](https://github.com/Atinux/nuxt-auth-utils) by [Atinux (Sébastien Chopin)](https://github.com/Atinux) which provides convenient utilities for managing client-side and server-side session data. We'll install and use this to get the core session management functionality we're going to need to manage user logins. For the database ORM we'll be using [Drizzle](https://orm.drizzle.team/) with [db0](https://db0.unjs.io/), but you can use any ORM or database connection strategy you prefer.
You'll need a `users` table in your database with the following columns: You'll need a `users` table in your database with the following columns:
- `id` (int, primary key, auto increment) - `id` (int, primary key, auto increment)
- `email` (varchar) - `email` (varchar)
- `password` (varchar) - `password` (varchar)
Additionally, we'll use [nuxt-aut-utils](https://github.com/Atinux/nuxt-auth-utils) by [Atinux](https://github.com/Atinux) to handle the authentication and session management.
## Steps ## Steps
### 1. Install nuxt-auth-utils ### 1. Install nuxt-auth-utils
@ -28,7 +26,7 @@ npx nuxi@latest module add auth-utils
### 1a. (Optional) Add a session encryption key ### 1a. (Optional) Add a session encryption key
Session cookies are encrypted. The encryption key is set from the `.env` file. This key will be added to your `.env` automatically when running in development mode the first time. However, you'll need to add this to your production environment before deploying. Session cookies are encrypted using a key set from the `.env` file. This key will be added to your `.env` automatically when running in development mode the first time. However, you'll need to add this to your production environment before deploying.
```dotenv [.env] ```dotenv [.env]
NUXT_SESSION_PASSWORD=password-with-at-least-32-characters NUXT_SESSION_PASSWORD=password-with-at-least-32-characters
@ -38,7 +36,7 @@ NUXT_SESSION_PASSWORD=password-with-at-least-32-characters
The first page we'll need is a page for users to register and create new accounts. Create a new Vue page in your Nuxt app at `/pages/register.vue` for user registration. This page should have a form with fields for email and password. We'll intercept the form submission using `@submit.prevent` and use the [`$fetch`](/docs/getting-started/data-fetching#fetch) utility to post the data to `/api/register`. This form POST will be received by Nuxt in an API route which we will set up next. The first page we'll need is a page for users to register and create new accounts. Create a new Vue page in your Nuxt app at `/pages/register.vue` for user registration. This page should have a form with fields for email and password. We'll intercept the form submission using `@submit.prevent` and use the [`$fetch`](/docs/getting-started/data-fetching#fetch) utility to post the data to `/api/register`. This form POST will be received by Nuxt in an API route which we will set up next.
If the request is successful, we'll navigate to the (soon to be created) `/users` page, which will be guarded and only be visible to logged in users. If the request is successful, we'll navigate to the (soon to be created) `/users` page, which will be guarded and only visible to logged in users.
Here's an example registration form for reference: Here's an example registration form for reference:
@ -309,7 +307,7 @@ export default defineEventHandler(async (event) => {
### 7. Protect your server route ### 7. Protect your server route
Protecting server routes is key to making sure your data are safe. Front-end middleware is helpful for the user, but without back-end protection your data can still be accessed. Because of this, it is critical that we protect any API routes with sensitive data. For these sensitive routes, we should return a 401 error if the user is not logged in. Protecting server routes is key to making sure your data are safe. Client-side middleware is helpful for the user, but without server-side protection your data can still be accessed. Because of this, it is critical that we protect any API routes with sensitive data. For these sensitive routes, we should return a 401 error if the user is not logged in.
The `auth-utils` module provides the `requireUserSession` utility function to help make sure that users are logged in and have an active session. We can use this to protect our different endpoints. Like many of the other utilities from the auth module, it is automatically imported in our server endpoints. The `auth-utils` module provides the `requireUserSession` utility function to help make sure that users are logged in and have an active session. We can use this to protect our different endpoints. Like many of the other utilities from the auth module, it is automatically imported in our server endpoints.
@ -325,7 +323,7 @@ export default defineEventHandler(async (event) => {
// This will throw a 401 error if the request doesn't come from a valid user session // This will throw a 401 error if the request doesn't come from a valid user session
await requireUserSession(event); await requireUserSession(event);
// If we make it hear, the user is authenticated. It's safe to fetch and return data // If we make it here, the user is authenticated. It's safe to fetch and return data
const db = await getDatabase(); const db = await getDatabase();
// Send back the list of users // Send back the list of users
@ -335,13 +333,13 @@ export default defineEventHandler(async (event) => {
}); });
``` ```
### 8. Create a front-end middleware to protect routes ### 8. Create a client-side middleware to protect routes
Our data are safe with the back-end route in place, but without doing anything else, unauthenticated users would probably get some odd data when trying to access the `/users` page. We should create a [front-end middleware](https://nuxt.com/docs/guide/directory-structure/middleware) to protect the route on the client side and redirect users to a login page. Our data are safe with the server-side route in place, but without doing anything else, unauthenticated users would probably get some odd data when trying to access the `/users` page. We should create a [client-side middleware](https://nuxt.com/docs/guide/directory-structure/middleware) to protect the route on the client side and redirect users to the login page.
`nuxt-auth-utils` provides a convenient `useUserSession` composable which we'll use to check if the user is actually logged in, and redirect them if they are not. `nuxt-auth-utils` provides a convenient `useUserSession` composable which we'll use to check if the user is logged in, and redirect them if they are not.
We'll create a middleware in the `/middleware` directory. Unlike on the server, front-end middleware is not automatically applied to all endpoints, and we'll need to specify where we want it applied. We'll create a middleware in the `/middleware` directory. Unlike on the server, client-side middleware is not automatically applied to all endpoints, and we'll need to specify where we want it applied.
```typescript [/middleware/RedirectIfNotAuthenticated.ts] ```typescript [/middleware/RedirectIfNotAuthenticated.ts]
export default defineNuxtRouteMiddleware(() => { export default defineNuxtRouteMiddleware(() => {
@ -357,14 +355,14 @@ export default defineNuxtRouteMiddleware(() => {
}); });
``` ```
### 9. Protect a route with the front-end middleware ### 9. Protect a route with the client-side middleware
Now that we have the front-end middleware to protect front-end routes, we can use it in any page to ensure that only logged-in users can access the route. Users will be redirected to the login page if they are not authenticated. Now that we have the client-side middleware to protect client-side routes, we can use it in any page to ensure that only logged-in users can access the route. Users will be redirected to the login page if they are not authenticated.
We'll use [`definePageMeta`](https://nuxt.com/docs/api/utils/define-page-meta) to apply the middleware to the route that we want to protect. We'll use [`definePageMeta`](https://nuxt.com/docs/api/utils/define-page-meta) to apply the middleware to the route that we want to protect.
::important ::important
:warning: Remember that your data aren't really secure without back-end protection! Always secure your data on the back-end first before worrying about the front-end. :warning: Remember that your data aren't really secure without server-side protection! Always secure your data server-side first before worrying about the client-side.
:: ::
```vue [pages/users/index.vue] ```vue [pages/users/index.vue]