{"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../src/astro/routes/api/auth/session-tokens/index.ts"],"sourcesContent":["/**\n * POST /_emdash/api/auth/session-tokens\n *\n * A short-lived personal API token for the signed-in user, for browser-side\n * helpers (toolbar extensions) that hand an agent or an integration a\n * credential acting as that user. Session-only: a token can never mint\n * another token, so a leaked one cannot fan out. The token carries the\n * caller's own policies (or a subset of them), expires within a day, and is\n * intersected with the owner's grants on every request like any other token.\n */\nimport type { APIRoute } from \"astro\";\nimport { z } from \"zod\";\n\nimport { requirePerm } from \"#api/authorize.js\";\nimport { apiError, apiSuccess, handleError } from \"#api/error.js\";\nimport { handleApiTokenCreate } from \"#api/handlers/api-tokens.js\";\nimport { isParseError, parseBody } from \"#api/parse.js\";\n\nexport const prerender = false;\n\nexport const MAX_TTL_SECONDS = 24 * 60 * 60;\nexport const DEFAULT_TTL_SECONDS = 8 * 60 * 60;\n\nconst schema = z.object({\n\t/** Names the token in Settings → API tokens (\"<purpose> · session\"). */\n\tpurpose: z.string().trim().min(1).max(60),\n\texpiresInSeconds: z.number().int().min(60).max(MAX_TTL_SECONDS).optional(),\n\t/** Policy slugs to carry; omitted = every policy the caller's role holds. */\n\tpolicies: z.array(z.string().min(1)).max(50).optional(),\n});\n\nexport const POST: APIRoute = async ({ request, locals }) => {\n\tconst { emdash, user, authz } = locals;\n\tif (!emdash?.db) return apiError(\"NOT_CONFIGURED\", \"EmDash is not initialized\", 500);\n\tif (locals.tokenAuth) {\n\t\treturn apiError(\n\t\t\t\"TOKEN_AUTH_FORBIDDEN\",\n\t\t\t\"Session tokens can only be minted from a signed-in session.\",\n\t\t\t403,\n\t\t);\n\t}\n\tconst denied = requirePerm(user, \"content:edit_own\");\n\tif (denied) return denied;\n\tif (!authz)\n\t\treturn apiError(\"NOT_CONFIGURED\", \"Authorization was not resolved for this request\", 500);\n\n\ttry {\n\t\tconst body = await parseBody(request, schema);\n\t\tif (isParseError(body)) return body;\n\n\t\tconst held = new Set(authz.rolePolicies);\n\t\tconst policies = body.policies?.length\n\t\t\t? body.policies.filter((slug) => held.has(slug))\n\t\t\t: [...authz.rolePolicies];\n\t\tif (policies.length === 0) {\n\t\t\treturn apiError(\n\t\t\t\t\"POLICY_NOT_HELD\",\n\t\t\t\t\"None of the requested policies are held by your role.\",\n\t\t\t\t403,\n\t\t\t);\n\t\t}\n\t\tconst ttl = body.expiresInSeconds ?? DEFAULT_TTL_SECONDS;\n\t\tconst expiresAt = new Date(Date.now() + ttl * 1000).toISOString();\n\t\tconst result = await handleApiTokenCreate(emdash.db, user!.id, {\n\t\t\tname: `${body.purpose} · session`,\n\t\t\tpolicies,\n\t\t\texpiresAt,\n\t\t});\n\t\tif (!result.success) return apiError(result.error.code, result.error.message, 500);\n\t\treturn apiSuccess({ id: result.data.info.id, token: result.data.token, expiresAt, policies });\n\t} catch (error) {\n\t\treturn handleError(error, \"Failed to mint a session token\", \"SESSION_TOKEN_ERROR\");\n\t}\n};\n"],"mappings":";;;;;;;;;;AAkBA,MAAa,YAAY;AAEzB,MAAa,kBAAkB,OAAU;AACzC,MAAa,sBAAsB,MAAS;AAE5C,MAAM,SAAS,EAAE,OAAO;CAEvB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,IAAI,GAAG;CACzC,kBAAkB,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,gBAAgB,CAAC,UAAU;CAE1E,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,GAAG,CAAC,UAAU;CACvD,CAAC;AAEF,MAAa,OAAiB,OAAO,EAAE,SAAS,aAAa;CAC5D,MAAM,EAAE,QAAQ,MAAM,UAAU;AAChC,KAAI,CAAC,QAAQ,GAAI,QAAO,SAAS,kBAAkB,6BAA6B,IAAI;AACpF,KAAI,OAAO,UACV,QAAO,SACN,wBACA,+DACA,IACA;CAEF,MAAM,SAAS,YAAY,MAAM,mBAAmB;AACpD,KAAI,OAAQ,QAAO;AACnB,KAAI,CAAC,MACJ,QAAO,SAAS,kBAAkB,mDAAmD,IAAI;AAE1F,KAAI;EACH,MAAM,OAAO,MAAM,UAAU,SAAS,OAAO;AAC7C,MAAI,aAAa,KAAK,CAAE,QAAO;EAE/B,MAAM,OAAO,IAAI,IAAI,MAAM,aAAa;EACxC,MAAM,WAAW,KAAK,UAAU,SAC7B,KAAK,SAAS,QAAQ,SAAS,KAAK,IAAI,KAAK,CAAC,GAC9C,CAAC,GAAG,MAAM,aAAa;AAC1B,MAAI,SAAS,WAAW,EACvB,QAAO,SACN,mBACA,yDACA,IACA;EAEF,MAAM,MAAM,KAAK,oBAAoB;EACrC,MAAM,YAAY,IAAI,KAAK,KAAK,KAAK,GAAG,MAAM,IAAK,CAAC,aAAa;EACjE,MAAM,SAAS,MAAM,qBAAqB,OAAO,IAAI,KAAM,IAAI;GAC9D,MAAM,GAAG,KAAK,QAAQ;GACtB;GACA;GACA,CAAC;AACF,MAAI,CAAC,OAAO,QAAS,QAAO,SAAS,OAAO,MAAM,MAAM,OAAO,MAAM,SAAS,IAAI;AAClF,SAAO,WAAW;GAAE,IAAI,OAAO,KAAK,KAAK;GAAI,OAAO,OAAO,KAAK;GAAO;GAAW;GAAU,CAAC;UACrF,OAAO;AACf,SAAO,YAAY,OAAO,kCAAkC,sBAAsB"}