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

Viewing files at commit 7ef7c69958caCommit detailsView latest

api/oauth-register.ts 70 lines · 2.3 KB · typescript
1
import { auth0Config } from './auth0.js';
2
3
const maximumRegistrationBytes = 32_768;
4
5
export async function POST(request: Request): Promise<Response> {
6
  const body = await request.text();
7
  if (new TextEncoder().encode(body).byteLength > maximumRegistrationBytes)
8
    return Response.json({ error: 'invalid_client_metadata' }, { status: 413 });
9
10
  let metadata: Record<string, unknown>;
11
  try {
12
    const parsed = JSON.parse(body) as unknown;
13
    if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error();
14
    metadata = parsed as Record<string, unknown>;
15
  } catch {
16
    return Response.json({ error: 'invalid_client_metadata' }, { status: 400 });
17
  }
18
  if (!Array.isArray(metadata.redirect_uris) || metadata.redirect_uris.length === 0)
19
    return Response.json(
20
      { error: 'invalid_client_metadata', error_description: 'redirect_uris is required' },
21
      { status: 400 },
22
    );
23
24
  const { issuer } = auth0Config();
25
  const upstream = await fetch(`${issuer}oidc/register`, {
26
    method: 'POST',
27
    headers: { 'content-type': 'application/json' },
28
    body,
29
  });
30
  const responseBody = await upstream.text();
31
  if (!upstream.ok) {
32
    let reason = 'Auth0 rejected dynamic client registration';
33
    try {
34
      const error = JSON.parse(responseBody) as Record<string, unknown>;
35
      reason = String(error.error_description ?? error.message ?? error.error ?? reason);
36
    } catch {
37
      // Do not log an arbitrary upstream body.
38
    }
39
    console.warn('[oauth.dcr] registration rejected', {
40
      status: upstream.status,
41
      reason,
42
      redirectUriCount: metadata.redirect_uris.length,
43
    });
44
  } else {
45
    console.info('[oauth.dcr] registration accepted', {
46
      redirectUriCount: metadata.redirect_uris.length,
47
    });
48
  }
49
50
  return new Response(responseBody, {
51
    status: upstream.status,
52
    headers: {
53
      'access-control-allow-origin': '*',
54
      'cache-control': 'no-store',
55
      'content-type': upstream.headers.get('content-type') ?? 'application/json',
56
    },
57
  });
58
}
59
60
export function OPTIONS(): Response {
61
  return new Response(null, {
62
    status: 204,
63
    headers: {
64
      'access-control-allow-headers': 'content-type',
65
      'access-control-allow-methods': 'POST, OPTIONS',
66
      'access-control-allow-origin': '*',
67
      'access-control-max-age': '86400',
68
    },
69
  });
70
}