会话管理

Better Auth使用传统的基于cookie的会话管理。会话存储在cookie中,并在每个请求时发送到服务器。服务器然后验证会话,如果会话有效则返回用户数据。

会话表

会话表存储会话数据。会话表包含以下字段:

  • id:会话令牌,也用作会话 cookie。
  • userId:用户的 ID。
  • expiresAt:会话的过期日期。
  • ipAddress:用户的 IP 地址。
  • userAgent:用户的用户代理。它存储来自请求的用户代理标头。

会话过期

默认情况下,会话在 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 天(每 1 天更新会话过期时间)
    }
})

禁用会话刷新

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

auth.ts
import { betterAuth } from "better-auth"
 
export const auth = betterAuth({
    //... 其他配置选项
    session: {
        disableSessionRefresh: true
    }
})

会话新鲜度

Better Auth 中的一些端点要求会话是新鲜的。如果会话的 createdAtfreshAge 限制内,则认为会话是新鲜的。默认情况下,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()

更改密码时撤销会话

你可以在用户更改密码时通过将 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
const auth = new BetterAuth({
    session: {
        cookieCache: {
            enabled: true,
            maxAge: 5 * 60 // 缓存持续时间(秒)
        }
    }
});

如果你想在获取会话时禁用从 cookie 缓存返回,可以传递 disableCookieCache:true

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

或在服务器端

server.ts
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 } = await authClient.useSession();
const { data: sessionData } = await authClient.getSession();
// data.roles
// data.user.newField

一些注意事项

  • 传递给回调的 session 对象不会推断插件添加的字段。

但是,作为一种解决方法,你可以提取你的 auth 选项并将其传递给插件以推断字段。

import { betterAuth, BetterAuthOptions } from "better-auth";
 
const options = {
  //...配置选项
  plugins: [
    //...插件
  ]
} satisfies BetterAuthOptions;
 
export const auth = betterAuth({
    ...options,
    plugins: [
        ...(options.plugins ?? []),
        customSession(async ({ user, session }) => {
            // 现在 user 和 session 都会推断插件添加的字段和你的自定义字段
            return {
                user,
                session
            }
        }, options), // 在这里传递选项
    ]
})
  • 如果你不能使用 auth 实例作为类型,客户端上的推断将不起作用。
  • 会话缓存,包括二级存储或 cookie 缓存,不包含自定义字段。每次获取会话时,都会调用你的自定义会话函数。

On this page