fix(auth): use internal secret for web→server calls instead of cookie forwarding
Cookie forwarding between dash.droidclaw.ai and tunnel.droidclaw.ai was unreliable. Now the web app passes userId + shared internal secret via headers. Also removes debug logging from device auth and session middleware.
This commit is contained in:
@@ -2,6 +2,7 @@ export const env = {
|
||||
DATABASE_URL: process.env.DATABASE_URL!,
|
||||
PORT: parseInt(process.env.PORT || "8080"),
|
||||
CORS_ORIGIN: process.env.CORS_ORIGIN || "http://localhost:5173",
|
||||
INTERNAL_SECRET: process.env.INTERNAL_SECRET || "",
|
||||
};
|
||||
|
||||
if (!env.DATABASE_URL) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { db } from "../db.js";
|
||||
import { session as sessionTable, user as userTable } from "../schema.js";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { getCookie } from "hono/cookie";
|
||||
import { env } from "../env.js";
|
||||
|
||||
/** Hono Env type for routes protected by sessionMiddleware */
|
||||
export type AuthEnv = {
|
||||
@@ -13,16 +14,35 @@ export type AuthEnv = {
|
||||
};
|
||||
|
||||
export async function sessionMiddleware(c: Context, next: Next) {
|
||||
// Extract session token from cookie (same approach as dashboard WS auth)
|
||||
// ── Internal server-to-server auth (web app → server) ──
|
||||
const internalSecret = c.req.header("x-internal-secret");
|
||||
const internalUserId = c.req.header("x-internal-user-id");
|
||||
|
||||
if (internalSecret && internalUserId && env.INTERNAL_SECRET && internalSecret === env.INTERNAL_SECRET) {
|
||||
const users = await db
|
||||
.select({ id: userTable.id, name: userTable.name, email: userTable.email })
|
||||
.from(userTable)
|
||||
.where(eq(userTable.id, internalUserId))
|
||||
.limit(1);
|
||||
|
||||
if (users.length === 0) {
|
||||
return c.json({ error: "unauthorized" }, 401);
|
||||
}
|
||||
|
||||
c.set("user", users[0]);
|
||||
c.set("session", { id: "internal", userId: internalUserId });
|
||||
await next();
|
||||
return;
|
||||
}
|
||||
|
||||
// ── Cookie-based auth (browser → server) ──
|
||||
const rawCookie = getCookie(c, "better-auth.session_token");
|
||||
if (!rawCookie) {
|
||||
console.log(`[SessionMiddleware] No session cookie. Headers: ${JSON.stringify(Object.fromEntries(c.req.raw.headers.entries())).slice(0, 200)}`);
|
||||
return c.json({ error: "unauthorized" }, 401);
|
||||
}
|
||||
|
||||
// Token may have a signature appended after a dot — use only the token part
|
||||
const token = rawCookie.split(".")[0];
|
||||
console.log(`[SessionMiddleware] cookie prefix: ${rawCookie.slice(0, 20)}... token prefix: ${token.slice(0, 20)}...`);
|
||||
|
||||
// Direct DB lookup
|
||||
const rows = await db
|
||||
|
||||
@@ -92,12 +92,6 @@ export async function handleDeviceMessage(
|
||||
try {
|
||||
// Hash the incoming key and look it up directly in the DB
|
||||
const hashedKey = await hashApiKey(msg.apiKey);
|
||||
console.log(`[Device Auth] key prefix: ${msg.apiKey.slice(0, 10)}... hash: ${hashedKey.slice(0, 16)}...`);
|
||||
|
||||
// Debug: list all keys in DB
|
||||
const allKeys = await db.select({ id: apikey.id, keyPrefix: apikey.start, hash: apikey.key, enabled: apikey.enabled }).from(apikey);
|
||||
console.log(`[Device Auth] DB has ${allKeys.length} keys:`, allKeys.map(k => `${k.keyPrefix ?? "?"} hash=${k.hash.slice(0, 16)}... enabled=${k.enabled}`));
|
||||
|
||||
const rows = await db
|
||||
.select({ id: apikey.id, userId: apikey.userId, enabled: apikey.enabled, expiresAt: apikey.expiresAt })
|
||||
.from(apikey)
|
||||
@@ -105,7 +99,6 @@ export async function handleDeviceMessage(
|
||||
.limit(1);
|
||||
|
||||
if (rows.length === 0 || !rows[0].enabled) {
|
||||
console.log(`[Device Auth] REJECTED: no matching key found (or disabled)`);
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "auth_error",
|
||||
|
||||
Reference in New Issue
Block a user