会话管理

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

会话表

会话表存储会话数据。会话表具有以下字段:

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

会话过期

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

您可以通过将 session 对象传递给 auth 配置来更改 expiresInupdateAge 值。

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

export const auth = betterAuth({
    //... other config options
    session: {
        expiresIn: 60 * 60 * 24 * 7, // 7 days
        updateAge: 60 * 60 * 24 // 1 day (every 1 day the session expiration is updated)
    }
})

禁用会话刷新

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

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

export const auth = betterAuth({
    //... other config options
    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({
    //... other config options
    session: {
        freshAge: 60 * 5 // 5 minutes (the session is fresh if created within the last 5 minutes)
    }
})

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

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

export const auth = betterAuth({
    //... other config options
    session: {
        freshAge: 0 // Disable freshness check
    }
})

会话管理

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 // Cache duration in seconds
        }
    }
});

如果您想在获取会话时禁用从 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, // pass the 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"; // Import the auth instance as a type

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

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

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

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

但是,作为一种变通方法,您可以拉取您的 auth 选项并将其传递给插件以推断字段。

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

const options = {
  //...config options
  plugins: [
    //...plugins 
  ]
} satisfies BetterAuthOptions;

export const auth = betterAuth({
    ...options,
    plugins: [
        ...(options.plugins ?? []),
        customSession(async ({ user, session }, ctx) => {
            // now both user and session will infer the fields added by plugins and your custom fields
            return {
                user,
                session
            }
        }, options), // pass options here
    ]
})
  1. 当您的服务器和客户端代码位于单独的项目或仓库中,并且您无法将 auth 实例作为类型引用导入时,客户端自定义会话字段的类型推断将不起作用。
  2. 会话缓存,包括二级存储或 Cookie 缓存,不包括自定义字段。每次获取会话时,都会调用您的自定义会话函数。

变异 list-device-sessions 端点 来自 multi-session 插件的 /multi-session/list-device-sessions 端点用于列出用户登录的设备。

您可以通过将 shouldMutateListDeviceSessionsEndpoint 选项传递给 customSession 插件来变异此端点的响应。

默认情况下,我们不变异此端点的响应。

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