Bridge AAM's consent flow into your existing auth library.
AAM doesn't replace your auth. It piggy-backs on it. When an agent wants to act on behalf of a logged-in user, it opens your /agent/authorize page, your existing login flow handles authentication, your existing user gets an "approve this agent?" UI, and on approval you mint an AAM auth code.
Pick your framework — each tab gives you a working /agent/authorize page, an /api/aam/token exchange handler, and a revocation hook that plugs into your settings page.
NextAuth v5 (auth.js). The only library-specific line is the call to auth() to read the current session — everything around it is the protocol-level dance you'd do for any framework.
01Add the shared signing helperlib/aam/sign.ts
Mints + verifies the short-lived auth_code that the agent exchanges for a bearer. Uses Node crypto, no extra deps. Set AAM_AUTH_CODE_SECRET in your env.
tsx
// lib/aam/sign.ts — shared signing helper used by both routes
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
const SECRET = process.env.AAM_AUTH_CODE_SECRET!;
export function mintAuthCode(payload: { userId: string; scope: string; ttlSec: number }) {
const exp = Math.floor(Date.now() / 1000) + payload.ttlSec;
const nonce = randomBytes(8).toString("hex");
const body = `${payload.userId}.${payload.scope}.${exp}.${nonce}`;
const sig = createHmac("sha256", SECRET).update(body).digest("hex");
return `${body}.${sig}`;
}
export function verifyAuthCode(code: string) {
const parts = code.split(".");
if (parts.length !== 5) return null;
const [userId, scope, exp, nonce, sig] = parts;
const body = `${userId}.${scope}.${exp}.${nonce}`;
const expected = createHmac("sha256", SECRET).update(body).digest("hex");
if (!timingSafeEqual(Buffer.from(sig, "hex"), Buffer.from(expected, "hex"))) return null;
if (Date.now() / 1000 > Number(exp)) return null;
return { userId, scope };
}
02The consent pageapp/agent/authorize/page.tsx
Reads the NextAuth session, redirects to /login if absent, renders an Approve/Deny form. On approve, mints an auth_code and redirects back to the agent's redirect_uri.
Agent POSTs auth_code here, gets back a short-lived JWT bearer with the approved scope. Use any JWT library (jose / jsonwebtoken). The bearer is what they'll send to your /api/aam-hooks/* endpoints.
04Revocation hook in your settings pageapp/settings/connected-agents/page.tsx
The agent's bearer expires automatically (1h above), so 'revocation' is just letting the user invalidate any cached agent state. List approved agents in your settings UI, button to delete from your agent_grants table.
Don't see your auth library? The pattern is the same for any session-based auth: read the session at /agent/authorize, redirect to your login if absent, render an Approve/Deny form on submit, mint a short-lived signed code, redirect back to the agent. The protocol piece (signing the code, exchanging it for a bearer) is identical across libraries — only the "read the current user's session" line differs. Email hello@whatcanido.dev and we'll add your library.