会话管理
Better Auth 采用传统的基于 cookie 的会话管理机制。会话信息存储在 cookie 中,并在每次请求时发送到服务器。服务器随后验证会话的有效性,并在会话有效时返回用户数据。
会话表
会话表用于存储会话数据,包含以下字段:
id
:会话令牌,同时用作会话 cookieuserId
:对应用户的用户 IDexpiresAt
:会话的过期时间ipAddress
:用户的 IP 地址userAgent
:用户的 User Agent,存储来自请求的 User Agent 标头
会话过期
默认情况下,会话在 7 天后过期。但当会话被使用且达到 updateAge
设定的时间时,会话的过期时间会更新为当前时间加上 expiresIn
的值。
你可以通过向 auth
配置传递 session
对象来修改 expiresIn
和 updateAge
的值。
import { betterAuth } from "better-auth"
export const auth = betterAuth({
//... 其他配置选项
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 天
updateAge: 60 * 60 * 24 // 1 天(每过一天更新会话过期时间)
}
})
禁用会话刷新
你可以禁用会话刷新功能,这样无论 updateAge
选项如何设置,会话都不会被更新。
import { betterAuth } from "better-auth"
export const auth = betterAuth({
//... 其他配置选项
session: {
disableSessionRefresh: true
}
})
会话新鲜度
Better Auth 中的某些端点要求会话保持新鲜。如果会话的 createdAt
时间在 freshAge
限制内,则该会话被视为新鲜会话。默认情况下,freshAge
设置为 1 天(60 * 60 * 24)。
你可以通过在 auth
配置中传递 session
对象来自定义 freshAge
值:
import { betterAuth } from "better-auth"
export const auth = betterAuth({
//... 其他配置选项
session: {
freshAge: 60 * 5 // 5 分钟(如果会话在最近 5 分钟内创建,则视为新鲜会话)
}
})
要禁用新鲜度检查,请将 freshAge
设置为 0
:
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
函数返回用户当前活跃的会话列表。
import { authClient } from "@/lib/client"
const sessions = await authClient.listSessions()
撤销会话
当用户从设备上登出时,会话会自动结束。但您也可以从用户已登录的任何设备上手动结束会话。
要结束会话,请使用 revokeSession
函数。只需将会话令牌作为参数传递即可。
await authClient.revokeSession({
token: "session-token"
})
撤销其他会话
要撤销除当前会话外的所有其他会话,您可以使用 revokeOtherSessions
函数。
await authClient.revokeOtherSessions()
撤销所有会话
要撤销所有会话,您可以使用 revokeSessions
函数。
await authClient.revokeSessions()
更改密码时撤销会话
您可以在用户更改密码时撤销所有会话,方法是在 changePassword
函数中将 revokeOtherSessions
参数设置为 true。
await authClient.changePassword({
newPassword: newPassword,
currentPassword: currentPassword,
revokeOtherSessions: true,
})
会话缓存
Cookie 缓存
每次调用 useSession
或 getSession
时都访问数据库并不是理想的做法,特别是在会话不频繁变更的情况下。Cookie 缓存通过将会话数据存储在一个短期有效的签名 cookie 中来解决这个问题——类似于 JWT 访问令牌与刷新令牌配合使用的方式。
启用 Cookie 缓存后,服务器可以直接从 cookie 本身验证会话有效性,而无需每次都访问数据库。Cookie 经过签名以防止篡改,较短的 maxAge
确保会话数据会定期刷新。如果会话被撤销或过期,cookie 将自动失效。
要启用 Cookie 缓存,只需在 auth 配置中设置 session.cookieCache
:
import { betterAuth } from "better-auth"
export const auth = betterAuth({
session: {
cookieCache: {
enabled: true,
maxAge: 5 * 60 // 缓存时长(单位:秒)
}
}
});
如果你想在获取会话时禁用从 cookie 缓存返回,可以传递 disableCookieCache:true
,这将强制服务器从数据库获取会话并刷新 cookie 缓存。
const session = await authClient.getSession({ query: {
disableCookieCache: true
}})
或者在服务器端:
await auth.api.getSession({
query: {
disableCookieCache: true,
},
headers: req.headers, // 传递请求头
});
自定义会话响应
当您调用 getSession
或 useSession
时,会话数据会以 user
和 session
对象的形式返回。您可以使用 customSession
插件来自定义此响应。
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
};
}),
],
});
这将在会话响应中添加 roles
和 user.newField
字段。
客户端类型推断
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
自定义会话响应的注意事项
- 传递给回调函数的
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
]
})
- 当您的服务器和客户端代码位于不同的项目或代码库中,并且无法导入
auth
实例作为类型引用时,自定义会话字段的类型推断在客户端将无法工作。 - 会话缓存(包括二级存储或 Cookie 缓存)不包含自定义字段。每次获取会话时,都会调用您的自定义会话函数。
修改 list-device-sessions 端点
多会话插件 中的 /multi-session/list-device-sessions
端点用于列出用户当前登录的设备。
您可以通过向 customSession
插件传递 shouldMutateListDeviceSessionsEndpoint
选项来修改此端点的响应。
默认情况下,我们不会修改此端点的响应。
import { customSession } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
customSession(async ({ user, session }, ctx) => {
return {
user,
session
}
}, {}, { shouldMutateListDeviceSessionsEndpoint: true }),
],
});