Skip to content

Commit 88b0fae

Browse files
author
Proactive Runtime Bot
committed
feat(relayauth): self-sufficient workspace-path mint
1 parent 2fd956c commit 88b0fae

2 files changed

Lines changed: 71 additions & 36 deletions

File tree

packages/server/src/__tests__/tokens-route.test.ts

Lines changed: 65 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -673,21 +673,38 @@ test("POST /v1/tokens/path", async (t) => {
673673
});
674674

675675
test("POST /v1/tokens/workspace-path", async (t) => {
676-
async function seedWorkspace(
677-
app: ReturnType<typeof createTestApp>,
678-
workspaceId = "ws_tokens_route",
679-
orgId = "org_tokens_route",
680-
): Promise<void> {
681-
await seedWorkspaceContext(app, {
682-
id: workspaceId,
683-
workspaceId,
684-
orgId,
685-
scopes: [],
686-
roles: [],
676+
await t.test("requires workspaceId", async () => {
677+
const { app, authHeaders } = await createHarness({
678+
authClaims: {
679+
scopes: [
680+
"relayauth:api-key:manage:*",
681+
"relayfile:fs:read:*",
682+
"relayfile:fs:write:*",
683+
],
684+
},
687685
});
688-
}
686+
const orgApiKey = await issueApiKey(app, authHeaders, [
687+
"relayauth:api-key:manage:*",
688+
"relayfile:fs:read:*",
689+
"relayfile:fs:write:*",
690+
]);
689691

690-
await t.test("mints a short-lived relay_pa directly from an org api key", async () => {
692+
const response = await requestRoute(app, "POST", "/v1/tokens/workspace-path", {
693+
body: {
694+
paths: ["/github/repos/AgentWorkforce/cloud/issues/123/**"],
695+
scopes: ["relayfile:fs:write:/github/repos/AgentWorkforce/cloud/issues/123/**"],
696+
},
697+
headers: {
698+
"x-api-key": orgApiKey.key,
699+
},
700+
});
701+
702+
await assertJsonResponse<ErrorBody>(response, 400, (body) => {
703+
assert.equal(body.code, "workspaceId_required");
704+
});
705+
});
706+
707+
await t.test("mints a short-lived relay_pa directly from an org api key without a seeded workspace row", async () => {
691708
const { app, authHeaders } = await createHarness({
692709
authClaims: {
693710
scopes: [
@@ -697,7 +714,6 @@ test("POST /v1/tokens/workspace-path", async (t) => {
697714
],
698715
},
699716
});
700-
await seedWorkspace(app);
701717
const orgApiKey = await issueApiKey(app, authHeaders, [
702718
"relayauth:api-key:manage:*",
703719
"relayfile:fs:read:*",
@@ -706,7 +722,7 @@ test("POST /v1/tokens/workspace-path", async (t) => {
706722

707723
const response = await requestRoute(app, "POST", "/v1/tokens/workspace-path", {
708724
body: {
709-
workspaceId: "ws_tokens_route",
725+
workspaceId: " ws_tokens_route ",
710726
agentName: "cloud-team-member",
711727
paths: ["/github/repos/AgentWorkforce/cloud/issues/123/**"],
712728
scopes: ["relayfile:fs:write:/github/repos/AgentWorkforce/cloud/issues/123/**"],
@@ -732,6 +748,7 @@ test("POST /v1/tokens/workspace-path", async (t) => {
732748
const accessClaims = decodeJwtJsonSegment<RelayAuthTokenClaims>(body.accessToken, 1);
733749
assert.equal(accessClaims.sub, "agent_cloud-team-member");
734750
assert.equal(accessClaims.wks, "ws_tokens_route");
751+
assert.equal(accessClaims.org, "org_tokens_route");
735752
assert.equal(accessClaims.meta?.tokenClass, "path");
736753
assert.equal(accessClaims.meta?.workspaceTokenId, undefined);
737754
assert.equal(accessClaims.parentTokenId, undefined);
@@ -743,52 +760,71 @@ test("POST /v1/tokens/workspace-path", async (t) => {
743760
assert.ok(accessClaims.exp - accessClaims.iat <= 120, "direct path access TTL should honor short ttlSeconds");
744761
});
745762

746-
await t.test("caps direct path token TTL at the agent-token maximum", async () => {
763+
await t.test("stamps the authenticated org even when the workspaceId is associated with another org", async () => {
747764
const { app, authHeaders } = await createHarness({
748765
authClaims: {
766+
org: "org_a",
749767
scopes: [
750768
"relayauth:api-key:manage:*",
751769
"relayfile:fs:read:*",
752770
"relayfile:fs:write:*",
753771
],
754772
},
755773
});
756-
await seedWorkspace(app);
774+
await seedWorkspaceContext(app, {
775+
id: "ws_owned_by_org_b",
776+
workspaceId: "ws_owned_by_org_b",
777+
orgId: "org_b",
778+
scopes: [],
779+
roles: [],
780+
});
781+
const orgApiKey = await issueApiKey(app, authHeaders, [
782+
"relayauth:api-key:manage:*",
783+
"relayfile:fs:read:*",
784+
"relayfile:fs:write:*",
785+
]);
757786

758787
const response = await requestRoute(app, "POST", "/v1/tokens/workspace-path", {
759788
body: {
760-
workspaceId: "ws_tokens_route",
789+
workspaceId: "ws_owned_by_org_b",
761790
paths: ["/github/repos/AgentWorkforce/cloud/issues/123/*"],
762-
ttlSeconds: 7200,
791+
scopes: ["relayfile:fs:write:/github/repos/AgentWorkforce/cloud/issues/123/*"],
792+
},
793+
headers: {
794+
"x-api-key": orgApiKey.key,
763795
},
764-
headers: authHeaders,
765796
});
766797

767798
const body = await assertJsonResponse<WorkspacePathTokenPair>(response, 201);
768799
const accessClaims = decodeJwtJsonSegment<RelayAuthTokenClaims>(body.accessToken, 1);
769-
assert.ok(accessClaims.exp - accessClaims.iat <= 3600, "direct path access TTL should cap at 1h");
800+
assert.equal(accessClaims.org, "org_a");
801+
assert.equal(accessClaims.wks, "ws_owned_by_org_b");
802+
assert.equal(body.workspaceId, "ws_owned_by_org_b");
770803
});
771804

772-
await t.test("rejects a workspace outside the caller org", async () => {
805+
await t.test("caps direct path token TTL at the agent-token maximum", async () => {
773806
const { app, authHeaders } = await createHarness({
774807
authClaims: {
775-
scopes: ["relayauth:api-key:manage:*", "relayfile:fs:write:*"],
808+
scopes: [
809+
"relayauth:api-key:manage:*",
810+
"relayfile:fs:read:*",
811+
"relayfile:fs:write:*",
812+
],
776813
},
777814
});
778-
await seedWorkspace(app, "ws_other_org", "org_other");
779815

780816
const response = await requestRoute(app, "POST", "/v1/tokens/workspace-path", {
781817
body: {
782-
workspaceId: "ws_other_org",
818+
workspaceId: "ws_tokens_route",
783819
paths: ["/github/repos/AgentWorkforce/cloud/issues/123/*"],
784-
scopes: ["relayfile:fs:write:/github/repos/AgentWorkforce/cloud/issues/123/*"],
820+
ttlSeconds: 7200,
785821
},
786822
headers: authHeaders,
787823
});
788824

789-
await assertJsonResponse<ErrorBody>(response, 404, (body) => {
790-
assert.equal(body.code, "workspace_not_found");
791-
});
825+
const body = await assertJsonResponse<WorkspacePathTokenPair>(response, 201);
826+
const accessClaims = decodeJwtJsonSegment<RelayAuthTokenClaims>(body.accessToken, 1);
827+
assert.ok(accessClaims.exp - accessClaims.iat <= 3600, "direct path access TTL should cap at 1h");
792828
});
793829

794830
await t.test("rejects requested scopes outside the org api-key grant", async () => {
@@ -797,7 +833,6 @@ test("POST /v1/tokens/workspace-path", async (t) => {
797833
scopes: ["relayauth:api-key:manage:*", "relayfile:fs:read:*"],
798834
},
799835
});
800-
await seedWorkspace(app);
801836

802837
const response = await requestRoute(app, "POST", "/v1/tokens/workspace-path", {
803838
body: {
@@ -819,7 +854,6 @@ test("POST /v1/tokens/workspace-path", async (t) => {
819854
scopes: ["relayauth:api-key:manage:*", "relayfile:fs:write:*"],
820855
},
821856
});
822-
await seedWorkspace(app);
823857

824858
const degenerate = await requestRoute(app, "POST", "/v1/tokens/workspace-path", {
825859
body: {

packages/server/src/routes/tokens.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -442,10 +442,11 @@ tokens.post("/workspace-path", async (c) => {
442442
}
443443

444444
const storage = getSqlStorage(c.get("storage"));
445-
const workspace = await storage.contexts.getWorkspace(workspaceId);
446-
if (!workspace || workspace.orgId !== auth.claims.org) {
447-
return c.json({ error: "workspace_not_found", code: "workspace_not_found" }, 404);
448-
}
445+
// Direct workspace-path minting is intentionally equivalent to:
446+
// POST /v1/tokens/workspace (org API key + caller-supplied workspaceId)
447+
// followed by /v1/tokens/path.
448+
// The org API key grant is the authorization boundary here; the workspace
449+
// row is not an auth source and must not be required on this hot path.
449450

450451
const paths = normalizePathTokenPaths(body.paths);
451452
if (!paths.ok) {
@@ -469,7 +470,7 @@ tokens.post("/workspace-path", async (c) => {
469470
agentId,
470471
agentName,
471472
orgId: auth.claims.org,
472-
workspaceId: workspace.workspaceId,
473+
workspaceId,
473474
sponsorId: auth.claims.sponsorId,
474475
sponsorChain: auth.claims.sponsorChain,
475476
scopes: auth.claims.scopes,

0 commit comments

Comments
 (0)