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

Viewing files at commit bd8b77ebf65aCommit detailsView latest

api/index.ts 66 lines · 2.9 KB · typescript
1
import type { IncomingMessage, ServerResponse } from 'node:http';
2
import { createClerkClient } from '@clerk/backend';
3
import { buildApi } from '../apps/api/src/app.js';
4
import { TrustEngine } from '../packages/core/src/index.js';
5
import { HostedRepository } from '../packages/persistence/src/hosted.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
function clerkRequest(request: IncomingMessage): Request {
13
  const headers = new Headers();
14
  for (const [name, value] of Object.entries(request.headers)) {
15
    if (Array.isArray(value)) for (const item of value) headers.append(name, item);
16
    else if (value !== undefined) headers.set(name, value);
17
  }
18
  const host = request.headers.host ?? 'openclasp.vercel.app';
19
  return new Request(`https://${host}${request.url ?? '/'}`, { headers });
20
}
21
22
export default async function handler(request: IncomingMessage, response: ServerResponse) {
23
  const incoming = new URL(request.url ?? '/', 'https://openclasp.local');
24
  const target = incoming.searchParams.get('path');
25
  if (target) request.url = target;
26
  if ((request.url ?? '').startsWith('/v0.1/')) {
27
    const secretKey = process.env.CLERK_SECRET_KEY;
28
    const publishableKey = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY;
29
    if (!secretKey || !publishableKey || !request.headers.authorization) {
30
      response.statusCode = 401;
31
      response.setHeader('content-type', 'application/json');
32
      response.end(JSON.stringify({ error: 'authentication_required' }));
33
      return;
34
    }
35
    try {
36
      const origin = `https://${request.headers.host ?? 'openclasp.vercel.app'}`;
37
      const authentication = await createClerkClient({
38
        secretKey,
39
        publishableKey,
40
      }).authenticateRequest(clerkRequest(request), {
41
        acceptsToken: 'session_token',
42
        authorizedParties: [origin, 'https://openclasp.vercel.app'],
43
      });
44
      if (!authentication.isAuthenticated) throw new Error('Invalid session');
45
      const auth = authentication.toAuth();
46
      if (!auth.userId) throw new Error('Session has no subject');
47
      request.headers['x-openclasp-operator'] = auth.userId;
48
      const user = await createClerkClient({ secretKey, publishableKey }).users.getUser(
49
        auth.userId,
50
      );
51
      request.headers['x-openclasp-email'] = encodeURIComponent(
52
        user.primaryEmailAddress?.emailAddress ?? '',
53
      );
54
      request.headers['x-openclasp-name'] = encodeURIComponent(
55
        [user.firstName, user.lastName].filter(Boolean).join(' '),
56
      );
57
    } catch {
58
      response.statusCode = 401;
59
      response.setHeader('content-type', 'application/json');
60
      response.end(JSON.stringify({ error: 'invalid_session' }));
61
      return;
62
    }
63
  }
64
  await ready;
65
  app.server.emit('request', request, response);
66
}