上次登录方式

最后登录方式插件会跟踪用户最近使用的认证方法(电子邮件、OAuth 提供商等)。这使你能够在登录页面上显示有用的提示,例如“上次使用 Google 登录”,或根据用户偏好优先显示某些登录方式。

【The last login method plugin tracks the most recent authentication method used by users (email, OAuth providers, etc.). This enables you to display helpful indicators on login pages, such as "Last signed in with Google" or prioritize certain login methods based on user preferences.】

安装

【Installation】

将插件添加到你的认证配置

auth.ts
import { betterAuth } from "better-auth"
import { lastLoginMethod } from "better-auth/plugins"

export const auth = betterAuth({
    // ... other config options
    plugins: [
        lastLoginMethod() 
    ]
})

Add the client plugin to your auth client

auth-client.ts
import { createAuthClient } from "better-auth/client"
import { lastLoginMethodClient } from "better-auth/client/plugins"

export const authClient = createAuthClient({
    plugins: [
        lastLoginMethodClient() 
    ]
})

用法

【Usage】

安装后,该插件会自动跟踪用户最后使用的身份验证方式。然后,你可以在应用中检索并显示此信息。

【Once installed, the plugin automatically tracks the last authentication method used by users. You can then retrieve and display this information in your application.】

获取最后使用的方法

【Getting the Last Used Method】

客户端插件提供了几种方法来处理上次登录方式:

【The client plugin provides several methods to work with the last login method:】

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

// Get the last used login method
const lastMethod = authClient.getLastUsedLoginMethod()
console.log(lastMethod) // "google", "email", "github", etc.

// Check if a specific method was last used
const wasGoogle = authClient.isLastUsedLoginMethod("google")

// Clear the stored method
authClient.clearLastUsedLoginMethod()

用户界面集成示例

【UI Integration Example】

以下是使用该插件增强登录页面的方法:

【Here's how to use the plugin to enhance your login page:】

sign-in.tsx
import { authClient } from "@/lib/auth-client"
import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge"

export function SignInPage() {
    const lastMethod = authClient.getLastUsedLoginMethod()
    
    return (
        <div className="space-y-4">
            <h1>Sign In</h1>
            
            {/* Email sign in */}
            <div className="relative">
                <Button 
                    onClick={() => authClient.signIn.email({...})}
                    variant={lastMethod === "email" ? "default" : "outline"}
                    className="w-full"
                >
                    Sign in with Email
                    {lastMethod === "email" && (
                        <Badge className="ml-2">Last used</Badge>
                    )}
                </Button>
            </div>
            
            {/* OAuth providers */}
            <div className="relative">
                <Button 
                    onClick={() => authClient.signIn.social({ provider: "google" })}
                    variant={lastMethod === "google" ? "default" : "outline"}
                    className="w-full"
                >
                    Continue with Google
                    {lastMethod === "google" && (
                        <Badge className="ml-2">Last used</Badge>
                    )}
                </Button>
            </div>
            
            <div className="relative">
                <Button 
                    onClick={() => authClient.signIn.social({ provider: "github" })}
                    variant={lastMethod === "github" ? "default" : "outline"}
                    className="w-full"
                >
                    Continue with GitHub
                    {lastMethod === "github" && (
                        <Badge className="ml-2">Last used</Badge>
                    )}
                </Button>
            </div>
        </div>
    )
}

数据库持久化

【Database Persistence】

默认情况下,最后的登录方式仅保存在 cookies 中。若需更持久的追踪和分析,你可以启用数据库存储。

【By default, the last login method is stored only in cookies. For more persistent tracking and analytics, you can enable database storage.】

启用数据库存储

在插件配置中将 storeInDatabase 设置为 true

auth.ts
import { betterAuth } from "better-auth"
import { lastLoginMethod } from "better-auth/plugins"

export const auth = betterAuth({
    plugins: [
        lastLoginMethod({
            storeInDatabase: true
        })
    ]
})

