Skip to content

Commit c6515f2

Browse files
fix(den): accept bearer sessions for desktop auth (#961)
1 parent a87b0ad commit c6515f2

4 files changed

Lines changed: 86 additions & 17 deletions

File tree

services/den/src/http/admin.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import express from "express"
2-
import { fromNodeHeaders } from "better-auth/node"
32
import { asc, desc, eq, isNotNull, sql } from "drizzle-orm"
43
import { ensureAdminAllowlistSeeded } from "../admin-allowlist.js"
5-
import { auth } from "../auth.js"
64
import { getCloudWorkerAdminBillingStatus } from "../billing/polar.js"
75
import { db } from "../db/index.js"
86
import { AdminAllowlistTable, AuthAccountTable, AuthSessionTable, AuthUserTable, WorkerTable } from "../db/schema.js"
97
import { asyncRoute } from "./errors.js"
8+
import { getRequestSession } from "./session.js"
109

1110
function normalizeEmail(value: string | null | undefined) {
1211
return value?.trim().toLowerCase() ?? ""
@@ -83,9 +82,7 @@ async function mapWithConcurrency<T, R>(items: T[], limit: number, mapper: (item
8382
}
8483

8584
async function requireAdminSession(req: express.Request, res: express.Response) {
86-
const session = await auth.api.getSession({
87-
headers: fromNodeHeaders(req.headers),
88-
})
85+
const session = await getRequestSession(req)
8986

9087
if (!session?.user?.id) {
9188
res.status(401).json({ error: "unauthorized" })

services/den/src/http/session.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import type express from "express"
2+
import { fromNodeHeaders } from "better-auth/node"
3+
import { and, eq, gt } from "drizzle-orm"
4+
import { auth } from "../auth.js"
5+
import { db } from "../db/index.js"
6+
import { AuthSessionTable, AuthUserTable } from "../db/schema.js"
7+
8+
type AuthSessionLike = Awaited<ReturnType<typeof auth.api.getSession>>
9+
10+
function readBearerToken(req: express.Request): string | null {
11+
const header = typeof req.headers.authorization === "string" ? req.headers.authorization.trim() : ""
12+
if (!header) {
13+
return null
14+
}
15+
16+
const match = header.match(/^Bearer\s+(.+)$/i)
17+
if (!match) {
18+
return null
19+
}
20+
21+
const token = match[1]?.trim() ?? ""
22+
return token || null
23+
}
24+
25+
async function getSessionFromBearerToken(token: string): Promise<AuthSessionLike> {
26+
const rows = await db
27+
.select({
28+
session: {
29+
id: AuthSessionTable.id,
30+
token: AuthSessionTable.token,
31+
userId: AuthSessionTable.userId,
32+
expiresAt: AuthSessionTable.expiresAt,
33+
createdAt: AuthSessionTable.createdAt,
34+
updatedAt: AuthSessionTable.updatedAt,
35+
ipAddress: AuthSessionTable.ipAddress,
36+
userAgent: AuthSessionTable.userAgent,
37+
},
38+
user: {
39+
id: AuthUserTable.id,
40+
name: AuthUserTable.name,
41+
email: AuthUserTable.email,
42+
emailVerified: AuthUserTable.emailVerified,
43+
image: AuthUserTable.image,
44+
createdAt: AuthUserTable.createdAt,
45+
updatedAt: AuthUserTable.updatedAt,
46+
},
47+
})
48+
.from(AuthSessionTable)
49+
.innerJoin(AuthUserTable, eq(AuthSessionTable.userId, AuthUserTable.id))
50+
.where(and(eq(AuthSessionTable.token, token), gt(AuthSessionTable.expiresAt, new Date())))
51+
.limit(1)
52+
53+
const row = rows[0]
54+
if (!row) {
55+
return null
56+
}
57+
58+
return {
59+
session: row.session,
60+
user: row.user,
61+
}
62+
}
63+
64+
export async function getRequestSession(req: express.Request): Promise<AuthSessionLike> {
65+
const cookieSession = await auth.api.getSession({
66+
headers: fromNodeHeaders(req.headers),
67+
})
68+
if (cookieSession?.user?.id) {
69+
return cookieSession
70+
}
71+
72+
const bearerToken = readBearerToken(req)
73+
if (!bearerToken) {
74+
return null
75+
}
76+
77+
return getSessionFromBearerToken(bearerToken)
78+
}

services/den/src/http/workers.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import { randomBytes, randomUUID } from "crypto"
22
import express from "express"
3-
import { fromNodeHeaders } from "better-auth/node"
43
import { and, asc, desc, eq, isNull } from "drizzle-orm"
54
import { z } from "zod"
6-
import { auth } from "../auth.js"
75
import { getCloudWorkerBillingStatus, requireCloudWorkerAccess, setCloudWorkerSubscriptionCancellation } from "../billing/polar.js"
86
import { db } from "../db/index.js"
97
import { AuditEventTable, WorkerBundleTable, WorkerInstanceTable, WorkerTable, WorkerTokenTable } from "../db/schema.js"
108
import { env } from "../env.js"
119
import { asyncRoute, isTransientDbConnectionError } from "./errors.js"
10+
import { getRequestSession } from "./session.js"
1211
import { ensureDefaultOrg, listUserOrgs, resolveUserOrg } from "../orgs.js"
1312
import { deprovisionWorker, provisionWorker } from "../workers/provisioner.js"
1413
import { customDomainForWorker } from "../workers/vanity-domain.js"
@@ -243,9 +242,7 @@ async function issueWorkerOwnerToken(workerId: string): Promise<string> {
243242
}
244243

245244
async function requireSession(req: express.Request, res: express.Response) {
246-
const session = await auth.api.getSession({
247-
headers: fromNodeHeaders(req.headers),
248-
})
245+
const session = await getRequestSession(req)
249246
if (!session?.user?.id) {
250247
res.status(401).json({ error: "unauthorized" })
251248
return null

services/den/src/index.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import cors from "cors"
33
import express from "express"
44
import path from "node:path"
55
import { fileURLToPath } from "node:url"
6-
import { fromNodeHeaders, toNodeHandler } from "better-auth/node"
6+
import { toNodeHandler } from "better-auth/node"
77
import { auth } from "./auth.js"
88
import { env } from "./env.js"
99
import { adminRouter } from "./http/admin.js"
1010
import { asyncRoute, errorMiddleware } from "./http/errors.js"
11+
import { getRequestSession } from "./http/session.js"
1112
import { workersRouter } from "./http/workers.js"
1213
import { listUserOrgs } from "./orgs.js"
1314

@@ -34,9 +35,7 @@ app.get("/health", (_, res) => {
3435
})
3536

3637
app.get("/v1/me", asyncRoute(async (req, res) => {
37-
const session = await auth.api.getSession({
38-
headers: fromNodeHeaders(req.headers),
39-
})
38+
const session = await getRequestSession(req)
4039
if (!session?.user?.id) {
4140
res.status(401).json({ error: "unauthorized" })
4241
return
@@ -45,9 +44,7 @@ app.get("/v1/me", asyncRoute(async (req, res) => {
4544
}))
4645

4746
app.get("/v1/me/orgs", asyncRoute(async (req, res) => {
48-
const session = await auth.api.getSession({
49-
headers: fromNodeHeaders(req.headers),
50-
})
47+
const session = await getRequestSession(req)
5148
if (!session?.user?.id) {
5249
res.status(401).json({ error: "unauthorized" })
5350
return

0 commit comments

Comments
 (0)