import { tokenCredential } from "../token-scope";
import type { RequestCredential } from "../token-scope";
import { agentForToken } from "./grants";
import { protectedResourceMetadataUrl } from "./oauth";
import { RPC } from "./protocol";
import type { AgentIdentity } from "./protocol";
import type { Viewer } from "~/lib/types";
const MAX_AGENT_NAME = 80;
type AgentRow = Awaited<ReturnType<typeof agentForToken>>;
export type Authenticated = {
credential: RequestCredential;
viewer: Viewer;
/** Stable for an OAuth grant's whole life: refreshes rotate the secret, not the row. */
tokenId: string;
agentRow: AgentRow;
};
function tokenIdOf(credential: RequestCredential): string | null {
const grant = credential.grant;
return grant && grant.kind !== "deploy" ? grant.tokenId : null;
}
/** Bearer personal tokens only (OAuth-issued or PAT); deploy tokens have no user to act for. */
export async function authenticate(request: Request): Promise<Authenticated | null> {
const credential = await tokenCredential(request);
const tokenId = credential ? tokenIdOf(credential) : null;
if (!credential?.viewer || !tokenId) return null;
const agentRow = await agentForToken(tokenId);
return { credential, viewer: credential.viewer, tokenId, agentRow };
}
export function unauthorizedResponse(origin: string, error?: string): Response {
const challenge = [
`Bearer resource_metadata="${protectedResourceMetadataUrl(origin)}"`,
...(error ? [`error="${error}"`] : []),
].join(", ");
return Response.json(