Run database migration

该插件会自动向你的用户表添加一个 lastLoginMethod 字段。运行迁移以应用更改:

npx @better-auth/cli migrate
npx @better-auth/cli generate

Access database field

当启用数据库存储时,lastLoginMethod 字段将在用户对象中可用:

user-profile.tsx
import { auth } from "@/lib/auth"

// Server-side access
const session = await auth.api.getSession({ headers })
console.log(session?.user.lastLoginMethod) // "google", "email", etc.

// Client-side access via session
const { data: session } = authClient.useSession()
console.log(session?.user.lastLoginMethod)

数据库架构

【Database Schema】

当启用 storeInDatabase 时,插件会向 user 表中添加以下字段:

【When storeInDatabase is enabled, the plugin adds the following field to the user table:】

表:user

【Table: user

Field NameTypeKeyDescription
lastLoginMethodstringThe last authentication method used by the user

自定义模式配置

【Custom Schema Configuration】

你可以自定义数据库字段名称:

【You can customize the database field name:】

auth.ts
import { betterAuth } from "better-auth"
import { lastLoginMethod } from "better-auth/plugins"

export const auth = betterAuth({
    plugins: [
        lastLoginMethod({
            storeInDatabase: true,
            schema: {
                user: {
                    lastLoginMethod: "last_auth_method" // Custom field name
                }
            }
        })
    ]
})

配置选项

【Configuration Options】

最后一次登录方式插件接受以下选项:

【The last login method plugin accepts the following options:】

服务器选项

【Server Options】

auth.ts
import { betterAuth } from "better-auth"
import { lastLoginMethod } from "better-auth/plugins"

export const auth = betterAuth({
    plugins: [
        lastLoginMethod({
            // Cookie configuration
            cookieName: "better-auth.last_used_login_method", // Default: "better-auth.last_used_login_method"
            maxAge: 60 * 60 * 24 * 30, // Default: 30 days in seconds
            
            // Database persistence
            storeInDatabase: false, // Default: false
            
            // Custom method resolution
            customResolveMethod: (ctx) => {
                // Custom logic to determine the login method
                if (ctx.path === "/oauth/callback/custom-provider") {
                    return "custom-provider"
                }
                // Return null to use default resolution
                return null
            },
            
            // Schema customization (when storeInDatabase is true)
            schema: {
                user: {
                    lastLoginMethod: "custom_field_name"
                }
            }
        })
    ]
})

cookieNamestring

  • 用于存储最后登录方式的 Cookie 名称
  • 默认值:"better-auth.last_used_login_method"
  • 注意:此 Cookie 的 httpOnlyfalse,以允许客户端 JavaScript 访问,用于界面功能

maxAge: number

  • Cookie 的过期时间(秒)
  • 默认值:2592000(30天)

storeInDatabase: boolean

  • 是否将最后一次登录方式存储在数据库中
  • 默认值: false
  • 启用时,会在用户表中添加一个 lastLoginMethod 字段

customResolveMethod: (ctx: GenericEndpointContext) => string | null

  • 自定义函数,用于根据请求上下文确定登录方式
  • 返回 null 以使用默认解析逻辑
  • 对自定义 OAuth 提供商或身份验证流程很有用

模式object

  • 在启用 storeInDatabase 时自定义数据库字段名称
  • 允许将 lastLoginMethod 字段映射到自定义列名

客户选项

【Client Options】

auth-client.ts
import { createAuthClient } from "better-auth/client"
import { lastLoginMethodClient } from "better-auth/client/plugins"

export const authClient = createAuthClient({
    plugins: [
        lastLoginMethodClient({
            cookieName: "better-auth.last_used_login_method" // Default: "better-auth.last_used_login_method"
        })
    ]
})

cookieName: string

  • 用于读取上一次登录方式的 cookie 名称
  • 必须与服务器端的 cookieName 配置匹配
  • 默认值: "better-auth.last_used_login_method"

