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/qwik

并创建一个名为 [email protected] 的新文件,其中包含示例配置。

关于使用 Node 手动部署的说明

当使用 Node.js 手动部署应用程序时,特别是使用 Express 等框架时,服务器或 Node 进程本身并不知道它是在 HTTP 下还是 HTTPS 下提供服务。与 Vercel、Netlify 或 Cloudflare 等托管服务不同,这些服务会自动管理 ORIGIN 配置,手动设置需要显式指定。为了确保您的 Node.js 应用程序识别它正在提供服务的正确协议和主机,请设置以下 **环境变量**

  • ORIGIN:将其设置为您的应用程序的 URL。例如:ORIGIN=https://your-app-name.example.com
  • PROTOCOL_HEADER:这用于指示客户端请求的原始协议。通常,这在代理配置中指定。将此变量设置为:PROTOCOL_HEADER=X-Forwarded-Proto
  • HOST_HEADER:此标头有助于识别客户端请求的原始主机。当您的 Node 环境位于代理或负载均衡器后面时,这尤其必要。将此变量设置为:HOST_HEADER=X-Forwarded-Host

Qwik API

useSession

一个 routeLoader$,它返回一个会话对象,如果不存在会话,则返回一个空对象。返回的会话对象的内容可以通过会话回调进行配置。会话数据也可以使用 session REST API 获取。

import { component$ } from '@builder.io/qwik';
import { useSession } from '~/routes/plugin@auth';
 
export default component$(() => {
  const session = useSession();
  return <p>{session.value?.user?.email}</p>;
});
 

useSignIn

一个 routeAction$,用于启动登录流程或将用户发送到列出所有可能提供商的登录页面。使用 useSignIn 登录时,CSRF 令牌在内部处理。

参数

  • providerId:一个可选的字符串参数,包含提供商的名称。当提供时,将启动对您的身份提供商的授权请求。当省略时,将重定向到内置/无品牌的登录页面。
  • options:一个可选的选项对象。
    • redirectTo:一个可选的字符串,指定用户登录后将被重定向到的 URL。默认为启动登录的页面 URL。
  • authorizationParams:一个可选的对象,包含发送到 /authorize 端点的其他参数。有关一些想法,请参阅授权请求 OIDC 规范。

注意:您也可以通过提供商设置 authorizationParams。authorizationParams 配置

使用 useSignIn<Form> 组件以及可选的 providerIdoptions.redirectTo 的示例

import { component$ } from '@builder.io/qwik';
import { Form } from '@builder.io/qwik-city';
import { useSignIn } from '~/routes/plugin@auth';
 
export default component$(() => {
  const signIn = useSignIn();
  return (
    <Form action={signIn}>
      <input type="hidden" name="providerId" value="github" />
      <input type="hidden" name="options.redirectTo" value="http://qwik-auth-example.com/dashboard" />
      <button>Sign In</button>
    </Form>
  );
});

以编程方式使用 useSignIn 的示例,以及可选的 providerIdoptions.redirectTo

import { component$ } from '@builder.io/qwik';
import { useSignIn } from '~/routes/plugin@auth';
 
export default component$(() => {
  const signIn = useSignIn();
  return (
    <button onClick$={() => signIn.submit({ providerId: 'github', options: { redirectTo: 'http://qwik-auth-example.com/dashboard' } })}>Sign In</button>
  );
});

useSignOut

一个 routeAction$,用于启动注销流程。用户会话将被失效/从 cookie/数据库中删除,具体取决于您选择存储会话的流程。

参数

  • redirectTo:一个可选的字符串,指定用户注销后将被重定向到的 URL。默认为启动登录的页面 URL。

“redirectTo”必须被重定向回调处理程序视为有效。默认情况下,它要求 URL 是同一主机名下的绝对 URL,或者您也可以提供以斜杠开头的相对 URL。如果它不匹配,它将重定向到主页。您可以定义自己的重定向回调以允许其他 URL。

使用 useSignOut<Form> 组件以及可选的 redirectTo 的示例

import { component$ } from '@builder.io/qwik';
import { Form } from '@builder.io/qwik-city';
import { useSignOut } from '~/routes/plugin@auth';
 
export default component$(() => {
  const signOut = useSignOut();
  return (
    <Form action={signOut}>
      <input type="hidden" name="redirectTo" value="/signedout" />
      <button>Sign Out</button>
    </Form>
  );
});

使用useSignOut以编程方式进行示例,并带有可选的redirectTo

import { component$ } from '@builder.io/qwik';
import { useSignOut } from '~/routes/plugin@auth';
 
export default component$(() => {
  const signOut = useSignOut();
  return <button onClick$={() => signOut.submit({ redirectTo: '/signedout' })}>Sign Out</button>;
});

REST API

