Getting Started

Client Setup

Start and follow orders from the browser.

Add the client plugin to your Better Auth client. The helpers wrap the server endpoints with typed methods and return { data, error } responses.

Quick Start

1. Register the client plugin

auth-client.ts
import { createAuthClient } from "better-auth/client"
import { crossDeviceClient } from "@onmax/better-auth-cross-device/client"

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

2. Start an order

The initiating device starts the order and shows the claimUrl to the approving device. Display it as a QR code, deep link, or copyable URL.

start-login.ts
const order = await authClient.crossDevice.start({
  kind: "login",
  returnTo: "/dashboard",
  displayTitle: "Sign in to Console",
  displaySummary: "Approve this sign-in on your trusted device.",
})

if (order.error) throw new Error(order.error.message)

showQrCode(order.data.claimUrl)

3. Subscribe to status

Subscribe with the eventToken from the start response. When the order is approved, finalize it with the desktopToken.

follow-order.ts
const source = authClient.crossDevice.subscribe({
  orderId: order.data.orderId,
  eventToken: order.data.eventToken,
  onEvent: async (event) => {
    if (event === "approved") {
      await authClient.crossDevice.finalize({
        orderId: order.data.orderId,
        desktopToken: order.data.desktopToken,
      })
    }
  },
})
Close the EventSource when the page unmounts or the order reaches a terminal state.

4. Build the approving-device route

The approving device claims the order, fetches the challenge, and submits the adapter proof.

claim-page.ts
await authClient.crossDevice.claim({ orderId, claimToken })

const challenge = await authClient.crossDevice.challenge({
  orderId,
  challengeToken,
})

await authClient.crossDevice.approve({
  orderId,
  challengeToken,
  proof: await createProof(challenge.data),
})

Order kinds

KindFinalize result
loginApplies the resolved Better Auth session and may return token.
signReturns the approved proofArtifact.
transactionReturns the approved proofArtifact.

Error handling

Every helper returns a typed response:

client.ts
const result = await authClient.crossDevice.finalize({ orderId, desktopToken })

if (result.error) {
  showError(result.error.message || "Approval failed")
  return
}

navigateTo(result.data.redirectTo || "/")

Next Steps

Copyright © 2026