Skip to content

Commit e7220f3

Browse files
hotlongCopilot
andcommitted
fix(rest): resolve hostname → projectId in resolveExecCtx
resolveProtocol already mapped hostname → projectId for multi-tenant hosts, but resolveExecCtx didn't. Result: on objectos, every authenticated request to /api/v1/data/:object (unscoped URL) returned 401 even with a valid cookie/Bearer token because the auth service was looked up against projectId=undefined. Mirror the hostname/header resolution at the top of resolveExecCtx so per-project AuthManager.getApi().getSession({headers}) is called on the right kernel. Verified on local + CF after rebuild. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 9138536 commit e7220f3

3 files changed

Lines changed: 28 additions & 2 deletions

File tree

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Code that exists and matches the intended architecture. Do not regress these.
5656
| Studio Flow Viewer + Flow Test Runner + Flow Runs panel | [apps/studio/src/components/FlowViewer.tsx](apps/studio/src/components/FlowViewer.tsx) |
5757
| Automation: flow auto-discovery from ObjectQL registry | [packages/services/service-automation/src/plugin.ts](packages/services/service-automation/src/plugin.ts) |
5858
| **D1** ObjectOS metadata DB bridge removed - `MetadataPlugin` no longer registers `sys_metadata` / `sys_metadata_history` or auto-bridges ObjectQL to `DatabaseLoader` | [packages/metadata/src/plugin.ts](packages/metadata/src/plugin.ts) |
59-
| **S1** REST `requireAuth` gate (resolved 2026-05-17) — anonymous `/api/v1/data/*` returns 401 when `auth` tier is enabled (CRUD + batch routes both gated); auto-enabled by CLI when `tierEnabled('auth')`. Plugs the CF data-leak reported via crm.objectos.app. | [packages/rest/src/rest-server.ts](packages/rest/src/rest-server.ts), [packages/cli/src/commands/serve.ts](packages/cli/src/commands/serve.ts) |
59+
| **S1** REST `requireAuth` gate (resolved 2026-05-18) — anonymous `/api/v1/data/*` returns 401 on multi-tenant ObjectOS hosts (CRUD + batch routes both gated). Auto-enabled by CLI when `tierEnabled('auth')`; force-enabled on `createObjectOSStack` since project artifacts own auth per-tenant. Verified live on crm.objectos.app / app.objectos.app — anonymous reads + writes now 401 (were leaking before). | [packages/rest/src/rest-server.ts](packages/rest/src/rest-server.ts), [packages/runtime/src/cloud/objectos-stack.ts](packages/runtime/src/cloud/objectos-stack.ts), [packages/cli/src/commands/serve.ts](packages/cli/src/commands/serve.ts) |
6060

6161
---
6262

apps/objectos/wrangler.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ compatibility_flags = ["nodejs_compat"]
4444
# rebuild + re-push to ship a new version, then run `wrangler deploy`.
4545
[[containers]]
4646
class_name = "ObjectOSContainer"
47-
image = "registry.cloudflare.com/2846eb40a60f4738e292b90dcd8cce10/objectos:7dcde27c"
47+
image = "registry.cloudflare.com/2846eb40a60f4738e292b90dcd8cce10/objectos:9138536d"
4848
max_instances = 5
4949
instance_type = "standard-1"
5050

packages/rest/src/rest-server.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,32 @@ export class RestServer {
366366
*/
367367
private async resolveExecCtx(projectId: string | undefined, req: any): Promise<any | undefined> {
368368
try {
369+
// For multi-tenant hosts (objectos), incoming requests on unscoped
370+
// URLs like `/api/v1/data/:object` arrive with `projectId === undefined`.
371+
// The route's protocol resolver already maps hostname → projectId
372+
// (see resolveProtocol). We mirror that here so getSession() can
373+
// find the right per-project auth service. Without this, the
374+
// hostname-routed requests fall through to defaultProjectIdProvider/
375+
// authServiceProvider (neither of which is wired in objectos) and
376+
// every authenticated user sees 401.
377+
if (!projectId && req && this.envRegistry && this.kernelManager) {
378+
const host = this.extractHostname(req);
379+
if (host) {
380+
try {
381+
const result = await this.envRegistry.resolveByHostname(host);
382+
if (result?.projectId) projectId = result.projectId;
383+
} catch { /* fall through */ }
384+
}
385+
if (!projectId && typeof this.envRegistry.resolveById === 'function') {
386+
const headerVal = this.extractProjectIdHeader(req);
387+
if (headerVal) {
388+
try {
389+
const driver = await this.envRegistry.resolveById(headerVal);
390+
if (driver) projectId = headerVal;
391+
} catch { /* fall through */ }
392+
}
393+
}
394+
}
369395
// Look up the auth service in the right kernel. For unscoped
370396
// single-project apps the kernelManager will hand us the lone
371397
// tenant kernel; for multi-project hosts we use the resolved

0 commit comments

Comments
 (0)