fix(den): accept bearer sessions for desktop auth (#961)

This commit is contained in:
ben
2026-03-16 15:17:26 -07:00
committed by GitHub
parent a87b0ad751
commit c6515f2222
4 changed files with 86 additions and 17 deletions

View File

@@ -1,12 +1,11 @@
import express from "express"
import { fromNodeHeaders } from "better-auth/node"
import { asc, desc, eq, isNotNull, sql } from "drizzle-orm"
import { ensureAdminAllowlistSeeded } from "../admin-allowlist.js"
import { auth } from "../auth.js"
import { getCloudWorkerAdminBillingStatus } from "../billing/polar.js"
import { db } from "../db/index.js"
import { AdminAllowlistTable, AuthAccountTable, AuthSessionTable, AuthUserTable, WorkerTable } from "../db/schema.js"
import { asyncRoute } from "./errors.js"
import { getRequestSession } from "./session.js"
function normalizeEmail(value: string | null | undefined) {
return value?.trim().toLowerCase() ?? ""
@@ -83,9 +82,7 @@ async function mapWithConcurrency<T, R>(items: T[], limit: number, mapper: (item
}
async function requireAdminSession(req: express.Request, res: express.Response) {
const session = await auth.api.getSession({
headers: fromNodeHeaders(req.headers),
})
const session = await getRequestSession(req)
if (!session?.user?.id) {
res.status(401).json({ error: "unauthorized" })

View File

@@ -0,0 +1,78 @@
import type express from "express"
import { fromNodeHeaders } from "better-auth/node"
import { and, eq, gt } from "drizzle-orm"
import { auth } from "../auth.js"
import { db } from "../db/index.js"
import { AuthSessionTable, AuthUserTable } from "../db/schema.js"
type AuthSessionLike = Awaited<ReturnType<typeof auth.api.getSession>>
function readBearerToken(req: express.Request): string | null {
const header = typeof req.headers.authorization === "string" ? req.headers.authorization.trim() : ""
if (!header) {
return null
}
const match = header.match(/^Bearer\s+(.+)$/i)
if (!match) {
return null
}
const token = match[1]?.trim() ?? ""
return token || null
}
async function getSessionFromBearerToken(token: string): Promise<AuthSessionLike> {
const rows = await db
.select({
session: {
id: AuthSessionTable.id,
token: AuthSessionTable.token,
userId: AuthSessionTable.userId,
expiresAt: AuthSessionTable.expiresAt,
createdAt: AuthSessionTable.createdAt,
updatedAt: AuthSessionTable.updatedAt,
ipAddress: AuthSessionTable.ipAddress,
userAgent: AuthSessionTable.userAgent,
},
user: {
id: AuthUserTable.id,
name: AuthUserTable.name,
email: AuthUserTable.email,
emailVerified: AuthUserTable.emailVerified,
image: AuthUserTable.image,
createdAt: AuthUserTable.createdAt,
updatedAt: AuthUserTable.updatedAt,
},
})
.from(AuthSessionTable)
.innerJoin(AuthUserTable, eq(AuthSessionTable.userId, AuthUserTable.id))
.where(and(eq(AuthSessionTable.token, token), gt(AuthSessionTable.expiresAt, new Date())))
.limit(1)
const row = rows[0]
if (!row) {
return null
}
return {
session: row.session,
user: row.user,
}
}
export async function getRequestSession(req: express.Request): Promise<AuthSessionLike> {
const cookieSession = await auth.api.getSession({
headers: fromNodeHeaders(req.headers),
})
if (cookieSession?.user?.id) {
return cookieSession
}
const bearerToken = readBearerToken(req)
if (!bearerToken) {
return null
}
return getSessionFromBearerToken(bearerToken)
}

View File

@@ -1,14 +1,13 @@
import { randomBytes, randomUUID } from "crypto"
import express from "express"
import { fromNodeHeaders } from "better-auth/node"
import { and, asc, desc, eq, isNull } from "drizzle-orm"
import { z } from "zod"
import { auth } from "../auth.js"
import { getCloudWorkerBillingStatus, requireCloudWorkerAccess, setCloudWorkerSubscriptionCancellation } from "../billing/polar.js"
import { db } from "../db/index.js"
import { AuditEventTable, WorkerBundleTable, WorkerInstanceTable, WorkerTable, WorkerTokenTable } from "../db/schema.js"
import { env } from "../env.js"
import { asyncRoute, isTransientDbConnectionError } from "./errors.js"
import { getRequestSession } from "./session.js"
import { ensureDefaultOrg, listUserOrgs, resolveUserOrg } from "../orgs.js"
import { deprovisionWorker, provisionWorker } from "../workers/provisioner.js"
import { customDomainForWorker } from "../workers/vanity-domain.js"
@@ -243,9 +242,7 @@ async function issueWorkerOwnerToken(workerId: string): Promise<string> {
}
async function requireSession(req: express.Request, res: express.Response) {
const session = await auth.api.getSession({
headers: fromNodeHeaders(req.headers),
})
const session = await getRequestSession(req)
if (!session?.user?.id) {
res.status(401).json({ error: "unauthorized" })
return null

View File

@@ -3,11 +3,12 @@ import cors from "cors"
import express from "express"
import path from "node:path"
import { fileURLToPath } from "node:url"
import { fromNodeHeaders, toNodeHandler } from "better-auth/node"
import { toNodeHandler } from "better-auth/node"
import { auth } from "./auth.js"
import { env } from "./env.js"
import { adminRouter } from "./http/admin.js"
import { asyncRoute, errorMiddleware } from "./http/errors.js"
import { getRequestSession } from "./http/session.js"
import { workersRouter } from "./http/workers.js"
import { listUserOrgs } from "./orgs.js"
@@ -34,9 +35,7 @@ app.get("/health", (_, res) => {
})
app.get("/v1/me", asyncRoute(async (req, res) => {
const session = await auth.api.getSession({
headers: fromNodeHeaders(req.headers),
})
const session = await getRequestSession(req)
if (!session?.user?.id) {
res.status(401).json({ error: "unauthorized" })
return
@@ -45,9 +44,7 @@ app.get("/v1/me", asyncRoute(async (req, res) => {
}))
app.get("/v1/me/orgs", asyncRoute(async (req, res) => {
const session = await auth.api.getSession({
headers: fromNodeHeaders(req.headers),
})
const session = await getRequestSession(req)
if (!session?.user?.id) {
res.status(401).json({ error: "unauthorized" })
return