@@ -0,0 +1,122 @@
|
||||
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
// Real signed assertions through the template's request adapter, across separate requests.
|
||||
import { generateKeyPairSync, randomUUID, sign } from "node:crypto"
|
||||
import { createServer } from "node:http"
|
||||
import type { AddressInfo } from "node:net"
|
||||
import { buildRegistry, Container } from "@conusai-kit/core"
|
||||
import { afterEach, describe, expect, it, vi } from "vitest"
|
||||
|
||||
const mocks = vi.hoisted(() => ({ runtime: vi.fn() }))
|
||||
vi.mock("../apps/web/src/server/app", () => ({ webRuntime: mocks.runtime }))
|
||||
vi.mock("@conusai-kit/tenancy", async (original) => ({
|
||||
...(await original<Record<string, unknown>>()),
|
||||
createTenancyHooks: () => ({}),
|
||||
}))
|
||||
|
||||
const { supportOboCaller } = await import("../apps/web/src/server/support-obo")
|
||||
const issuer = "https://platform.example.test"
|
||||
const audience = "https://app.example.test"
|
||||
const keys = generateKeyPairSync("ed25519")
|
||||
const jwk = { ...keys.publicKey.export({ format: "jwk" }), kid: "synthetic", alg: "EdDSA", use: "sig" }
|
||||
const registry = buildRegistry([])
|
||||
const httpFetch = globalThis.fetch
|
||||
|
||||
function configure() {
|
||||
mocks.runtime.mockReturnValue({
|
||||
data: {},
|
||||
registry,
|
||||
env: {
|
||||
deployment: {
|
||||
CONUSAI_API_URL: `${issuer}/api`,
|
||||
CONUSAI_PUBLIC_URL: audience,
|
||||
CONUSAI_ORG_ID: "3",
|
||||
CONUSAI_PROJECT_ID: "4",
|
||||
CONUSAI_ENVIRONMENT_ID: "5",
|
||||
},
|
||||
},
|
||||
})
|
||||
vi.stubGlobal(
|
||||
"fetch",
|
||||
vi.fn(async () => Response.json({ keys: [jwk] })),
|
||||
)
|
||||
}
|
||||
|
||||
function assertion(extra: Record<string, unknown> = {}) {
|
||||
const now = Math.floor(Date.now() / 1000)
|
||||
const encode = (value: object) => Buffer.from(JSON.stringify(value)).toString("base64url")
|
||||
const body = `${encode({ alg: "EdDSA", typ: "conusai-obo+jwt", kid: "synthetic" })}.${encode({
|
||||
iss: issuer,
|
||||
aud: audience,
|
||||
sub: "synthetic-user",
|
||||
sub_ns: "app",
|
||||
app_tenant: "tenant-a",
|
||||
conv: "synthetic-conversation",
|
||||
channel: "sc_synthetic",
|
||||
org: "3",
|
||||
project: "4",
|
||||
env: "5",
|
||||
jti: randomUUID(),
|
||||
iat: now,
|
||||
exp: now + 60,
|
||||
...extra,
|
||||
})}`
|
||||
return `${body}.${sign(null, Buffer.from(body), keys.privateKey).toString("base64url")}`
|
||||
}
|
||||
|
||||
function request(token?: string, pathname = "/api/mcp") {
|
||||
return new Request(`${audience}${pathname}`, {
|
||||
headers: token ? { "Conusai-On-Behalf-Of": token } : {},
|
||||
})
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
describe("the Support OBO request adapter", () => {
|
||||
it("accepts a signed assertion once and refuses its replay on a separate request", async () => {
|
||||
configure()
|
||||
const token = assertion()
|
||||
expect(await supportOboCaller(request(token), new Container())).not.toBeNull()
|
||||
await expect(supportOboCaller(request(token), new Container())).rejects.toMatchObject({ status: 401 })
|
||||
})
|
||||
|
||||
it("refuses another environment and permits a new assertion", async () => {
|
||||
configure()
|
||||
await expect(supportOboCaller(request(assertion({ env: "6" })), new Container())).rejects.toMatchObject({
|
||||
status: 401,
|
||||
})
|
||||
expect(await supportOboCaller(request(assertion()), new Container())).not.toBeNull()
|
||||
})
|
||||
|
||||
it("keeps the replay boundary across real HTTP requests", async () => {
|
||||
configure()
|
||||
const server = createServer(async (incoming, response) => {
|
||||
try {
|
||||
const token = incoming.headers["conusai-on-behalf-of"]
|
||||
const caller = await supportOboCaller(request(typeof token === "string" ? token : undefined), new Container())
|
||||
response.writeHead(caller ? 200 : 204).end()
|
||||
} catch (error) {
|
||||
response.writeHead((error as { status?: number }).status ?? 500).end()
|
||||
}
|
||||
})
|
||||
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve))
|
||||
const origin = `http://127.0.0.1:${(server.address() as AddressInfo).port}`
|
||||
const token = assertion()
|
||||
try {
|
||||
const headers = { "Conusai-On-Behalf-Of": token }
|
||||
expect((await httpFetch(`${origin}/api/mcp`, { headers })).status).toBe(200)
|
||||
expect((await httpFetch(`${origin}/api/mcp`, { headers })).status).toBe(401)
|
||||
expect((await httpFetch(`${origin}/api/mcp`)).status).toBe(204)
|
||||
} finally {
|
||||
await new Promise<void>((resolve, reject) => server.close((error) => (error ? reject(error) : resolve())))
|
||||
}
|
||||
})
|
||||
|
||||
it("leaves ordinary requests and non-MCP routes on session authentication", async () => {
|
||||
expect(await supportOboCaller(request(), new Container())).toBeNull()
|
||||
expect(await supportOboCaller(request("ignored", "/api/ready"), new Container())).toBeNull()
|
||||
expect(mocks.runtime).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user