Nuxt 认证工具
[![npm 版本][npm-version-src]][npm-version-href] [![npm 下载量][npm-downloads-src]][npm-downloads-href] [![许可证][license-src]][license-href] [![Nuxt][nuxt-src]][nuxt-href]
为 Nuxt 应用程序添加身份验证功能,使用安全且密封的 Cookie 会话。
功能
- 支持混合渲染(SSR / CSR / SWR / 预渲染)
- 15+ OAuth 提供商
- Vue 组合式函数
- 服务器工具
<AuthState>
组件- 可通过钩子扩展
它依赖很少(仅来自 UnJS),可在多个 JS 环境(Node, Deno, Workers)中运行,并完全使用 TypeScript 进行类型化。
要求
此模块仅适用于运行 Nuxt 服务器的情况,因为它使用服务器 API 路由(nuxt build
)。
这意味着您不能将此模块与 nuxt generate
一起使用。
不过,您可以使用混合渲染来预渲染应用程序的页面或完全禁用服务器端渲染。
快速设置
- 在您的 Nuxt 项目中添加
nuxt-auth-utils
npx nuxi@latest module add auth-utils
- 在
.env
中添加一个至少 32 个字符的NUXT_SESSION_PASSWORD
环境变量。
# .env
NUXT_SESSION_PASSWORD=password-with-at-least-32-characters
如果未设置 NUXT_SESSION_PASSWORD
,Nuxt Auth Utils 会在首次以开发模式运行 Nuxt 时为您生成一个。
- 就是这样!您现在可以为 Nuxt 应用程序添加身份验证了 ✨
Vue 组合式函数
Nuxt Auth Utils 自动添加一些插件来获取当前用户会话,让您可以从 Vue 组件中访问它。
用户会话
<script setup>
const { loggedIn, user, session, fetch, clear } = useUserSession()
</script>
<template>
<div v-if="loggedIn">
<h1>欢迎 {{ user.login }}!</h1>
<p>登录时间:{{ session.loggedInAt }}</p>
<button @click="clear">退出登录</button>
</div>
<div v-else>
<h1>未登录</h1>
<a href="/auth/github">使用 GitHub 登录</a>
</div>
</template>
TypeScript 签名:
interface UserSessionComposable {
/**
* 计算属性,表示认证会话是否就绪
*/
ready: ComputedRef<boolean>
/**
* 计算属性,表示用户是否已登录
*/
loggedIn: ComputedRef<boolean>
/**
* 如果已登录则为用户对象,否则为 null
*/
user: ComputedRef<User | null>
/**
* 会话对象
*/
session: Ref<UserSession>
/**
* 从服务器获取用户会话
*/
fetch: () => Promise<void>
/**
* 清除用户会话并移除会话 cookie
*/
clear: () => Promise<void>
}
服务器工具
以下辅助函数在您的 server/
目录中自动导入。
会话管理
// 设置用户会话,注意这些数据在 cookie 中加密,但可以通过 API 调用解密
// 只存储允许您识别用户的数据,不要存储敏感数据
// 使用 defu() 将新数据与现有数据合并
await setUserSession(event, {
user: {
// ... 用户数据
},
loggedInAt: new Date()
// 任何额外字段
})
// 替换用户会话。与 setUserSession 行为相同,但不会与现有数据合并
await replaceUserSession(event, data)
// 获取当前用户会话
const session = await getUserSession(event)
// 清除当前用户会话
await clearUserSession(event)
// 要求用户会话(如果会话中没有 `user` 键,则返回 401)
const session = await requireUserSession(event)
您可以通过在项目中创建类型声明文件(例如 auth.d.ts
)来定义用户会话的类型,以扩展 UserSession
类型:
// auth.d.ts
declare module '#auth-utils' {
interface User {
// 添加您自己的字段
}
interface UserSession {
// 添加您自己的字段
}
}
export {}
OAuth 事件处理器
所有处理器都可以在服务器路由或 API 路由中自动导入和使用。
模式是 oauth<Provider>EventHandler({ onSuccess, config?, onError? })
,例如:oauthGitHubEventHandler
。
该辅助函数返回一个事件处理器,自动重定向到提供商授权页面,然后根据结果调用 onSuccess
或 onError
。
config
可以直接在 nuxt.config.ts
的 runtimeConfig
中定义:
export default defineNuxtConfig({
runtimeConfig: {
oauth: {
// 小写的提供商名称 (github, google 等)
<provider>: {
clientId: '...',
clientSecret: '...'
}
}
}
})
也可以使用环境变量设置:
NUXT_OAUTH_<PROVIDER>_CLIENT_ID
NUXT_OAUTH_<PROVIDER>_CLIENT_SECRET
提供商名称应为大写 (GITHUB, GOOGLE 等)
支持的 OAuth 提供商
- Auth0
- AWS Cognito
- Battle.net
- Discord
- GitHub
- Keycloak
- Microsoft
- PayPal
- Spotify
- Steam
- Twitch
- X (Twitter)
- XSUAA
- Yandex
您可以通过在 src/runtime/server/lib/oauth/ 中创建新文件来添加您喜欢的提供商。
示例
示例:~/server/routes/auth/github.get.ts
export default oauthGitHubEventHandler({
config: {
emailRequired: true
},
async onSuccess(event, { user, tokens }) {
await setUserSession(event, {
user: {
githubId: user.id
}
})
return sendRedirect(event, '/')
},
// 可选,默认会返回 json 错误和 401 状态码
onError(event, error) {
console.error('GitHub OAuth 错误:', error)
return sendRedirect(event, '/')
},
})
确保在 OAuth 应用程序设置中将回调 URL 设置为 <your-domain>/auth/github
。
如果在生产环境中重定向 URL 不匹配,这意味着模块无法猜测正确的重定向 URL。您可以设置 NUXT_OAUTH_<PROVIDER>_REDIRECT_URL
环境变量来覆盖默认值。
扩展会话
我们利用钩子让您可以使用自己的数据扩展会话数据,或在用户清除会话时进行日志记录。
// server/plugins/session.ts
export default defineNitroPlugin(() => {
// 在 SSR 期间获取会话时调用(用于 Vue 组合式函数 /api/_auth/session)
// 或当我们调用 useUserSession().fetch() 时
sessionHooks.hook('fetch', async (session, event) => {
// 通过调用数据库扩展用户会话
// 或
// 如果会话无效,则抛出 createError({ ... })
})
// 当我们调用 useServerSession().clear() 或 clearUserSession(event) 时调用
sessionHooks.hook('clear', async (session, event) => {
// 记录用户登出日志
})
})
服务器端渲染
您可以在客户端和服务器端发出经过身份验证的请求。但是,如果您不使用 useFetch()
,则必须使用 useRequestFetch()
在 SSR 期间发出经过身份验证的请求。
<script setup lang="ts">
// 使用 useAsyncData 时
const { data } = await useAsyncData('team', () => useRequestFetch()('/api/protected-endpoint'))
// useFetch 在 SSR 期间会自动使用 useRequestFetch
const { data } = await useFetch('/api/protected-endpoint')
</script>
有一个未解决的问题是在 Nuxt 中让
$fetch
包含凭证。
混合渲染
当使用 Nuxt routeRules
预渲染或缓存页面时,Nuxt Auth Utils 不会在预渲染期间获取用户会话,而是在客户端(水合后)获取。
这是因为用户会话存储在安全 cookie 中,无法在预渲染期间访问。
这意味着您不应该在预渲染期间依赖用户会话。
<AuthState>
组件
您可以使用 <AuthState>
组件在组件中安全地显示与认证相关的数据,而无需担心渲染模式。
一个常见用例是头部的登录按钮:
<template>
<header>
<AuthState v-slot="{ loggedIn, clear }">
<button v-if="loggedIn" @click="clear">退出登录</button>
<NuxtLink v-else to="/login">登录</NuxtLink>
</AuthState>
</header>
</template>
如果页面被缓存或预渲染,在客户端获取用户会话之前不会渲染任何内容。
您可以使用 placeholder
插槽在服务器端和预渲染页面获取用户会话期间显示占位符:
<template>
<header>
<AuthState>
<template #default="{ loggedIn, clear }">
<button v-if="loggedIn" @click="clear">退出登录</button>
<NuxtLink v-else to="/login">登录</NuxtLink>
</template>
<template #placeholder>
<button disabled>加载中...</button>
</template>
</AuthState>
</header>
</template>
如果您正在使用 routeRules
缓存路由,请确保使用 Nitro >= 2.9.7
以支持客户端获取用户会话。
配置
我们利用 runtimeConfig.session
为 h3 useSession
提供默认选项。
您可以在 nuxt.config.ts
中覆盖这些选项:
export default defineNuxtConfig({
modules: ['nuxt-auth-utils'],
runtimeConfig: {
session: {
maxAge: 60 * 60 * 24 * 7 // 1 周
}
}
})
我们的默认值是:
{
name: 'nuxt-session',
password: process.env.NUXT_SESSION_PASSWORD || '',
cookie: {
sameSite: 'lax'
}
}
查看 SessionConfig
了解所有选项。
更多
- nuxt-authorization:用于管理 Nuxt 应用内权限的授权模块,与
nuxt-auth-utils
兼容
开发
# 安装依赖
npm install
# 生成类型存根
npm run dev:prepare
# 使用 playground 进行开发
npm run dev
# 构建 playground
npm run dev:build
# 运行 ESLint
npm run lint
# 运行 Vitest
npm run test
npm run test:watch
# 发布新版本
npm run release