}
async function signOut() {
- await fetch(`${BASE}/api/auth/sign-out`, { method: "POST", credentials: "include" });
- window.location.reload();
+ const res = await fetch(`${BASE}/api/auth/sign-out`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: "{}" });
+ if (res.ok) window.location.reload();
}
async function callProtected(mode) {
diff --git a/playgrounds/better-auth-hono/src/auth.ts b/playgrounds/better-auth-hono/src/auth.ts
index 6abade93..28a6e564 100644
--- a/playgrounds/better-auth-hono/src/auth.ts
+++ b/playgrounds/better-auth-hono/src/auth.ts
@@ -1,17 +1,36 @@
import { betterAuth } from "better-auth";
-import { bearer } from "better-auth/plugins";
+import { bearer, deviceAuthorization } from "better-auth/plugins";
import Database from "better-sqlite3";
import path from "path";
import { fileURLToPath } from "url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
+// Device-flow client identifier. This is the app's own ID for the device
+// authorization grant — it is NOT the Google OAuth client ID.
+export const DEVICE_CLIENT_ID = "writing-tools-word-poc";
+
export const auth = betterAuth({
// Run `npx @better-auth/cli migrate` after adding or changing plugins.
database: new Database(path.join(__dirname, "../db/auth.db")),
baseURL: process.env.BETTER_AUTH_URL ?? "http://localhost:3001",
secret: process.env.BETTER_AUTH_SECRET,
- plugins: [bearer()],
+ plugins: [
+ bearer(),
+ deviceAuthorization({
+ // Browser-facing approval page the user is sent to.
+ verificationUri: "/device.html",
+ // Device codes expire after 10 minutes if never approved.
+ expiresIn: "10m",
+ // Task pane must not poll faster than every 5 seconds.
+ interval: "5s",
+ // Only issue device codes for this app's known client ID.
+ validateClient: (clientId) => clientId === DEVICE_CLIENT_ID,
+ // Required by this plugin version's runtime options schema even though
+ // the TS type marks it optional. Empty = use the default deviceCode table.
+ schema: {},
+ }),
+ ],
socialProviders: {
google: {
clientId: process.env.GOOGLE_CLIENT_ID ?? "",
diff --git a/playgrounds/better-auth-hono/src/index.ts b/playgrounds/better-auth-hono/src/index.ts
index 4166f2db..c5cc420c 100644
--- a/playgrounds/better-auth-hono/src/index.ts
+++ b/playgrounds/better-auth-hono/src/index.ts
@@ -10,11 +10,14 @@ import chat from "./routes/chat.js";
const app = new Hono();
const PORT = 3001;
-// CORS — allow the static test page and future frontend origins.
+// CORS — allow the backend's own static pages (3001) AND the separate
+// task-pane simulator origin (3002). The 3002 origin reproduces the Word
+// task-pane / browser split: it has NO Better Auth cookie and must rely
+// entirely on the bearer token returned by the device flow.
app.use(
"*",
cors({
- origin: [`http://localhost:${PORT}`],
+ origin: ["http://localhost:3001", "http://localhost:3002"],
allowHeaders: ["Content-Type", "Authorization"],
allowMethods: ["GET", "POST", "OPTIONS"],
credentials: true,
diff --git a/playgrounds/better-auth-hono/src/taskpane-server.ts b/playgrounds/better-auth-hono/src/taskpane-server.ts
new file mode 100644
index 00000000..20392cba
--- /dev/null
+++ b/playgrounds/better-auth-hono/src/taskpane-server.ts
@@ -0,0 +1,17 @@
+import { Hono } from "hono";
+import { serve } from "@hono/node-server";
+import { serveStatic } from "@hono/node-server/serve-static";
+
+// Standalone static server for the task-pane simulator.
+// Runs on a DIFFERENT origin (3002) than the backend (3001) so that
+// cookies set during browser login never reach this origin. The simulator
+// must succeed using only the bearer token from the device flow.
+const app = new Hono();
+const PORT = 3002;
+
+app.use("/*", serveStatic({ root: "./taskpane" }));
+
+serve({ fetch: app.fetch, port: PORT }, () => {
+ console.log(`Task-pane simulator at http://localhost:${PORT}`);
+ console.log(`(backend expected at http://localhost:3001)`);
+});
diff --git a/playgrounds/better-auth-hono/taskpane/index.html b/playgrounds/better-auth-hono/taskpane/index.html
new file mode 100644
index 00000000..1d42d664
--- /dev/null
+++ b/playgrounds/better-auth-hono/taskpane/index.html
@@ -0,0 +1,177 @@
+
+
+
+
+
+ Task-Pane Simulator (Device Flow)
+
+
+
+
Task-Pane Simulator
+
+ Origin http://localhost:3002 — no Better Auth cookie here.
+ Backend is http://localhost:3001. Success depends only on the
+ bearer token from the device flow.
+