Cookies(Cookie)

了解 Better Auth 中 Cookies 的使用。

Cookies 用于存储诸如会话令牌、会话数据、OAuth 状态等信息。所有 Cookies 都使用认证选项中提供的 secret 密钥或环境变量 BETTER_AUTH_SECRET 进行签名。如果您使用 版本化密钥 来进行密钥轮换,加密的 Cookie 数据(例如 JWE 会话缓存)将自动使用当前密钥,并且仍然可以用之前的密钥解密。

默认情况下,Better Auth 的 Cookies 格式为 ${prefix}.${cookie_name}。默认前缀是 "better-auth"。您可以通过在认证选项的 advanced 对象中设置 cookiePrefix 来更改前缀。

auth.ts
import { betterAuth } from "better-auth"

export const auth = betterAuth({
    advanced: {
        cookiePrefix: "my-app"
    }
})

自定义 Cookies

在服务器运行于生产模式时,所有 Cookies 都是 httpOnlysecure 的。

如果您想设置自定义的 Cookie 名称和属性,可以通过在认证选项的 advanced 对象中设置 cookieOptions 来实现。

默认情况下,Better Auth 使用以下 Cookies:

  • session_token 用于存储会话令牌
  • session_data 用于在启用 Cookie 缓存时存储会话数据
  • dont_remember 用于存储当 rememberMe 被禁用时的标志

插件也可能使用 Cookies 来存储数据。例如,双因素认证插件使用 two_factor Cookie 来存储双因素认证状态。

auth.ts
import { betterAuth } from "better-auth"

export const auth = betterAuth({
    advanced: {
        cookies: {
            session_token: {
                name: "custom_session_token",
                attributes: {
                    // 设置自定义的 Cookie 属性
                }
            },
        }
    }
})

跨子域 Cookies

有时您可能需要在子域间共享 Cookies。 例如,如果您在 auth.example.com 进行身份验证,您可能也想在 app.example.com 访问相同的会话。

domain 属性控制哪些域可以访问该 Cookie。将其设置为根域(例如 example.com)将使 Cookie 在所有子域间可访问。为了安全,请遵循以下指南:

  1. 仅当必要时启用跨子域 Cookies
  2. 将域设置为所需的最具体范围(例如,使用 app.example.com 而非 .example.com
  3. 警惕可能访问这些 Cookies 的不受信任的子域
  4. 考虑为不受信任的服务使用不同的域(例如 status.company.comapp.company.com
auth.ts
import { betterAuth } from "better-auth"

export const auth = betterAuth({
    advanced: {
        crossSubDomainCookies: {
            enabled: true,
            domain: "app.example.com", // 您的域名
        },
    },
    trustedOrigins: [
        'https://example.com',
        'https://app1.example.com',
        'https://app2.example.com',
    ],
})

安全 Cookies

默认情况下,只有在服务器运行于生产环境时,Cookies 才是安全的。您可以通过在认证选项的 advanced 对象中设置 useSecureCookiestrue,强制 Cookies 始终使用安全模式。

auth.ts
import { betterAuth } from "better-auth"

export const auth = betterAuth({
    advanced: {
        useSecureCookies: true
    }
})

Safari、ITP 与跨域设置

Safari 内建了一个隐私功能,称为智能追踪防护(Intelligent Tracking Prevention,简称 ITP),会阻止第三方 Cookies。

如果您的 Better Auth API 托管在与前端不同的域名下,Safari 可能完全阻止身份验证 Cookies。

以下示例在 Safari 中会失败:

前端: https://app.domainB.com
API:   https://domainA.com

如果您的前端发起类似以下请求:

fetch("https://domainA.com/api/auth/get-session", {
  credentials: "include"
})

Safari 会将 domainA.com 视为第三方,阻止其 Cookies。这可能导致会话无法持久化, Set-Cookie 头被忽略,用户在登录后依然显示未登录状态,或是在 Chrome 中工作正常但 Safari 中失败。

为解决此问题,有两种方案:

  1. 使用反向代理将请求代理到 API。
  2. 使用共享的父域名。

使用反向代理

您可以通过与前端相同域名的反向代理来访问 API,而不是直接调用它。

例如,不调用:

https://domainA.com/api/auth/*

而调用:

https://app.domainA.com/api/auth/*

然后配置您的托管服务,将请求代理到实际的 API 服务器。

这样请求在 Safari 中被视作为同源请求,从而允许 Cookies 正常工作。

Netlify 示例

netlify.toml
[[redirects]]
  from = "/api/*"
  to = "https://domainA.com/api/:splat"
  status = 200
  force = true

Vercel 示例

vercel.json
{
  "rewrites": [
    {
      "source": "/api/:path*",
      "destination": "https://domainA.com/api/:path*"
    }
  ]
}

使用共享父域名

您也可以使用共享的父域名来允许 Cookies 跨子域共享:

https://app.example.com
https://api.example.com

然后启用跨子域 Cookies 并设置域为:

auth.ts
import { betterAuth } from "better-auth"

export const auth = betterAuth({
    advanced: {
        crossSubDomainCookies: {
            enabled: true,
            domain: "example.com",
        },
    },
})

更多关于跨子域 Cookies 的内容,请参见 上述文档

这样可以避免 Safari 将 API 识别为第三方。