Auth.js 提供的所有相同 REST API 都可用。

登录

GET /api/auth/signin

显示内置/无品牌登录页面。

POST /api/auth/signin/:provider

启动特定于提供者的登录流程。对于 OAuth 提供者,调用此端点将启动对您的身份提供者的授权请求。此端点也由 useSignIn 操作在内部使用。

回调

GET/POST /api/auth/callback/:provider

登出

GET /api/auth/signout

显示内置/无品牌登出页面。

POST /api/auth/signout

处理用户登出 - 这是一个 POST 提交,以防止恶意链接在未经用户同意的情况下触发用户登出。用户会话将失效/从 cookie/数据库中删除,具体取决于您选择存储会话的流程。此端点也由 useSignOut 方法在内部使用。

会话

GET /api/auth/session

返回对客户端安全的会话对象 - 或者如果不存在会话则返回一个空对象。返回的会话对象的内容可以通过会话回调进行配置。会话数据也可以使用 useSession 路由加载器检索。

CSRF

GET /api/auth/csrf

返回包含 CSRF 令牌的对象。在 NextAuth.js 中,所有身份验证路由都存在 CSRF 保护。它使用“双重提交 cookie 方法”,该方法使用签名的 HttpOnly、仅主机 cookie。此端点返回的 CSRF 令牌必须作为名为 csrfToken 的表单变量传递给对任何 API 端点的所有 POST 提交。

提供者

GET /api/auth/providers

返回已配置的 OAuth 服务列表以及每个服务的详细信息(例如登录和回调 URL)。它有助于动态生成自定义注册页面并检查为配置的每个 OAuth 提供者配置了哪些回调 URL。

示例

GitHub

  1. 按照 GitHub OAuth 指南 获取您的GitHub 客户端 IDGitHub 客户端密钥并使用openssl rand -base64 32密钥生成器 生成AUTH_SECRET
  2. 由于默认的[email protected] 使用 GitHub 作为示例,因此我们无需在那里更改任何内容。但是,可以使用除 GitHub 之外的提供者,或者可以添加其他提供者。Auth.js 还支持许多可以在此文件中设置的 其他选项
import { QwikAuth$ } from "@auth/qwik";
import GitHub from "@auth/qwik/providers/github";
 
export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
  () => ({
    providers: [GitHub],
  }),
);

重要:确保保留onRequest导出,因为它用于处理 oAuth 流程重定向。用户完成 oAuth 流程后,GitHub(或任何其他提供者)将把用户重定向回应用程序到/api/auth/callback/github(或/api/auth/callback/[otherProvider])。onRequest中间件函数将处理对该端点的请求并完成 oAuth 流程。

  1. 创建或编辑项目根目录下的.env.local文件以存储密钥
.env.local
GITHUB_ID=
GITHUB_SECRET=
AUTH_SECRET=

重要:请阅读 Qwik 文档关于 环境变量 的内容,以确保您安全地使用它们。许多提供者密钥应保持安全,不要暴露给客户端/浏览器。

  1. 应用程序现在已准备好使用 Auth.js 实现身份验证。
  2. 享受!

凭据

警告:Auth.js 不鼓励使用此功能。

https://next-auth.js.org/providers/credentials

  • 为基于凭据的身份验证提供的功能有意限制,以阻止使用密码,因为密码存在固有的安全风险,并且支持用户名和密码会增加额外的复杂性。
  1. 由于默认的[email protected] 使用 GitHub 作为示例,因此我们需要用凭据替换它。
import { QwikAuth$ } from "@auth/qwik";
import GitHub from "@auth/qwik/providers/github";
 
export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
  () => ({
    providers: [
      Credentials({
        async authorize(credentials, req) {
          // Add logic here to look up the user from the credentials supplied
          const user = {
            id: 1,
            name: "Mike",
            email: "[email protected]",
          };
 
          return user;
        },
      }),
    ],
  }),
);
  1. 创建或编辑项目根目录下的.env.local文件以存储密钥
.env.local
AUTH_SECRET=

重要:请阅读 Qwik 文档关于 环境变量 的内容,以确保您安全地使用它们。许多提供者密钥应保持安全,不要暴露给客户端/浏览器。

  1. 应用程序现在已准备好使用 Auth.js 实现身份验证。
  2. 享受!

路由保护

可以通过路由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?redirectTo=${event.url.pathname}`);
  }
};

注意:如果放在 layout.tsx 中,请确保重定向目标不共享相同的 layout.tsx,否则可能会发生重定向循环。

贡献者

感谢所有帮助改进此文档的贡献者!

  • the-r3aper7
  • ulic75
  • jakovljevic-mladen
  • ayoub9494
  • igorbabko
  • yaikohi
  • mrhoodz
  • VinuB-Dev
  • hugomonte
  • VarPDev