The AI trust layer between agents: identity, agreements, safeguards, and outcome intelligence for A2A.

Viewing files at commit 90ba0b085978Commit detailsView latest

api/index.ts 65 lines · 3.1 KB · typescript
1
import type { IncomingMessage, ServerResponse } from 'node:http';
2
import { buildApi } from '../apps/api/src/app.js';
3
import { TrustEngine } from '../packages/core/src/index.js';
4
import { HostedRepository } from '../packages/persistence/src/hosted.js';
5
import { dashboardTokenFromCookie, loadAuth0Profile, verifyAuth0Token } from './auth0.js';
6
7
const databaseUrl = process.env.DATABASE_URL;
8
const repository = databaseUrl ? new HostedRepository(databaseUrl) : undefined;
9
const app = buildApi(new TrustEngine(), undefined, repository);
10
const ready = app.ready();
11
12
export default async function handler(request: IncomingMessage, response: ServerResponse) {
13
  const incoming = new URL(request.url ?? '/', 'https://openclasp.local');
14
  const target = incoming.searchParams.get('path');
15
  if (target) request.url = target;
16
  if ((request.url ?? '').startsWith('/v0.1/')) {
17
    const authorization = request.headers.authorization;
18
    const token = authorization?.startsWith('Bearer ')
19
      ? authorization.slice(7)
20
      : dashboardTokenFromCookie(request.headers.cookie);
21
    if (!token) {
22
      response.statusCode = 401;
23
      response.setHeader('content-type', 'application/json');
24
      response.end(JSON.stringify({ error: 'authentication_required' }));
25
      return;
26
    }
27
    try {
28
      const agentSelfService =
29
        /^\/v0\.1\/runtime(?:\/bootstrap|\/heartbeat)?(?:\?|$)/.test(request.url ?? '') ||
30
        /^\/v0\.1\/feedback-requests(?:\?|$)/.test(request.url ?? '') ||
31
        /^\/v0\.1\/federated-interactions\/[^/]+\/(?:brief|session|completion-reports|feedback)(?:\?|$)/.test(
32
          request.url ?? '',
33
        );
34
      if (token.startsWith('oc_at_') && agentSelfService) {
35
        if (!repository) throw new Error('Agent access tokens are not configured');
36
        const authentication = await repository.verifyAgentAccessToken(token);
37
        // Existing beta tokens had only mcp:access. They remain accepted because
38
        // they are already cryptographically bound to the same agent.
39
        if (
40
          !authentication.scopes.includes('runtime:connect') &&
41
          !authentication.scopes.includes('mcp:access')
42
        )
43
          throw new Error('Agent token cannot connect a runtime');
44
        request.headers['x-openclasp-operator'] = authentication.operatorId;
45
        request.headers['x-openclasp-bound-agent'] = authentication.agentId;
46
        request.headers['x-openclasp-credential-type'] = 'agent_access_token';
47
      } else {
48
        const authentication = await verifyAuth0Token(token, { dashboard: true });
49
        request.headers['x-openclasp-operator'] = authentication.payload.sub!;
50
        if ((request.url ?? '').startsWith('/v0.1/account')) {
51
          const user = await loadAuth0Profile(token);
52
          request.headers['x-openclasp-email'] = encodeURIComponent(user.email ?? '');
53
          request.headers['x-openclasp-name'] = encodeURIComponent(user.name ?? '');
54
        }
55
      }
56
    } catch {
57
      response.statusCode = 401;
58
      response.setHeader('content-type', 'application/json');
59
      response.end(JSON.stringify({ error: 'invalid_session' }));
60
      return;
61
    }
62
  }
63
  await ready;
64
  app.server.emit('request', request, response);
65
}