默认方法解析

【Default Method Resolution】

默认情况下,该插件会跟踪以下身份验证方法:

【By default, the plugin tracks these authentication methods:】

  • 电子邮件验证: "email"
  • OAuth 提供商:提供商 ID(例如,"google""github""discord"
  • OAuth2 回调:来自 URL 路径的提供者 ID
  • 注册方式:与登录方式的跟踪相同

该插件会自动从以下端点检测方法:

  • /callback/:id - 带有提供商 ID 的 OAuth 回调
  • /oauth2/callback/:id - 带有提供商 ID 的 OAuth2 回调
  • /sign-in/email - 邮箱登录
  • /sign-up/email - 邮箱注册

【The plugin automatically detects the method from these endpoints:

  • /callback/:id - OAuth callback with provider ID
  • /oauth2/callback/:id - OAuth2 callback with provider ID
  • /sign-in/email - Email sign in
  • /sign-up/email - Email sign up】

跨字段支持

【Cross-Domain Support】

该插件会自动继承来自 Better Auth 集中式 Cookie 系统的 Cookie 设置。这解决了上次登录方式无法在以下情况中保持的问题:

【The plugin automatically inherits cookie settings from Better Auth's centralized cookie system. This solves the problem where the last login method wouldn't persist across:】

  • 跨子域设置auth.example.comapp.example.com
  • 跨域设置api.company.comapp.different.com

当你在 Better Auth 配置中启用 crossSubDomainCookiescrossOriginCookies 时,插件将自动使用与会话 Cookie 相同的域、Secure 和 SameSite 设置,确保在应用中行为一致。

【When you enable crossSubDomainCookies or crossOriginCookies in your Better Auth config, the plugin will automatically use the same domain, secure, and sameSite settings as your session cookies, ensuring consistent behavior across your application.】

高级示例

【Advanced Examples】

自定义提供者跟踪

【Custom Provider Tracking】

如果你有自定义的 OAuth 提供商或身份验证方法,你可以使用 customResolveMethod 选项:

【If you have custom OAuth providers or authentication methods, you can use the customResolveMethod option:】

auth.ts
import { betterAuth } from "better-auth"
import { lastLoginMethod } from "better-auth/plugins"

export const auth = betterAuth({
    plugins: [
        lastLoginMethod({
            customResolveMethod: (ctx) => {
                // Track custom SAML provider
                if (ctx.path === "/saml/callback") {
                    return "saml"
                }
                
                // Track magic link authentication
                if (ctx.path === "/magic-link/verify") {
                    return "magic-link"
                }
                
                // Track phone authentication
                if (ctx.path === "/sign-in/phone") {
                    return "phone"
                }
                
                // Return null to use default logic
                return null
            }
        })
    ]
})

在 Expo 中的使用

【Usage with Expo】

在使用 Expo 的 Better Auth 时,请确保从 @better-auth/expo/plugins 导入客户端插件,而不是从 better-auth/plugins/client 导入。这可以确保上次登录方式能够使用配置的存储正确保存。

【When using Better Auth with Expo, make sure to import the client plugin from @better-auth/expo/plugins rather than from better-auth/plugins/client. This ensures the last login method is stored correctly using the configured storage.】

import { createAuthClient } from "better-auth/react"
import { expoClient } from "@better-auth/expo"
import { lastLoginMethodClient } from "@better-auth/expo/plugins"
import * as SecureStore from "expo-secure-store"

export const authClient = createAuthClient({
  plugins: [
    expoClient({
      scheme: "myapp",
      storagePrefix: "myapp",
      storage: SecureStore,
    }),
    lastLoginMethodClient({
      storagePrefix: "myapp",
      storage: SecureStorage,
    })
  ]
})

在仅使用 Expo 的应用中,如果不需要浏览器支持,可以省略服务器插件,只依赖客户端插件。