Auth.js
Auth.js 是一个广泛应用于各种 JS 框架的身份验证库。使用 Auth.js,我们可以减少复杂性。我们还可以访问一系列身份验证提供者,例如 GitHub、Google、Facebook 等。此外,它可以集成到多个框架中,包括 Qwik。
Auth.js 提供了几个功能,可以增强简单性、生产力、灵活性和提供者多样性。以下是 Auth.js 的主要功能:
- 提供者:Auth.js 支持多个提供者,简化了应用程序中的身份验证过程(例如 Github、Google、Facebook、Twitter)。它还提供了单点登录(SSO)服务以及传统身份验证。
- 管理:Auth.js 在我们专注于业务逻辑方面提供了很大的帮助。它自动管理令牌的存储和刷新。
- 配置:配置 Auth.js 很简单。它提供了简单的安装、错误处理、自定义登录和注册表单以及与提供者的无缝集成。
- 集成:Auth.js 与 JS 框架无缝集成,其全面的文档提供了清晰的指南。
- 安全性:虽然 Auth.js 对开发人员友好,但我们必须认识到底层的复杂性,以确保我们的数据具有高级别的安全性。
请注意,Auth.js 库仍处于 1.0 版本之前的阶段,可能存在错误。
安装
您可以使用以下 Qwik 起始脚本轻松添加 Auth.js:
npm run qwik add auth
此命令将添加以下新包:
@auth/core
@builder.io/qwik-auth
并创建一个名为 plugin@auth.ts
的新文件,其中包含一个示例配置。
需要手动操作
在使用 npm run qwik add auth
安装 auth 包之后,需要将 @auth/core
包添加到 vite.config.js
文件的依赖优化设置中:
export default defineConfig(() => {
return {
plugins: [qwikCity(), qwikVite(), tsconfigPaths()],
dev: {
headers: {
'Cache-Control': 'public, max-age=0',
},
},
preview: {
headers: {
'Cache-Control': 'public, max-age=600',
},
},
optimizeDeps: {
include: [ "@auth/core" ]
}
};
});
Qwik API
useAuthSession
一个 routeLoader$,如果存在会返回一个会话对象,否则返回一个空对象。返回的会话对象的内容可以通过会话回调进行配置。还可以使用 session REST API 来检索会话数据。
import { component$ } from '@builder.io/qwik';
import { useAuthSession } from '~/routes/plugin@auth';
export default component$(() => {
const session = useAuthSession();
return <p>{session.value?.user?.email}</p>;
});
useAuthSignin
一个 routeAction$,用于启动登录流程或将用户发送到登录页面,列出所有可能的提供者。在使用 useAuthSignin
进行登录时,CSRF 令牌会在内部处理。
参数
providerId
:可选的字符串参数,提供者的名称。如果提供,将启动到身份提供者的授权请求。如果省略,将重定向到内置/无品牌的登录页面。options
:可选的选项对象。callbackUrl
:可选的字符串,指定用户在登录后将被重定向到的 URL。默认为发起登录的页面 URL。
authorizationParams
:可选的附加参数对象,发送到 /authorize 端点。有关一些想法,请参阅授权请求 OIDC 规范。
注意:您还可以通过 provider.authorizationParams 配置设置
authorizationParams
使用 <Form> 组件和可选的 providerId
和 options.callbackUrl
来示例使用 useAuthSignin
:
import { component$ } from '@builder.io/qwik';
import { Form } from '@builder.io/qwik-city';
import { useAuthSignin } from '~/routes/plugin@auth';
export default component$(() => {
const signIn = useAuthSignin();
return (
<Form action={signIn}>
<input type="hidden" name="providerId" value="github" />
<input type="hidden" name="options.callbackUrl" value="http://qwik-auth-example.com/dashboard" />
<button>登录</button>
</Form>
);
});
使用可选的 providerId
和 options.callbackUrl
来示例使用 useAuthSignin
:
import { component$ } from '@builder.io/qwik';
import { useAuthSignin } from '~/routes/plugin@auth';
export default component$(() => {
const signIn = useAuthSignin();
return (
<button onClick$={() => signIn.submit({ providerId: 'github', options: { callbackUrl: 'http://qwik-auth-example.com/dashboard' } })}>登录</button>
);
});
useAuthSignout
一个 routeAction$,用于启动登出流程。用户会话将被作废/从 cookie/数据库中删除,具体取决于您选择存储会话的流程。
参数
callbackUrl
:可选的字符串,指定用户在登出后将被重定向到的 URL。默认为发起登录的页面 URL。
'callbackUrl' 必须被重定向回调处理程序视为有效。默认情况下,它要求 URL 是同一主机名下的绝对 URL,或者您还可以提供以斜杠开头的相对 URL。如果不匹配,将重定向到主页。您可以定义自己的重定向回调以允许其他 URL。
使用 <Form> 组件和可选的 callbackUrl
来示例使用 useAuthSignout
:
import { component$ } from '@builder.io/qwik';
import { Form } from '@builder.io/qwik-city';
import { useAuthSignout } from '~/routes/plugin@auth';
export default component$(() => {
const signOut = useAuthSignout();
return (
<Form action={signOut}>
<input type="hidden" name="callbackUrl" value="/signedout" />
<button>登出</button>
</Form>
);
});
使用可选的 callbackUrl
来示例使用 useAuthSignout
:
import { component$ } from '@builder.io/qwik';
import { useAuthSignout } from '~/routes/plugin@auth';
export default component$(() => {
const signOut = useAuthSignout();
return <button onClick$={() => signOut.submit({ callbackUrl: '/signedout' })}>登出</button>;
});
REST API
提供了与 Auth.js 提供的相同的 REST API。
signin
GET /api/auth/signin
显示内置/无品牌的登录页面。
POST /api/auth/signin/:provider
启动特定于提供者的登录流程。对于 OAuth 提供者,调用此端点将启动到您的身份提供者的授权请求。此端点也由 useAuthSignin 方法在内部使用。
callback
GET/POST /api/auth/callback/:provider
signout
GET /api/auth/signout
显示内置/无品牌的登出页面。
POST /api/auth/signout
处理用户登出 - 这是一个 POST 提交,以防止恶意链接触发未经用户同意的用户登出。用户会话将被作废/从 cookie/数据库中删除,具体取决于您选择存储会话的流程。此端点也由 useAuthSignout 方法在内部使用。
session
GET /api/auth/session
返回客户端安全的会话对象 - 如果没有会话,则返回一个空对象。返回的会话对象的内容可以通过会话回调进行配置。还可以使用 useAuthSession routerLoader 来检索会话数据。
csrf
GET /api/auth/csrf
返回包含 CSRF 令牌的对象。在 NextAuth.js 中,所有身份验证路由都具有 CSRF 保护。它使用“双重提交 cookie 方法”,该方法使用了一个带有签名的 HttpOnly、仅限主机的 cookie。此端点返回的 CSRF 令牌必须作为名为 csrfToken 的表单变量传递给任何 API 端点的所有 POST 提交。
providers
GET /api/auth/providers
返回配置的 OAuth 服务和每个服务的详细信息(例如登录和回调 URL)的列表。它对于动态生成自定义注册页面以及检查为每个配置的 OAuth 提供者配置了哪些回调 URL 非常有用。
示例
GitHub
- 按照 GitHub OAuth 指南 获取您的
GitHub 客户端 ID
、GitHub 客户端密钥
,并使用openssl rand -base64 32
或 Secret Generator 生成AUTH_SECRET
。 - 由于默认的
plugin@auth.ts
使用 GitHub 作为示例,我们不需要在那里进行任何更改。但是可以使用除 GitHub 之外的其他提供者,或者添加其他提供者。Auth.js 还支持许多其他选项,可以在此文件中设置。
import { serverAuth$ } from '@builder.io/qwik-auth';
import GitHub from '@auth/core/providers/github';
import type { Provider } from '@auth/core/providers';
export const { onRequest, useAuthSession, useAuthSignin, useAuthSignout } = serverAuth$(
({ env }) => ({
secret: env.get("AUTH_SECRET"),
trustHost: true,
providers: [
GitHub({
clientId: env.get("GITHUB_ID"),
clientSecret: env.get("GITHUB_SECRET"),
}),
] as Provider[],
})
);
重要提示:请确保保持
onRequest
导出,因为它用于处理 oAuth 流重定向。用户完成 oAuth 流后,GitHub(或任何其他提供者)将重定向用户返回到应用程序的/api/auth/callback/github
(或/api/auth/callback/[otherProvider]
)。onRequest
中间件函数将处理对此端点的请求并完成 oAuth 流。
- 创建或编辑项目根目录下的
.env.local
文件以存储密钥
GITHUB_ID=
GITHUB_SECRET=
AUTH_SECRET=
重要提示:请阅读 Qwik 文档中关于环境变量的部分,以确保您安全地使用它们。许多提供者密钥应保持安全,不要暴露给客户端/浏览器。
- 应用程序现在已准备好使用 Auth.js 实现身份验证。
- 尽情享受吧!
凭据
警告:Auth.js 不鼓励使用此功能。
https://next-auth.js.org/providers/credentials
- 凭据身份验证提供的功能有意限制,以防止使用密码,因为密码本身存在固有的安全风险,并且支持用户名和密码会增加额外的复杂性。
- 由于默认的
plugin@auth.ts
使用 GitHub 作为示例,我们需要将其替换为凭据。
import { serverAuth$ } from '@builder.io/qwik-auth';
import Credentials from "@auth/core/providers/credentials";
import type { Provider } from '@auth/core/providers';
export const { onRequest, useAuthSession, useAuthSignin, useAuthSignout } = serverAuth$(
({ env }) => ({
secret: env.get("AUTH_SECRET"),
trustHost: true,
providers: [
Credentials({
async authorize(credentials, req) {
// 在此处添加逻辑以查找提供的凭据对应的用户
const user = {
id: 1,
name: "Mike",
email: "mike@example.com",
};
return user;
},
}),
] as Provider[],
})
);
- 创建或编辑项目根目录下的
.env.local
文件以存储密钥
AUTH_SECRET=
重要提示:请阅读 Qwik 文档中关于环境变量的部分,以确保您安全地使用它们。许多提供者密钥应保持安全,不要暴露给客户端/浏览器。
- 应用程序现在已准备好使用 Auth.js 实现身份验证。
- 尽情享受吧!
路由保护
会话数据可以通过路由 event.sharedMap
访问。因此,可以通过在 layout.tsx
或页面 index.tsx
中放置以下代码来保护和重定向路由:
export const onRequest: RequestHandler = (event) => {
const session: Session | null = event.sharedMap.get('session');
if (!session || new Date(session.expires) < new Date()) {
throw event.redirect(302, `/api/auth/signin?callbackUrl=${event.url.pathname}`);
}
};
注意:如果放置在 layout.tsx 中,请确保重定向目标不与相同的 layout.tsx 共享,否则可能会发生重定向循环。