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

Viewing files at commit ce192002f08aCommit detailsView latest

api/session.ts 105 lines · 3.6 KB · typescript
1
import {
2
  auth0Config,
3
  dashboardSessionCookie,
4
  dashboardTokenFromCookie,
5
  loadAuth0Profile,
6
  verifyAuth0Token,
7
} from '../apps/api/src/auth0.js';
8
import { guardRequest } from '../apps/api/src/request-security.js';
9
import { assertProductionConfiguration } from '../apps/api/src/production-config.js';
10
11
assertProductionConfiguration('auth');
12
13
function cookie(value: string, maxAge: number, secure: boolean) {
14
  return [
15
    `${dashboardSessionCookie}=${encodeURIComponent(value)}`,
16
    'HttpOnly',
17
    'Path=/',
18
    'SameSite=Lax',
19
    `Max-Age=${Math.max(0, Math.floor(maxAge))}`,
20
    ...(secure ? ['Secure'] : []),
21
  ].join('; ');
22
}
23
24
export async function POST(request: Request): Promise<Response> {
25
  const rejected = await guardRequest(request, 'dashboard-session-create', {
26
    limit: 30,
27
    maximumBytes: 16_384,
28
  });
29
  if (rejected) return rejected;
30
  const input = (await request.json()) as { code?: unknown; codeVerifier?: unknown };
31
  if (typeof input.code !== 'string' || typeof input.codeVerifier !== 'string')
32
    return Response.json({ error: 'invalid_callback' }, { status: 400 });
33
  const config = auth0Config();
34
  const redirectUri = `${new URL(request.url).origin}/sso-callback`;
35
  const tokenResponse = await fetch(`${config.issuer}oauth/token`, {
36
    method: 'POST',
37
    headers: { 'content-type': 'application/x-www-form-urlencoded' },
38
    body: new URLSearchParams({
39
      grant_type: 'authorization_code',
40
      client_id: config.dashboardClientId,
41
      code: input.code,
42
      code_verifier: input.codeVerifier,
43
      redirect_uri: redirectUri,
44
    }),
45
  });
46
  if (!tokenResponse.ok) return Response.json({ error: 'token_exchange_failed' }, { status: 401 });
47
  const tokens = (await tokenResponse.json()) as { access_token?: unknown; expires_in?: unknown };
48
  if (typeof tokens.access_token !== 'string')
49
    return Response.json({ error: 'invalid_token_response' }, { status: 401 });
50
  const authentication = await verifyAuth0Token(tokens.access_token, { dashboard: true });
51
  const user = await loadAuth0Profile(tokens.access_token);
52
  const expiresIn =
53
    typeof tokens.expires_in === 'number'
54
      ? tokens.expires_in
55
      : typeof authentication.payload.exp === 'number'
56
        ? authentication.payload.exp - Math.floor(Date.now() / 1000)
57
        : 3600;
58
  return Response.json(
59
    { user: { ...user, sub: authentication.payload.sub } },
60
    {
61
      headers: {
62
        'cache-control': 'no-store',
63
        'set-cookie': cookie(
64
          tokens.access_token,
65
          Math.max(expiresIn - 30, 1),
66
          new URL(request.url).protocol === 'https:',
67
        ),
68
      },
69
    },
70
  );
71
}
72
73
export async function GET(request: Request): Promise<Response> {
74
  const token = dashboardTokenFromCookie(request.headers.get('cookie') ?? undefined);
75
  if (!token) return Response.json({ authenticated: false }, { status: 401 });
76
  try {
77
    const authentication = await verifyAuth0Token(token, { dashboard: true });
78
    const user = await loadAuth0Profile(token);
79
    return Response.json(
80
      { authenticated: true, user: { ...user, sub: authentication.payload.sub } },
81
      { headers: { 'cache-control': 'no-store' } },
82
    );
83
  } catch {
84
    return Response.json(
85
      { authenticated: false },
86
      {
87
        status: 401,
88
        headers: {
89
          'cache-control': 'no-store',
90
          'set-cookie': cookie('', 0, new URL(request.url).protocol === 'https:'),
91
        },
92
      },
93
    );
94
  }
95
}
96
97
export function DELETE(request: Request): Response {
98
  return new Response(null, {
99
    status: 204,
100
    headers: {
101
      'cache-control': 'no-store',
102
      'set-cookie': cookie('', 0, new URL(request.url).protocol === 'https:'),
103
    },
104
  });
105
}