会话管理

Better Auth 采用传统的基于 cookie 的会话管理机制。会话信息存储在 cookie 中,并在每次请求时发送到服务器。服务器随后验证会话的有效性,并在会话有效时返回用户数据。

会话表

会话表用于存储会话数据,包含以下字段:

  • id:会话令牌,同时用作会话 cookie
  • userId:对应用户的用户 ID
  • expiresAt:会话的过期时间
  • ipAddress:用户的 IP 地址
  • userAgent:用户的 User Agent,存储来自请求的 User Agent 标头

会话过期

默认情况下,会话在 7 天后过期。但当会话被使用且达到 updateAge 设定的时间时,会话的过期时间会更新为当前时间加上 expiresIn 的值。

你可以通过向 auth 配置传递 session 对象来修改 expiresInupdateAge 的值。

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

export const auth = betterAuth({
    //... 其他配置选项
    session: {
        expiresIn: 60 * 60 * 24 * 7, // 7 天
        updateAge: 60 * 60 * 24 // 1 天(每过一天更新会话过期时间)
    }
})

禁用会话刷新

你可以禁用会话刷新功能,这样无论 updateAge 选项如何设置,会话都不会被更新。

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

export const auth = betterAuth({
    //... 其他配置选项
    session: {
        disableSessionRefresh: true
    }
})

会话新鲜度

Better Auth 中的某些端点要求会话保持新鲜。如果会话的 createdAt 时间在 freshAge 限制内,则该会话被视为新鲜会话。默认情况下,freshAge 设置为 1 天(60 * 60 * 24)。

你可以通过在 auth 配置中传递 session 对象来自定义 freshAge 值:

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

export const auth = betterAuth({
    //... 其他配置选项
    session: {
        freshAge: 60 * 5 // 5 分钟(如果会话在最近 5 分钟内创建,则视为新鲜会话)
    }
})

禁用新鲜度检查,请将 freshAge 设置为 0

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

export const auth = betterAuth({
    //... 其他配置选项
    session: {
        freshAge: 0 // 禁用新鲜度检查
    }
})

会话管理

Better Auth 提供了一组用于管理会话的函数。

获取会话

getSession 函数用于检索当前活跃的会话。

import { authClient } from "@/lib/client"

const { data: session } = await authClient.getSession()

要了解如何自定义会话响应,请查看自定义会话响应部分。

使用会话

useSession 操作提供了一种响应式的方式来访问当前会话。

import { authClient } from "@/lib/client"

const { data: session } = authClient.useSession()

列出会话

listSessions 函数返回用户当前活跃的会话列表。

auth-client.ts
import { authClient } from "@/lib/client"

const sessions = await authClient.listSessions()

撤销会话

当用户从设备上登出时,会话会自动结束。但您也可以从用户已登录的任何设备上手动结束会话。

要结束会话,请使用 revokeSession 函数。只需将会话令牌作为参数传递即可。

auth-client.ts
await authClient.revokeSession({
    token: "session-token"
})

撤销其他会话

要撤销除当前会话外的所有其他会话,您可以使用 revokeOtherSessions 函数。

auth-client.ts
await authClient.revokeOtherSessions()

撤销所有会话

要撤销所有会话,您可以使用 revokeSessions 函数。

auth-client.ts
await authClient.revokeSessions()

更改密码时撤销会话

您可以在用户更改密码时撤销所有会话,方法是在 changePassword 函数中将 revokeOtherSessions 参数设置为 true。

auth.ts
await authClient.changePassword({
    newPassword: newPassword,
    currentPassword: currentPassword,
    revokeOtherSessions: true,
})

会话缓存

每次调用 useSessiongetSession 时都访问数据库并不是理想的做法,特别是在会话不频繁变更的情况下。Cookie 缓存通过将会话数据存储在一个短期有效的签名 cookie 中来解决这个问题——类似于 JWT 访问令牌与刷新令牌配合使用的方式。

启用 Cookie 缓存后,服务器可以直接从 cookie 本身验证会话有效性,而无需每次都访问数据库。Cookie 经过签名以防止篡改,较短的 maxAge 确保会话数据会定期刷新。如果会话被撤销或过期,cookie 将自动失效。

要启用 Cookie 缓存,只需在 auth 配置中设置 session.cookieCache

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

export const auth = betterAuth({
    session: {
        cookieCache: {
            enabled: true,
            maxAge: 5 * 60 // 缓存时长(单位:秒)
        }
    }
});

如果你想在获取会话时禁用从 cookie 缓存返回,可以传递 disableCookieCache:true,这将强制服务器从数据库获取会话并刷新 cookie 缓存。

auth-client.ts
const session = await authClient.getSession({ query: {
    disableCookieCache: true
}})

或者在服务器端:

server.ts
await auth.api.getSession({
    query: {
        disableCookieCache: true,
    },
    headers: req.headers, // 传递请求头
});

自定义会话响应

当您调用 getSessionuseSession 时,会话数据会以 usersession 对象的形式返回。您可以使用 customSession 插件来自定义此响应。

auth.ts
import { customSession } from "better-auth/plugins";

export const auth = betterAuth({
    plugins: [
        customSession(async ({ user, session }) => {
            const roles = findUserRoles(session.session.userId);
            return {
                roles,
                user: {
                    ...user,
                    newField: "newField",
                },
                session
            };
        }),
    ],
});

这将在会话响应中添加 rolesuser.newField 字段。

客户端类型推断

auth-client.ts
import { customSessionClient } from "better-auth/client/plugins";
import type { auth } from "@/lib/auth"; // 将 auth 实例作为类型导入

const authClient = createAuthClient({
    plugins: [customSessionClient<typeof auth>()],
});

const { data } = authClient.useSession();
const { data: sessionData } = await authClient.getSession();
// data.roles
// data.user.newField

自定义会话响应的注意事项

  1. 传递给回调函数的 session 对象不会推断由插件添加的字段。

不过,作为一种变通方案,您可以提取您的认证选项并将其传递给插件以推断字段。

import { betterAuth, BetterAuthOptions } from "better-auth";

const options = {
  //...配置选项
  plugins: [
    //...插件
  ]
} satisfies BetterAuthOptions;

export const auth = betterAuth({
    ...options,
    plugins: [
        ...(options.plugins ?? []),
        customSession(async ({ user, session }, ctx) => {
            // 现在 user 和 session 都会推断出由插件和您的自定义字段添加的字段
            return {
                user,
                session
            }
        }, options), // 在此处传递 options
    ]
})
  1. 当您的服务器和客户端代码位于不同的项目或代码库中,并且无法导入 auth 实例作为类型引用时,自定义会话字段的类型推断在客户端将无法工作。
  2. 会话缓存(包括二级存储或 Cookie 缓存)不包含自定义字段。每次获取会话时,都会调用您的自定义会话函数。

修改 list-device-sessions 端点 多会话插件 中的 /multi-session/list-device-sessions 端点用于列出用户当前登录的设备。

您可以通过向 customSession 插件传递 shouldMutateListDeviceSessionsEndpoint 选项来修改此端点的响应。

默认情况下,我们不会修改此端点的响应。

auth.ts
import { customSession } from "better-auth/plugins";

export const auth = betterAuth({
    plugins: [
        customSession(async ({ user, session }, ctx) => {
            return {
                user,
                session
            }
        }, {}, { shouldMutateListDeviceSessionsEndpoint: true }), 
    ],
});

On this page