From 056bc947436478ea044a528936112cf705e0c893 Mon Sep 17 00:00:00 2001 From: David Nahodyl Date: Mon, 20 May 2024 20:10:00 -0400 Subject: [PATCH 01/17] Added session recipe --- .../4.sessions-and-authentication.md | 331 ++++++++++++++++++ 1 file changed, 331 insertions(+) create mode 100644 docs/2.guide/4.recipes/4.sessions-and-authentication.md diff --git a/docs/2.guide/4.recipes/4.sessions-and-authentication.md b/docs/2.guide/4.recipes/4.sessions-and-authentication.md new file mode 100644 index 0000000000..8b5e42a942 --- /dev/null +++ b/docs/2.guide/4.recipes/4.sessions-and-authentication.md @@ -0,0 +1,331 @@ +--- +title: 'Sessions and Authentication' +description: "User registration and authentication is an extremely common requirement in web apps. This recipe will show you how to implement basic user registration and authentication in you Nuxt app." +--- + +## Introduction + +In this recipe we'll be using [Drizzle](https://orm.drizzle.team/) with [db0](https://db0.unjs.io/) for database queries, 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: +- `id` (int, primary key, auto increment) +- `email` (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 + +### 1. Install nuxt-auth-utils +```bash +pnpm install nuxt-auth-utils +``` + +### 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. + +```dotenv [.env] +NUXT_SESSION_PASSWORD=password-with-at-least-32-characters +``` + +### 2. Create a registration page +Create a new page in your Nuxt app 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 [`$fetch`](https://nuxt.com/docs/getting-started/data-fetching#fetch) to post the data to `/api/register`. + +```vue [pages/register.vue] + + + + +``` + +### 3. Create an API route for registration +With the UI created we'll need to add a route to receive the registration form data. This route should accept a POST request with the email and password in the request body. It should hash the password and insert the user into the database. This route will only accept POST requests, so we'll follow the instructions for [API route methods](https://nuxt.com/docs/guide/directory-structure/server#matching-http-method) and name the file with `*.post.ts` to restrict the endpoint to only accept POST. + +This is a very simple example of registration. You would probably want to add some error handling and nice response messages. Additionally, you could log the user in as part of the registration process rather than redirecting them to the login screen. + +```typescript [server/api/register.post.ts] +import users from "~/database/schema/users"; +import getDatabase from "~/database/database"; +import bcrypt from "bcrypt"; + +export default defineEventHandler(async (event) => { + const body = await readBody(event); + + const db = await getDatabase(); + + // has the password before creating the user record + const passwordHash = bcrypt.hashSync(body.password, 12); + await db.insert(users).values({ + name: body.name, + email: body.email, + password: passwordHash, + }); +}); +``` + +### 4. Create a login page +Create a new page in your Nuxt app for user login. This page should have a form with fields for email and password and should submit a POST request to `/api/login`. + +Like the registration page, we'll intercept the form submission using `@submit.prevent` and use [`$fetch`](https://nuxt.com/docs/getting-started/data-fetching#fetch) to post the data to `/api/login`. + +This is a very simple login form example, so you'd definitely want to add more validation and error checking in a real-world application. + +```vue [pages/login.vue] + + + + +``` + +### 5. Create an API route for login +With the login form created, we need to create an API route to handle the login request. This route should accept a POST request with the email and password in the request body and check the email and password against the database. If the user and password match, we'll set a session cookie to log the user in. + +```typescript [server/api/auth/login.post.ts] +import users from "~/database/schema/users"; +import getDatabase from "~/database/database"; +import { eq } from "drizzle-orm"; +import bcrypt from "bcrypt"; + + const db = await getDatabase(); + + const user = (await db.select().from(users).where(eq(users.email, body.email)).limit(1))?.[0]; + + // compare the password hash + if (!user || !bcrypt.compareSync(body.password, user.password)) { + // throw an error if the user is not found or the password doesn't match + throw createError({ + statusCode: 401, + statusMessage: "Invalid email or password", + }); + } + + // set the session + await setUserSession(event, { + user: { + id: user.id, + name: user.name, + }, + loggedInAt: new Date(), + }); +``` + +The user should now be logged in! With the session set, we can get the current user session in any API route or page by calling `getUserSession(event)` which is auto-imported as a util function from the `nuxt-auth-utils` package. + +### 6. Create a logout API route +Users need to be able to log out, so we should create an API route to allow them to do this. This should require post request as well, just like login. We'll clear the session when this endpoint is called. + +```typescript [server/api/auth/logout.post.ts] +export default defineEventHandler(async (event) => { + // Clear the current user session + await clearUserSession(event); +}); +``` + +### 7. Create a server utility function to protect routes +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 create a server utility function to protect any API routes with sensitive data. For these sensitive routes, we should return a 401 error if the user is not logged in. + +We'll create a utility function which will make a reusable function to help protect our endpoints. Functions in the `/server/util` folder are auto-imported to server endpoints. You can read about the `/server/util` folder [here](https://nuxt.com/docs/guide/directory-structure/utils). This utility function will help us prevent data from being accessed by users who are not logged in. + +The file will be named `requireUserLoggedIn.ts` which will make this utl function available in any server route by calling `requireUserLoggedIn(event)`. + +```typescript [server/utils/requireUserLoggedIn.ts] +export default async (event) => { + await requireUserSession(event); +}; +``` + +### 8. Protect a route with the utility function +Now that we have the utility function to protect routes, we can use it in any API route to ensure that only logged-in users can access the route. + +In the example below, we use the `requireUserLoggedIn` utility function to protect the `/api/users.get` route. This route will only be accessible to logged-in users. + +```typescript [server/api/users.get.ts] +import getDatabase from "~/database/database"; +import users from "~/database/schema/users"; +import requireUserLoggedIn from "~/server/utils/requireUserLoggedIn"; + +export default defineEventHandler(async (event) => { + // make sure the user is logged in + await requireUserLoggedIn(event); + + const db = await getDatabase(); + // Send back the list of users + const userList = await db.select({ name: users.name, id: users.id }).from(users).limit(10); + + return userList; +}); +``` + + +### 9. Create a front-end 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. + +`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. + +```typescript [middleware/RedirectIfNotAuthenticated.ts] +export default defineNuxtRouteMiddleware(() => { + // check if the user is logged in + const { loggedIn } = useUserSession(); + + // redirect the user to the login screen if they're not authenticated + if (!loggedIn.value) { + return navigateTo("/login"); + } + + return null; +}); +``` + +### 10. Protect a route with the front-end 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. + +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. + +> :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. + +```vue [pages/users/index.vue] + + + +``` + +### Complete! + +We've successfully set up user registration and authentication in our Nuxt app. Users can now register, log in, and log out. We've also protected sensitive routes on the server and client side to ensure that only authenticated users can access them. From 51450631cdc61a03850c19b40e1261c77a55fe1c Mon Sep 17 00:00:00 2001 From: David Nahodyl Date: Mon, 20 May 2024 20:16:04 -0400 Subject: [PATCH 02/17] clarified warning --- docs/2.guide/4.recipes/4.sessions-and-authentication.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/2.guide/4.recipes/4.sessions-and-authentication.md b/docs/2.guide/4.recipes/4.sessions-and-authentication.md index 8b5e42a942..e112fdd3e2 100644 --- a/docs/2.guide/4.recipes/4.sessions-and-authentication.md +++ b/docs/2.guide/4.recipes/4.sessions-and-authentication.md @@ -298,7 +298,9 @@ Now that we have the front-end middleware to protect front-end routes, we can us 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. -> :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. +::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. +:: ```vue [pages/users/index.vue] @@ -124,6 +135,10 @@ export default defineEventHandler(async (event) => { email: body.email, password: passwordHash, }); + + // log the user in as the user that was just created + const user = (await db.select().from(users).where(eq(users.email, body.email)).limit(1))[0]; + await auth.login(event, user); }); ``` @@ -151,16 +166,28 @@ This is a very simple login form example, so you'd definitely want to add more v error.value = null; // perform the login - await $fetch("/api/auth/login", { - method: "POST", - body: form.value, - onResponseError: () => { - error.value = "Error logging in"; - }, - }); + try { + await $fetch("/api/auth/login", { + method: "POST", + body: loginForm.value, + }); + } catch (e) { + // if there's an error, set the error message and return early + error.value = "Error logging in"; + return; + } - // we're not using Nuxt Router here so that we can easily trigger a whole page load and get everything refreshed now that the user is logged in - window.location.href = "/"; + // refresh the session status now that the user is logged in + const { fetch } = useUserSession(); + await fetch(); + // you may want to use something like Pinia to manage global state of the logged-in user + // update Pinia state here... + + // take the user to the auth-only users index page now that they're logged in + await navigateTo("/users"); + + // Alternative - Don't use Nuxt Router here so that we can easily trigger a whole page load and get the whole UI refreshed now that the user is logged in. + // window.location.href = "/users"; } @@ -221,7 +248,7 @@ import bcrypt from "bcrypt"; } // set the session - await setUserSession(event, { + await replaceUserSession(event, { user: { id: user.id, name: user.name, From b97b4a083a17ad3cc11017f57c53a8016cb669f9 Mon Sep 17 00:00:00 2001 From: David Nahodyl Date: Mon, 10 Jun 2024 17:24:49 -0400 Subject: [PATCH 07/17] more details --- .../4.sessions-and-authentication.md | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/docs/2.guide/4.recipes/4.sessions-and-authentication.md b/docs/2.guide/4.recipes/4.sessions-and-authentication.md index 5b24d512a2..0ee723085f 100644 --- a/docs/2.guide/4.recipes/4.sessions-and-authentication.md +++ b/docs/2.guide/4.recipes/4.sessions-and-authentication.md @@ -3,9 +3,11 @@ title: 'Sessions and Authentication' description: "User registration and authentication is an extremely common requirement in web apps. This recipe will show you how to implement basic user registration and authentication in you Nuxt app." --- + ## Introduction -In this recipe we'll be using [Drizzle](https://orm.drizzle.team/) with [db0](https://db0.unjs.io/) for database queries, but you can use any ORM or database connection strategy you prefer. +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. You'll need a `users` table in your database with the following columns: - `id` (int, primary key, auto increment) @@ -18,6 +20,10 @@ Additionally, we'll use [nuxt-aut-utils](https://github.com/Atinux/nuxt-auth-uti ### 1. Install nuxt-auth-utils + +Install the [auth-utils](https://github.com/Atinux/nuxt-auth-utils) module using the `nuxi` CLI. + + ```bash npx nuxi@latest module add auth-utils ``` @@ -32,7 +38,7 @@ NUXT_SESSION_PASSWORD=password-with-at-least-32-characters ### 2. Create a registration page -Create a new page in your Nuxt app 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 [`$fetch`](/docs/getting-started/data-fetching#fetch) to post the data to `/api/register`. +Create a new page in your Nuxt app 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 [`$fetch`](/docs/getting-started/data-fetching#fetch) to post the data to `/api/register`. Here's an example registration form for reference: ```vue [pages/register.vue] @@ -250,26 +253,35 @@ This is a very simple login form example, so you'd definitely want to add more v With the login form created, we need to create an API route to handle the login request. This route should accept a POST request with the email and password in the request body and check the email and password against the database. If the user and password match, we'll set a session cookie to log the user in. -```typescript [server/api/auth/login.post.ts] +This server API route should be at `/server/api/auth/login.post.ts`. Just like the registration form endpoint, suffixing the filename in `.post.ts.` means that this handler will only respond to post requests. + +```typescript [/server/api/auth/login.post.ts] import users from "~/database/schema/users"; import getDatabase from "~/database/database"; import { eq } from "drizzle-orm"; import bcrypt from "bcrypt"; +export default defineEventHandler(async (event) => { const db = await getDatabase(); - const user = (await db.select().from(users).where(eq(users.email, body.email)).limit(1))?.[0]; + const foundUser = ( + await db + .select({ id: users.id, name: users.name, email: users.email, password: users.password }) + .from(users) + .where(eq(users.email, email)) + .limit(1) + )?.[0]; // compare the password hash - if (!user || !bcrypt.compareSync(body.password, user.password)) { - // throw an error if the user is not found or the password doesn't match + if (!foundUser || !bcrypt.compareSync(password, foundUser.password)) { + // return an error if the user is not found or the password doesn't match throw createError({ statusCode: 401, statusMessage: "Invalid email or password", }); } - // set the session + // log in as the selected user await replaceUserSession(event, { user: { id: user.id, @@ -277,6 +289,9 @@ import bcrypt from "bcrypt"; }, loggedInAt: new Date(), }); +}); + + ``` The user should now be logged in! With the session set, we can get the current user session in any API route or page by calling `getUserSession(event)` which is auto-imported as a util function from the `nuxt-auth-utils` package. @@ -285,6 +300,8 @@ The user should now be logged in! With the session set, we can get the current u Users need to be able to log out, so we should create an API route to allow them to do this. This should require post request as well, just like login. We'll clear the session when this endpoint is called. +We'll use the `clearUserSession` from the `auth-utils` module to log the user out and clear the session data. This function is automatically imported from the module by Nuxt, so we don't need to manually import it. + ```typescript [server/api/auth/logout.post.ts] export default defineEventHandler(async (event) => { // Clear the current user session From fd435ca06b5bbba0bfbbd39b96892e2e304e570b Mon Sep 17 00:00:00 2001 From: David Nahodyl Date: Mon, 10 Jun 2024 19:26:24 -0400 Subject: [PATCH 10/17] removed unnecessary utility function, improvements to the back-half of the guide --- .../4.sessions-and-authentication.md | 44 +++++++++---------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/docs/2.guide/4.recipes/4.sessions-and-authentication.md b/docs/2.guide/4.recipes/4.sessions-and-authentication.md index 19b2668c89..8d8d55a438 100644 --- a/docs/2.guide/4.recipes/4.sessions-and-authentication.md +++ b/docs/2.guide/4.recipes/4.sessions-and-authentication.md @@ -309,36 +309,27 @@ export default defineEventHandler(async (event) => { }); ``` -### 7. Create a server utility function to protect routes +### 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 create a server utility function to 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. 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. -We'll create a utility function which will make a reusable function to help protect our endpoints. Functions in the `/server/util` folder are auto-imported to server endpoints. You can read about the `/server/util` folder [here](https://nuxt.com/docs/guide/directory-structure/utils). This utility function will help us prevent data from being accessed by users who are 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 file will be named `requireUserLoggedIn.ts` which will make this utl function available in any server route by calling `requireUserLoggedIn(event)`. +In the example below, we use the `requireUserSession` utility function to protect the `/server/api/users.get.ts` server route. This route will only be accessible to logged-in users. -```typescript [server/utils/requireUserLoggedIn.ts] -export default async (event) => { - await requireUserSession(event); -}; -``` - -### 8. Protect a route with the utility function - -Now that we have the utility function to protect routes, we can use it in any API route to ensure that only logged-in users can access the route. - -In the example below, we use the `requireUserLoggedIn` utility function to protect the `/api/users.get` route. This route will only be accessible to logged-in users. - -```typescript [server/api/users.get.ts] +```typescript [/server/api/users.get.ts] import getDatabase from "~/database/database"; import users from "~/database/schema/users"; import requireUserLoggedIn from "~/server/utils/requireUserLoggedIn"; export default defineEventHandler(async (event) => { // make sure the user is logged in - await requireUserLoggedIn(event); + // This will throw a 401 error if the request doesn't come from a valid user session + await requireUserSession(event); + // If we make it hear, the user is authenticated. It's safe to fetch and return data const db = await getDatabase(); + // Send back the list of users const userList = await db.select({ name: users.name, id: users.id }).from(users).limit(10); @@ -346,13 +337,15 @@ export default defineEventHandler(async (event) => { }); ``` -### 9. Create a front-end middleware to protect routes +### 8. Create a front-end 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. `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. -```typescript [middleware/RedirectIfNotAuthenticated.ts] +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. + +```typescript [/middleware/RedirectIfNotAuthenticated.ts] export default defineNuxtRouteMiddleware(() => { // check if the user is logged in const { loggedIn } = useUserSession(); @@ -366,7 +359,7 @@ export default defineNuxtRouteMiddleware(() => { }); ``` -### 10. Protect a route with the front-end middleware +### 9. Protect a route with the front-end 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. @@ -378,11 +371,14 @@ We'll use [`definePageMeta`](https://nuxt.com/docs/api/utils/define-page-meta) t ```vue [pages/users/index.vue] ``` -### Complete +### Complete! We've successfully set up user registration and authentication in our Nuxt app. Users can now register, log in, and log out. We've also protected sensitive routes on the server and client side to ensure that only authenticated users can access them. From e9e6c1b142d7269dfe0c7cf3055fd3b64120abec Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sun, 16 Jun 2024 17:03:10 +0000 Subject: [PATCH 11/17] [autofix.ci] apply automated fixes --- docs/2.guide/4.recipes/4.sessions-and-authentication.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/2.guide/4.recipes/4.sessions-and-authentication.md b/docs/2.guide/4.recipes/4.sessions-and-authentication.md index 8d8d55a438..80d4a4bda4 100644 --- a/docs/2.guide/4.recipes/4.sessions-and-authentication.md +++ b/docs/2.guide/4.recipes/4.sessions-and-authentication.md @@ -20,10 +20,8 @@ Additionally, we'll use [nuxt-aut-utils](https://github.com/Atinux/nuxt-auth-uti ### 1. Install nuxt-auth-utils - Install the [auth-utils](https://github.com/Atinux/nuxt-auth-utils) module using the `nuxi` CLI. - ```bash npx nuxi@latest module add auth-utils ``` @@ -38,7 +36,7 @@ NUXT_SESSION_PASSWORD=password-with-at-least-32-characters ### 2. Create a registration page -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. @@ -398,6 +396,6 @@ const { data: users } = await useFetch("/api/users"); ``` -### Complete! +### Complete We've successfully set up user registration and authentication in our Nuxt app. Users can now register, log in, and log out. We've also protected sensitive routes on the server and client side to ensure that only authenticated users can access them. From f7ac32f50949621b2f5d242492174890ba93c024 Mon Sep 17 00:00:00 2001 From: David Nahodyl Date: Sun, 16 Jun 2024 13:04:49 -0400 Subject: [PATCH 12/17] updated based on feedback from GalacticHypernova --- .../4.sessions-and-authentication.md | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/docs/2.guide/4.recipes/4.sessions-and-authentication.md b/docs/2.guide/4.recipes/4.sessions-and-authentication.md index 80d4a4bda4..d42e470124 100644 --- a/docs/2.guide/4.recipes/4.sessions-and-authentication.md +++ b/docs/2.guide/4.recipes/4.sessions-and-authentication.md @@ -7,15 +7,13 @@ description: "User registration and authentication is an extremely common requir ## Introduction 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: - `id` (int, primary key, auto increment) - `email` (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 ### 1. Install nuxt-auth-utils @@ -28,7 +26,7 @@ npx nuxi@latest module add auth-utils ### 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] 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. -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: @@ -309,7 +307,7 @@ export default defineEventHandler(async (event) => { ### 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. @@ -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 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(); // 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] 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. ::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] From 5047501be473476488b6e03590eb7a5672ee2726 Mon Sep 17 00:00:00 2001 From: David Nahodyl Date: Sun, 16 Jun 2024 13:15:39 -0400 Subject: [PATCH 13/17] fixed module name for consistency. --- docs/2.guide/4.recipes/4.sessions-and-authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/2.guide/4.recipes/4.sessions-and-authentication.md b/docs/2.guide/4.recipes/4.sessions-and-authentication.md index d42e470124..48426f43bd 100644 --- a/docs/2.guide/4.recipes/4.sessions-and-authentication.md +++ b/docs/2.guide/4.recipes/4.sessions-and-authentication.md @@ -18,7 +18,7 @@ You'll need a `users` table in your database with the following columns: ### 1. Install nuxt-auth-utils -Install the [auth-utils](https://github.com/Atinux/nuxt-auth-utils) module using the `nuxi` CLI. +Install the [nuxt-auth-utils](https://github.com/Atinux/nuxt-auth-utils) module using the `nuxi` CLI. ```bash npx nuxi@latest module add auth-utils From 2037207e02a56c18711ac3e0c6cdb5f31e7eda86 Mon Sep 17 00:00:00 2001 From: David Nahodyl Date: Sun, 16 Jun 2024 13:24:46 -0400 Subject: [PATCH 14/17] Victims of the great "data is/are" debate --- docs/2.guide/4.recipes/4.sessions-and-authentication.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/2.guide/4.recipes/4.sessions-and-authentication.md b/docs/2.guide/4.recipes/4.sessions-and-authentication.md index 48426f43bd..5b9da43ebd 100644 --- a/docs/2.guide/4.recipes/4.sessions-and-authentication.md +++ b/docs/2.guide/4.recipes/4.sessions-and-authentication.md @@ -307,7 +307,7 @@ export default defineEventHandler(async (event) => { ### 7. Protect your server route -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. +Protecting server routes is key to making sure your data is 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. @@ -335,7 +335,7 @@ export default defineEventHandler(async (event) => { ### 8. Create a client-side middleware to protect routes -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. +Our data is 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 logged in, and redirect them if they are not. @@ -362,7 +362,7 @@ Now that we have the client-side middleware to protect client-side routes, we ca 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 -: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. +:warning: Remember that your data isn'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] From a831877cc7d07617f5ec82b1fae2d5d9a407cd05 Mon Sep 17 00:00:00 2001 From: David Nahodyl Date: Sun, 16 Jun 2024 13:30:26 -0400 Subject: [PATCH 15/17] grammar suggestions from GalacticHypernova --- docs/2.guide/4.recipes/4.sessions-and-authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/2.guide/4.recipes/4.sessions-and-authentication.md b/docs/2.guide/4.recipes/4.sessions-and-authentication.md index 5b9da43ebd..c7e11e89cc 100644 --- a/docs/2.guide/4.recipes/4.sessions-and-authentication.md +++ b/docs/2.guide/4.recipes/4.sessions-and-authentication.md @@ -26,7 +26,7 @@ npx nuxi@latest module add auth-utils ### 1a. (Optional) Add a session encryption key -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. +Session cookies are encrypted using a key from the `.env` file. This key will be added to your `.env` automatically when running in development mode for the first time. However, you'll need to add this to your production environment before deploying. ```dotenv [.env] NUXT_SESSION_PASSWORD=password-with-at-least-32-characters From 95dabb13ade9e44713961dd06595e42c49d34ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Chopin?= Date: Fri, 19 Jul 2024 16:08:02 +0200 Subject: [PATCH 16/17] Update docs/2.guide/4.recipes/4.sessions-and-authentication.md --- docs/2.guide/4.recipes/4.sessions-and-authentication.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/2.guide/4.recipes/4.sessions-and-authentication.md b/docs/2.guide/4.recipes/4.sessions-and-authentication.md index c7e11e89cc..5eb8c1e76f 100644 --- a/docs/2.guide/4.recipes/4.sessions-and-authentication.md +++ b/docs/2.guide/4.recipes/4.sessions-and-authentication.md @@ -1,6 +1,6 @@ --- title: 'Sessions and Authentication' -description: "User registration and authentication is an extremely common requirement in web apps. This recipe will show you how to implement basic user registration and authentication in you Nuxt app." +description: "Authentication is an extremely common requirement in web apps. This recipe will show you how to implement basic user registration and authentication in your Nuxt app." --- From 93f5fe07aae4933ae9927e77a3c919ed36197fdb Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Fri, 19 Jul 2024 15:47:41 +0100 Subject: [PATCH 17/17] Update 4.sessions-and-authentication.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sébastien Chopin --- docs/2.guide/4.recipes/4.sessions-and-authentication.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/2.guide/4.recipes/4.sessions-and-authentication.md b/docs/2.guide/4.recipes/4.sessions-and-authentication.md index 5eb8c1e76f..f9e8e2d940 100644 --- a/docs/2.guide/4.recipes/4.sessions-and-authentication.md +++ b/docs/2.guide/4.recipes/4.sessions-and-authentication.md @@ -6,8 +6,7 @@ description: "Authentication is an extremely common requirement in web apps. Thi ## Introduction -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 [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. +In this recipe we'll be setting up user registration, login, sessions, and authentication in a full-stack Nuxt app using [Nuxt Auth Utils](https://github.com/Atinux/nuxt-auth-utils) 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 the built-in [Nitro SQL database](https://nitro.unjs.io/guide/database), 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: - `id` (int, primary key, auto increment)