Сессии и аутентификация
Введение
Настроим аутентификацию в full-stack Nuxt с Nuxt Auth Utils: удобные утилиты для сессии на клиенте и сервере.
Модуль хранит сессию в подписанных и запечатанных cookie, отдельная БД для сессий не нужна.
Установка nuxt-auth-utils
Установите модуль nuxt-auth-utils через CLI Nuxt.
npx nuxt module add auth-utils
nuxt-auth-utils в зависимости и пропишет модуль в nuxt.config.ts.Ключ шифрования cookie
Так как nuxt-auth-utils использует запечатанные cookie, сессия шифруется секретом из переменной окружения NUXT_SESSION_PASSWORD.
.env автоматически.NUXT_SESSION_PASSWORD=a-random-password-with-at-least-32-characters
API-маршрут входа
Создадим простой API-маршрут входа по статическим данным.
Маршрут /api/login принимает POST с email и password в теле.
import { z } from 'zod'
const bodySchema = z.object({
email: z.email(),
password: z.string().min(8),
})
export default defineEventHandler(async (event) => {
const { email, password } = await readValidatedBody(event, bodySchema.parse)
if (email === 'admin@admin.com' && password === 'iamtheadmin') {
// set the user session in the cookie
// this server util is auto-imported by the auth-utils module
await setUserSession(event, {
user: {
name: 'John Doe',
},
})
return {}
}
throw createError({
status: 401,
message: 'Bad credentials',
})
})
zod в проект (npm i zod).Страница входа
Модуль экспортирует Vue-композабл для проверки, вошёл ли пользователь:
<script setup>
const { loggedIn, session, user, clear, fetch } = useUserSession()
</script>
Сделаем страницу входа с формой, отправляющей данные на /api/login.
<script setup lang="ts">
const { loggedIn, user, fetch: refreshSession } = useUserSession()
const credentials = reactive({
email: '',
password: '',
})
async function login () {
try {
await $fetch('/api/login', {
method: 'POST',
body: credentials,
})
// Refresh the session on client-side and redirect to the home page
await refreshSession()
await navigateTo('/')
} catch {
alert('Bad credentials')
}
}
</script>
<template>
<form @submit.prevent="login">
<input
v-model="credentials.email"
type="email"
placeholder="Email"
>
<input
v-model="credentials.password"
type="password"
placeholder="Password"
>
<button type="submit">
Login
</button>
</form>
</template>
Защита API-маршрутов
Клиентский middleware удобен для UX, но без сервера данные всё равно доступны. Критично защищать чувствительные маршруты и отдавать 401, если сессии нет.
В auth-utils есть requireUserSession — проверка активной сессии.
Пример /api/user/stats только для авторизованных:
export default defineEventHandler(async (event) => {
// make sure the user is logged in
// This will throw a 401 error if the request doesn't come from a valid user session
const { user } = await requireUserSession(event)
// TODO: Fetch some stats based on the user
return {}
})
Защита маршрутов приложения
С серверной защитой данные в порядке, но без клиента неавторизованный пользователь может увидеть странное поведение на /users. Добавим клиентский middleware, чтобы перенаправлять на логин.
nuxt-auth-utils даёт useUserSession — по нему проверяем вход и делаем редирект.
Создаём middleware в каталоге middleware. На клиенте его нужно явно подключать к маршрутам.
export default defineNuxtRouteMiddleware(() => {
const { loggedIn } = useUserSession()
// redirect the user to the login screen if they're not authenticated
if (!loggedIn.value) {
return navigateTo('/login')
}
})
Главная страница
Подключаем middleware на домашней странице с данными пользователя: без сессии будет редирект на логин.
Используем definePageMeta:
<script setup lang="ts">
definePageMeta({
middleware: ['authenticated'],
})
const { user, clear: clearSession } = useUserSession()
async function logout () {
await clearSession()
await navigateTo('/login')
}
</script>
<template>
<div>
<h1>Welcome {{ user.name }}</h1>
<button @click="logout">
Logout
</button>
</div>
</template>
Кнопка выхода очищает сессию и ведёт на страницу входа.
Итог
Настроены базовая аутентификация и сессии, защищены чувствительные маршруты на сервере и клиенте.
Дальше можно:
- Подключить более 20 OAuth-провайдеров
- Добавить БД для пользователей: Nitro SQL Database или NuxtHub SQL Database
- Регистрацию по email и паролю с хешированием паролей
- WebAuthn / Passkeys
Полный пример с OAuth, БД и CRUD — репозиторий atidone.