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

Viewing files at commit a9e4757bb766Commit detailsView latest

api/index.ts 68 lines · 3.3 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\/[^/]+\/contract-proposals(?:\/[^/]+\/respond)?(?:\?|$)/.test(
32
          request.url ?? '',
33
        ) ||
34
        /^\/v0\.1\/federated-interactions\/[^/]+\/(?:brief|session|completion-reports|feedback)(?:\?|$)/.test(
35
          request.url ?? '',
36
        );
37
      if (token.startsWith('oc_at_') && agentSelfService) {
38
        if (!repository) throw new Error('Agent access tokens are not configured');
39
        const authentication = await repository.verifyAgentAccessToken(token);
40
        // Existing beta tokens had only mcp:access. They remain accepted because
41
        // they are already cryptographically bound to the same agent.
42
        if (
43
          !authentication.scopes.includes('runtime:connect') &&
44
          !authentication.scopes.includes('mcp:access')
45
        )
46
          throw new Error('Agent token cannot connect a runtime');
47
        request.headers['x-openclasp-operator'] = authentication.operatorId;
48
        request.headers['x-openclasp-bound-agent'] = authentication.agentId;
49
        request.headers['x-openclasp-credential-type'] = 'agent_access_token';
50
      } else {
51
        const authentication = await verifyAuth0Token(token, { dashboard: true });
52
        request.headers['x-openclasp-operator'] = authentication.payload.sub!;
53
        if ((request.url ?? '').startsWith('/v0.1/account')) {
54
          const user = await loadAuth0Profile(token);
55
          request.headers['x-openclasp-email'] = encodeURIComponent(user.email ?? '');
56
          request.headers['x-openclasp-name'] = encodeURIComponent(user.name ?? '');
57
        }
58
      }
59
    } catch {
60
      response.statusCode = 401;
61
      response.setHeader('content-type', 'application/json');
62
      response.end(JSON.stringify({ error: 'invalid_session' }));
63
      return;
64
    }
65
  }
66
  await ready;
67
  app.server.emit('request', request, response);
68
}