@cauthyhq/node 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.md +79 -24
- package/dist/{cauthy-Wy-YDGrM.d.ts → cauthy-C7gZ-pbv.d.ts} +64 -2
- package/dist/{cauthy-Bn-i7cKD.d.mts → cauthy-CCSSj3Lg.d.mts} +64 -2
- package/dist/express.d.mts +2 -1
- package/dist/express.d.ts +2 -1
- package/dist/fastify.d.mts +2 -1
- package/dist/fastify.d.ts +2 -1
- package/dist/hono.d.mts +2 -1
- package/dist/hono.d.ts +2 -1
- package/dist/identity-BTJ8NB_w.d.mts +27 -0
- package/dist/identity-BTJ8NB_w.d.ts +27 -0
- package/dist/index.d.mts +21 -2
- package/dist/index.d.ts +21 -2
- package/dist/index.js +688 -67
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +683 -67
- package/dist/index.mjs.map +1 -1
- package/dist/keys.js.map +1 -1
- package/dist/keys.mjs.map +1 -1
- package/dist/mcp.js.map +1 -1
- package/dist/mcp.mjs.map +1 -1
- package/dist/next.d.mts +2 -1
- package/dist/next.d.ts +2 -1
- package/dist/testing.d.mts +36 -0
- package/dist/testing.d.ts +36 -0
- package/dist/testing.js +395 -0
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +395 -0
- package/dist/testing.mjs.map +1 -1
- package/package.json +3 -1
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/testing.ts"],"sourcesContent":["// A Cauthy that lives in memory, and a browser that keeps cookies: what the\n// tests sign in against. Exported as `@cauthyhq/node/testing`, for tests only:\n// it signs in anyone it was told about, and checks nothing a real Cauthy does.\n\nimport { createServer, type Server } from 'node:http'\n\n/** The settings the fake's admin API changes (PUT /v1/settings/auth), as the dashboard's switches do. */\nconst ADMIN_SETTINGS = ['allow_signup', 'allow_password', 'allow_magic_link', 'allow_passkeys', 'confirm_email', 'verify_grace_days'] as const\n\nexport class FakeCauthy {\n readonly issuer = 'https://id.test'\n users = new Map<string, { sub: string; email: string; name: string; active: boolean; restricted: boolean; anonymous?: boolean; verified?: boolean }>()\n codes = new Map<string, { sub: string; challenge: string; redirectUri: string }>()\n refresh = new Map<string, string>() // refresh token → sub\n access = new Map<string, string>() // access token → sub\n orgs = new Map<string, Record<string, unknown>[]>() // sub → organizations\n revoked: string[] = []\n checks: { organization: string; sub: string; body: any }[] = []\n calls: string[] = []\n down = false\n expiresIn = 600\n /** What token answers say of the session's kind; absent means an older Cauthy that does not say. */\n persistent: boolean | undefined = undefined\n /** Serve the API an application's own screens use (/v1/auth/*). Off unless a test asks. */\n ownScreens = false\n /** The workspace those screens sign in to, as X-Tenant-Key names it. */\n tenant = 'acme'\n /** Each account's password, by email: only these accounts sign in with one. */\n passwords = new Map<string, string>()\n /** What GET /v1/auth/settings answers. */\n settings: Record<string, unknown> = { allow_password: true, allow_signup: false, allow_passkeys: false, persistent_sessions: false }\n /** The bearer the admin API takes (settings, the application's client, accounts); '' refuses every admin call. */\n adminToken = ''\n /** The application's client, as the admin API lists it; its allow_persistent_sessions is settings.persistent_sessions. */\n clientId = 'acme-crm'\n private n = 0\n\n constructor() {\n this.users.set('u1', { sub: 'u1', email: 'ana@acme.com', name: 'Ana', active: true, restricted: false })\n }\n\n /** What the hosted page does once the person has signed in: hands the browser a code. */\n authorize(location: string, sub = 'u1'): string {\n const q = new URL(location).searchParams\n if (q.get('response_type') !== 'code') throw new Error('Cauthy answers unsupported_response_type: response_type=code is required')\n const code = `code-${++this.n}`\n this.codes.set(code, { sub, challenge: q.get('code_challenge') ?? '', redirectUri: q.get('redirect_uri') ?? '' })\n return `${q.get('redirect_uri')}?code=${code}&state=${q.get('state')}`\n }\n\n /** A token answer. `kind` is what a sign-in asked for (remember) when the client allows long sessions; `persistent` wins when set. */\n private issue(sub: string, kind?: boolean) {\n const at = `at-${++this.n}`\n const rt = `rt-${++this.n}`\n this.access.set(at, sub)\n this.refresh.set(rt, sub)\n const persistent = this.persistent ?? kind\n return { access_token: at, refresh_token: rt, token_type: 'Bearer', expires_in: this.expiresIn, scope: '', ...(persistent === undefined ? {} : { persistent }) }\n }\n\n fetch: typeof fetch = async (input, init) => {\n const url = new URL(typeof input === 'string' ? input : input instanceof URL ? input.href : input.url)\n this.calls.push(`${init?.method ?? 'GET'} ${url.pathname}`)\n if (this.down) throw new TypeError('fetch failed')\n const reply = (status: number, body: unknown) =>\n new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' } })\n const form = new URLSearchParams(init?.body instanceof URLSearchParams ? init.body : String(init?.body ?? ''))\n const bearer = new Headers(init?.headers).get('authorization')?.replace('Bearer ', '') ?? ''\n const admin = adminAnswer(this, url, init)\n if (admin) return admin\n if (this.ownScreens && url.pathname.startsWith('/v1/auth/')) return this.ownScreen(url, init)\n\n if (url.pathname === '/v1/oauth/token') {\n if (form.get('grant_type') === 'authorization_code') {\n const c = this.codes.get(form.get('code') ?? '')\n this.codes.delete(form.get('code') ?? '')\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(form.get('code_verifier') ?? ''))\n const challenge = btoa(String.fromCharCode(...new Uint8Array(digest))).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n if (!c || c.challenge !== challenge || c.redirectUri !== form.get('redirect_uri')) return reply(400, { error: 'invalid_grant', error_description: 'the grant is invalid, expired or already used' })\n return reply(200, this.issue(c.sub))\n }\n if (form.get('grant_type') === 'refresh_token') {\n const sub = this.refresh.get(form.get('refresh_token') ?? '')\n this.refresh.delete(form.get('refresh_token') ?? '') // used once\n if (!sub || !this.users.get(sub)?.active) return reply(400, { error: 'invalid_grant', error_description: 'the grant is invalid, expired or already used' })\n return reply(200, this.issue(sub))\n }\n return reply(400, { error: 'unsupported_grant_type', error_description: 'supported grant types: authorization_code, refresh_token' })\n }\n if (url.pathname === '/v1/oauth/revoke') {\n this.revoked.push(form.get('token') ?? '')\n this.refresh.delete(form.get('token') ?? '')\n return reply(200, {})\n }\n const sub = this.access.get(bearer)\n const user = sub ? this.users.get(sub) : undefined\n if (url.pathname === '/v1/oauth/userinfo') {\n if (!user) return reply(401, {})\n return reply(200, { sub: user.sub, tid: 't1', email: user.email, name: user.name, email_verified: user.verified ?? !user.anonymous, restricted: user.restricted, roles: ['member'], ...(user.anonymous ? { anonymous: true } : {}) })\n }\n const asking = url.pathname.match(/^\\/v1\\/oauth\\/organizations\\/([^/]+)\\/check$/)\n if (asking && init?.method === 'POST') {\n if (!user) return reply(401, { errors: [{ code: 'unauthorized', message: 'invalid or expired token' }] })\n const org = decodeURIComponent(asking[1])\n const body = JSON.parse(String(init.body))\n this.checks.push({ organization: org, sub: user.sub, body })\n if (!(this.orgs.get(user.sub) ?? []).some((o) => o.id === org)) return reply(404, { errors: [{ code: 'organization_not_found', message: 'organization not found' }] })\n // An accountant approves invoices up to a million.\n const allowed = body.resource === 'invoices' && body.action === 'approve' && Number(body.attributes?.amount) <= 1_000_000\n return reply(200, { data: allowed\n ? { allowed, reason: 'allowed by role accountant: invoices:approve', rule: { source: 'role', role: 'accountant', kind: '*', resource: 'invoices', action: 'approve', effect: 'allow', condition: 'resource.attrs.amount <= 1000000' } }\n : { allowed, reason: 'no rule allows it' } })\n }\n if (url.pathname === '/v1/oauth/organizations') {\n if (!user) return reply(401, {})\n const system = url.searchParams.get('system')\n const data = (this.orgs.get(user.sub) ?? []).filter((o) => !system || o.system === system)\n return reply(200, { data, total_count: data.length })\n }\n return reply(404, {})\n }\n\n /** The part of /v1/auth/* a password sign-in and the sign-in form read. */\n private ownScreen(url: URL, init: RequestInit | undefined): Response {\n const reply = (status: number, body: unknown) =>\n new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' } })\n const refuse = (status: number, code: string, message: string) => reply(status, { errors: [{ code, message }] })\n if (new Headers(init?.headers).get('x-tenant-key') !== this.tenant) return refuse(404, 'tenant_not_found', 'no such workspace')\n const method = init?.method ?? 'GET'\n if (method === 'POST' && url.pathname === '/v1/auth/login') {\n if (this.settings.allow_password === false) return refuse(403, 'password_disabled', 'this workspace does not take passwords: sign in another way')\n const body = JSON.parse(String(init?.body ?? '{}')) as { email?: string; password?: string }\n const user = [...this.users.values()].find((u) => u.email === body.email)\n const password = body.email === undefined ? undefined : this.passwords.get(body.email)\n if (!user || !user.active || password === undefined || password !== body.password) {\n return refuse(401, 'invalid_credentials', 'invalid email or password')\n }\n // A long session only when the person asked and the client allows it.\n const kind = this.settings.persistent_sessions === true ? new Headers(init?.headers).get('x-cauthy-remember') === '1' : undefined\n return reply(200, { data: this.issue(user.sub, kind) })\n }\n if (method === 'POST' && url.pathname === '/v1/auth/register') {\n if (this.settings.allow_signup === false) return refuse(403, 'signups_disabled', 'this workspace is not accepting new accounts')\n if (this.settings.allow_password === false) return refuse(403, 'password_disabled', 'this workspace does not take passwords: sign in another way')\n const body = JSON.parse(String(init?.body ?? '{}')) as { email?: string; password?: string; display_name?: string }\n if (!body.email || !body.password) return refuse(400, 'validation_error', 'email and password are required')\n if ([...this.users.values()].some((u) => u.email === body.email)) return refuse(409, 'email_taken', 'an account with this email exists')\n // With confirm_email the account waits for its address (verify_grace_days 0 in the fake).\n const confirm = this.settings.confirm_email === true\n const sub = `u-${++this.n}`\n this.users.set(sub, { sub, email: body.email, name: body.display_name ?? '', active: !confirm, restricted: false, verified: !confirm })\n this.passwords.set(body.email, body.password)\n return reply(201, { data: { user_id: sub, email: body.email, ...(confirm ? { verification_required: true } : {}) } })\n }\n if (method === 'GET' && url.pathname === '/v1/auth/settings') return reply(200, { data: this.settings })\n if (method === 'GET' && (url.pathname === '/v1/auth/social/providers' || url.pathname === '/v1/auth/sso/connections')) return reply(200, { data: [] })\n if (method === 'GET' && url.pathname === '/v1/auth/webauthn/place') return reply(200, { data: { here: false, on_cauthy: false } })\n return refuse(404, 'not_found', 'not found')\n }\n}\n\n/**\n * The part of Cauthy's admin API the conformance runner's dashboard-switch\n * checks drive: GET and PUT /v1/settings/auth, GET /v1/oauth/clients,\n * PATCH /v1/oauth/clients/{id}, GET /v1/users and DELETE /v1/users/{id}.\n * null when the request is none of them.\n */\nfunction adminAnswer(fake: FakeCauthy, url: URL, init: RequestInit | undefined): Response | null {\n const method = (init?.method ?? 'GET').toUpperCase()\n const route = `${method} ${url.pathname}`\n const client = method === 'PATCH' ? url.pathname.match(/^\\/v1\\/oauth\\/clients\\/([^/]+)$/) : null\n const user = method === 'DELETE' ? url.pathname.match(/^\\/v1\\/users\\/([^/]+)$/) : null\n const known = ['GET /v1/settings/auth', 'PUT /v1/settings/auth', 'GET /v1/oauth/clients', 'GET /v1/users'].includes(route)\n if (!known && !client && !user) return null\n const reply = (status: number, body: unknown) =>\n new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' } })\n const refuse = (status: number, code: string, message: string) => reply(status, { errors: [{ code, message }] })\n const headers = new Headers(init?.headers)\n if (fake.adminToken === '' || headers.get('authorization') !== `Bearer ${fake.adminToken}`) return refuse(401, 'unauthorized', 'invalid or expired token')\n if (headers.get('x-tenant-key') !== fake.tenant) return refuse(404, 'tenant_not_found', 'no such workspace')\n if (route === 'GET /v1/settings/auth') return reply(200, { data: fake.settings })\n if (route === 'PUT /v1/settings/auth') {\n const change = JSON.parse(String(init?.body ?? '{}')) as Record<string, unknown>\n const next = { ...fake.settings }\n for (const key of ADMIN_SETTINGS) if (change[key] !== undefined && change[key] !== null) next[key] = change[key]\n if (next.allow_password === false && next.allow_magic_link !== true && next.allow_passkeys !== true) {\n return refuse(409, 'last_method', 'the change would leave no way to sign in')\n }\n fake.settings = next\n return reply(200, { data: fake.settings })\n }\n const persistent = () => fake.settings.persistent_sessions === true\n if (route === 'GET /v1/oauth/clients') return reply(200, { data: [{ client_id: fake.clientId, allow_persistent_sessions: persistent() }], total_count: 1 })\n if (client) {\n if (decodeURIComponent(client[1]) !== fake.clientId) return refuse(404, 'client_not_found', 'no such client')\n const change = JSON.parse(String(init?.body ?? '{}')) as { allow_persistent_sessions?: unknown }\n if (typeof change.allow_persistent_sessions === 'boolean') fake.settings = { ...fake.settings, persistent_sessions: change.allow_persistent_sessions }\n return reply(200, { data: { client_id: fake.clientId, allow_persistent_sessions: persistent() } })\n }\n if (route === 'GET /v1/users') {\n const q = (url.searchParams.get('q') ?? '').toLowerCase()\n const data = [...fake.users.values()].filter((u) => u.email.toLowerCase().includes(q)).map((u) => ({ id: u.sub, email: u.email, name: u.name }))\n return reply(200, { data, total_count: data.length })\n }\n const id = decodeURIComponent(user![1])\n const gone = fake.users.get(id)\n if (!gone) return refuse(404, 'user_not_found', 'no such user')\n fake.users.delete(id)\n fake.passwords.delete(gone.email)\n return new Response(null, { status: 204 })\n}\n\n/**\n * Serves a FakeCauthy over HTTP on 127.0.0.1, for a process outside this one\n * (the conformance runner) to reach it, as `--cauthy http://127.0.0.1:<port>`.\n */\nexport function serveFakeCauthy(fake: FakeCauthy, port: number): Promise<Server> {\n const server = createServer(async (req, res) => {\n const chunks: Buffer[] = []\n for await (const chunk of req) chunks.push(chunk as Buffer)\n const headers = new Headers()\n for (const [k, v] of Object.entries(req.headers)) if (typeof v === 'string') headers.set(k, v)\n const answer = await fake.fetch(`${fake.issuer}${req.url ?? '/'}`, {\n method: req.method,\n headers,\n ...(chunks.length > 0 ? { body: Buffer.concat(chunks).toString('utf8') } : {}),\n })\n res.writeHead(answer.status, Object.fromEntries(answer.headers))\n res.end(Buffer.from(await answer.arrayBuffer()))\n })\n return new Promise((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, '127.0.0.1', () => resolve(server))\n })\n}\n\n/** A browser as far as cookies go: it keeps what it is told to and sends it back. */\nexport class Browser {\n jar = new Map<string, string>()\n /** `requestHeader: false`: a browser whose client sends no X-Cauthy-Request, as cauthy-auth 0.42's does not. */\n constructor(readonly origin = 'https://app.test', private readonly options: { requestHeader?: boolean } = {}) {}\n\n request(path: string, init: RequestInit = {}): Request {\n const headers = new Headers(init.headers)\n if (this.jar.size > 0) headers.set('cookie', [...this.jar].map(([k, v]) => `${k}=${v}`).join('; '))\n // What a protocol-1 client sends on every mutation.\n const method = (init.method ?? 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'HEAD' && this.options.requestHeader !== false && !headers.has('x-cauthy-request')) {\n headers.set('x-cauthy-request', '1')\n }\n return new Request(this.origin + path, { ...init, headers })\n }\n\n /** Keeps the cookies of a response, as a browser would. */\n take(source: Response | string[]): void {\n const cookies = Array.isArray(source) ? source : source.headers.getSetCookie()\n for (const c of cookies) {\n const [pair, ...attrs] = c.split('; ')\n const i = pair.indexOf('=')\n const name = pair.slice(0, i)\n const value = pair.slice(i + 1)\n if (value === '' || attrs.includes('Max-Age=0')) this.jar.delete(name)\n else this.jar.set(name, value)\n }\n }\n}\n"],"mappings":"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","names":["sub"]}
|
|
1
|
+
{"version":3,"sources":["../src/testing.ts","../src/sha256.ts","../src/types.ts","../src/cauthykey.ts","../src/rootkey.ts","../src/keyderive.ts"],"sourcesContent":["// A Cauthy that lives in memory, and a browser that keeps cookies: what the\n// tests sign in against. Exported as `@cauthyhq/node/testing`, for tests only:\n// it signs in anyone it was told about, and checks little a real Cauthy does.\n// What it does check is how a CAUTHY_KEY application proves itself: it issues\n// keys (`issueKey`), takes Ed25519 assertions on every token and revoke call of\n// a key client, and answers GET /v1/connect/self.\n\nimport { createServer, type Server } from 'node:http'\nimport { b64uDecode, b64uEncode, composeKey, parseKey } from './cauthykey'\nimport type { Identity } from './identity'\nimport { ASSERTION_TYPE, deriveFromKey } from './keyderive'\n\n/** The settings the fake's admin API changes (PUT /v1/settings/auth), as the dashboard's switches do. */\nconst ADMIN_SETTINGS = ['allow_signup', 'allow_password', 'allow_magic_link', 'allow_passkeys', 'confirm_email', 'verify_grace_days'] as const\n\nexport class FakeCauthy {\n readonly issuer = 'https://id.test'\n users = new Map<string, { sub: string; email: string; name: string; active: boolean; restricted: boolean; anonymous?: boolean; verified?: boolean }>()\n codes = new Map<string, { sub: string; challenge: string; redirectUri: string }>()\n refresh = new Map<string, string>() // refresh token → sub\n access = new Map<string, string>() // access token → sub\n orgs = new Map<string, Record<string, unknown>[]>() // sub → organizations\n revoked: string[] = []\n checks: { organization: string; sub: string; body: any }[] = []\n calls: string[] = []\n down = false\n expiresIn = 600\n /** What token answers say of the session's kind; absent means an older Cauthy that does not say. */\n persistent: boolean | undefined = undefined\n /** Serve the API an application's own screens use (/v1/auth/*). Off unless a test asks. */\n ownScreens = false\n /** The workspace those screens sign in to, as X-Tenant-Key names it. */\n tenant = 'acme'\n /** Each account's password, by email: only these accounts sign in with one. */\n passwords = new Map<string, string>()\n /** What GET /v1/auth/settings answers. */\n settings: Record<string, unknown> = { allow_password: true, allow_signup: false, allow_passkeys: false, persistent_sessions: false }\n /** The bearer the admin API takes (settings, the application's client, accounts); '' refuses every admin call. */\n adminToken = ''\n /** The application's client, as the admin API lists it; its allow_persistent_sessions is settings.persistent_sessions. */\n clientId = 'acme-crm'\n /** The application's registered origins, as GET /v1/connect/self answers them (app_urls). */\n appUrls: string[] = ['http://localhost:3000']\n /** The application's system, as GET /v1/connect/self answers it. */\n system = 'acme-crm'\n /** The workspace's id, as GET /v1/connect/self answers it. */\n tenantId = 't-acme'\n /** What GET /v1/connect/self answers as `contract`. */\n contract: Identity['contract'] = null\n /** Every call to the token and revoke endpoints, with its form and headers: what a test reads of how the application authenticated. */\n oauthRequests: { path: string; form: Record<string, string>; headers: Record<string, string> }[] = []\n private n = 0\n private keys = new Map<string, { x: string; clientId: string; issuer: string; revoked: boolean }>()\n private jtis = new Set<string>()\n private systemTokens = new Map<string, string>() // access token → kid\n private keyClients = new Set<string>()\n\n constructor() {\n this.users.set('u1', { sub: 'u1', email: 'ana@acme.com', name: 'Ana', active: true, restricted: false })\n }\n\n /** What the hosted page does once the person has signed in: hands the browser a code. */\n authorize(location: string, sub = 'u1'): string {\n const q = new URL(location).searchParams\n if (q.get('response_type') !== 'code') throw new Error('Cauthy answers unsupported_response_type: response_type=code is required')\n const code = `code-${++this.n}`\n this.codes.set(code, { sub, challenge: q.get('code_challenge') ?? '', redirectUri: q.get('redirect_uri') ?? '' })\n return `${q.get('redirect_uri')}?code=${code}&state=${q.get('state')}`\n }\n\n /**\n * Makes a CAUTHY_KEY for the application's client and registers its public\n * half, as Cauthy's dashboard does in the browser. `issuer` is the address\n * the application will reach this Cauthy at (the served one, over HTTP).\n * From then on that client authenticates with assertions only.\n */\n async issueKey(issuer: string = this.issuer, options: { clientId?: string } = {}): Promise<string> {\n const clientId = options.clientId ?? this.clientId\n const key = composeKey(issuer.replace(/\\/+$/, ''), clientId, b64uEncode(crypto.getRandomValues(new Uint8Array(32))))\n const signer = await deriveFromKey(parseKey(key)).signer\n this.keys.set(signer.kid, { x: signer.publicJwk.x, clientId, issuer: parseKey(key).issuer, revoked: false })\n this.keyClients.add(clientId)\n return key\n }\n\n /** Revokes a registered key, as the dashboard's Revoke does: refused from now on. */\n revokeKey(kid: string): void {\n const k = this.keys.get(kid)\n if (k) k.revoked = true\n }\n\n /** The kids registered, revoked or not. */\n registeredKids(): string[] {\n return [...this.keys.keys()]\n }\n\n /** Checks an RFC 7523 assertion of a registered Ed25519 key: the kid it signed with, or null. */\n private async assertionOf(form: URLSearchParams): Promise<string | null> {\n if (form.get('client_assertion_type') !== ASSERTION_TYPE) return null\n const jws = form.get('client_assertion') ?? ''\n const [h, p, sig, extra] = jws.split('.')\n if (!h || !p || !sig || extra !== undefined) return null\n const dec = new TextDecoder()\n let header: { alg?: unknown; kid?: unknown }\n let claims: { iss?: unknown; sub?: unknown; aud?: unknown; jti?: unknown; exp?: unknown }\n try {\n header = JSON.parse(dec.decode(b64uDecode(h) ?? new Uint8Array()))\n claims = JSON.parse(dec.decode(b64uDecode(p) ?? new Uint8Array()))\n } catch {\n return null\n }\n const kid = typeof header.kid === 'string' ? header.kid : ''\n const k = this.keys.get(kid)\n const clientId = form.get('client_id') ?? ''\n if (header.alg !== 'EdDSA' || !k || k.revoked || k.clientId !== clientId) return null\n if (claims.iss !== clientId || claims.sub !== clientId || claims.aud !== `${k.issuer}/v1/oauth/token`) return null\n const now = Math.floor(Date.now() / 1000)\n if (typeof claims.exp !== 'number' || claims.exp <= now || claims.exp - now > 330) return null\n if (typeof claims.jti !== 'string' || claims.jti === '' || this.jtis.has(claims.jti)) return null\n const signature = b64uDecode(sig)\n if (!signature) return null\n const pub = await crypto.subtle.importKey('jwk', { kty: 'OKP', crv: 'Ed25519', x: k.x }, { name: 'Ed25519' }, false, ['verify'])\n if (!(await crypto.subtle.verify({ name: 'Ed25519' }, pub, new Uint8Array(signature), new TextEncoder().encode(`${h}.${p}`)))) return null\n this.jtis.add(claims.jti)\n return kid\n }\n\n /** What GET /v1/connect/self answers for a key. */\n private identityOf(kid: string): Identity {\n const k = this.keys.get(kid)!\n return {\n issuer: k.issuer, audience: k.issuer, tenant: { id: this.tenantId, slug: this.tenant }, system: this.system,\n client_id: k.clientId, sign_in_client_id: k.clientId, app_urls: [...this.appUrls], callback_path: '/api/auth/callback',\n key: { kid, retires_at: null }, contract: this.contract,\n }\n }\n\n /** A token answer. `kind` is what a sign-in asked for (remember) when the client allows long sessions; `persistent` wins when set. */\n private issue(sub: string, kind?: boolean) {\n const at = `at-${++this.n}`\n const rt = `rt-${++this.n}`\n this.access.set(at, sub)\n this.refresh.set(rt, sub)\n const persistent = this.persistent ?? kind\n return { access_token: at, refresh_token: rt, token_type: 'Bearer', expires_in: this.expiresIn, scope: '', ...(persistent === undefined ? {} : { persistent }) }\n }\n\n fetch: typeof fetch = async (input, init) => {\n const url = new URL(typeof input === 'string' ? input : input instanceof URL ? input.href : input.url)\n this.calls.push(`${init?.method ?? 'GET'} ${url.pathname}`)\n if (this.down) throw new TypeError('fetch failed')\n const reply = (status: number, body: unknown) =>\n new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' } })\n const form = new URLSearchParams(init?.body instanceof URLSearchParams ? init.body : String(init?.body ?? ''))\n const bearer = new Headers(init?.headers).get('authorization')?.replace('Bearer ', '') ?? ''\n const admin = adminAnswer(this, url, init)\n if (admin) return admin\n if (this.ownScreens && url.pathname.startsWith('/v1/auth/')) return this.ownScreen(url, init)\n\n const oauth = url.pathname === '/v1/oauth/token' || url.pathname === '/v1/oauth/revoke'\n if (oauth) {\n this.oauthRequests.push({ path: url.pathname, form: Object.fromEntries(form), headers: Object.fromEntries(new Headers(init?.headers)) })\n }\n // A client with a registered key proves itself with an assertion on every\n // call of the token and revoke endpoints, as Cauthy asks of it.\n const asserted = oauth && form.has('client_assertion') ? await this.assertionOf(form) : null\n const keyClient = this.keyClients.has(form.get('client_id') ?? '')\n if (oauth && (keyClient || form.has('client_assertion')) && asserted === null) {\n return reply(401, { error: 'invalid_client', error_description: 'client authentication failed' })\n }\n if (url.pathname === '/v1/connect/self') {\n const kid = this.systemTokens.get(bearer)\n if (kid === undefined) {\n return reply(this.access.has(bearer) ? 403 : 401, { errors: [{ code: this.access.has(bearer) ? 'not_a_system_token' : 'invalid_token', message: 'a system token is required' }] })\n }\n if (this.keys.get(kid)?.revoked) return reply(401, { errors: [{ code: 'invalid_token', message: 'the key was revoked' }] })\n const body = JSON.stringify({ data: this.identityOf(kid) })\n const etag = `\"${b64uEncode(new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(body)))).slice(0, 16)}\"`\n const headers = { etag, 'cache-control': 'private, max-age=300' }\n if (new Headers(init?.headers).get('if-none-match') === etag) return new Response(null, { status: 304, headers })\n return new Response(body, { status: 200, headers: { 'content-type': 'application/json', ...headers } })\n }\n\n if (url.pathname === '/v1/oauth/token') {\n if (form.get('grant_type') === 'client_credentials' && asserted !== null) {\n const token = `sys-${++this.n}`\n this.systemTokens.set(token, asserted)\n return reply(200, { access_token: token, token_type: 'Bearer', expires_in: this.expiresIn })\n }\n if (form.get('grant_type') === 'authorization_code') {\n const c = this.codes.get(form.get('code') ?? '')\n this.codes.delete(form.get('code') ?? '')\n const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(form.get('code_verifier') ?? ''))\n const challenge = btoa(String.fromCharCode(...new Uint8Array(digest))).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n if (!c || c.challenge !== challenge || c.redirectUri !== form.get('redirect_uri')) return reply(400, { error: 'invalid_grant', error_description: 'the grant is invalid, expired or already used' })\n return reply(200, this.issue(c.sub))\n }\n if (form.get('grant_type') === 'refresh_token') {\n const sub = this.refresh.get(form.get('refresh_token') ?? '')\n this.refresh.delete(form.get('refresh_token') ?? '') // used once\n if (!sub || !this.users.get(sub)?.active) return reply(400, { error: 'invalid_grant', error_description: 'the grant is invalid, expired or already used' })\n return reply(200, this.issue(sub))\n }\n return reply(400, { error: 'unsupported_grant_type', error_description: 'supported grant types: authorization_code, refresh_token' })\n }\n if (url.pathname === '/v1/oauth/revoke') {\n this.revoked.push(form.get('token') ?? '')\n this.refresh.delete(form.get('token') ?? '')\n return reply(200, {})\n }\n const sub = this.access.get(bearer)\n const user = sub ? this.users.get(sub) : undefined\n if (url.pathname === '/v1/oauth/userinfo') {\n if (!user) return reply(401, {})\n return reply(200, { sub: user.sub, tid: 't1', email: user.email, name: user.name, email_verified: user.verified ?? !user.anonymous, restricted: user.restricted, roles: ['member'], ...(user.anonymous ? { anonymous: true } : {}) })\n }\n const asking = url.pathname.match(/^\\/v1\\/oauth\\/organizations\\/([^/]+)\\/check$/)\n if (asking && init?.method === 'POST') {\n if (!user) return reply(401, { errors: [{ code: 'unauthorized', message: 'invalid or expired token' }] })\n const org = decodeURIComponent(asking[1])\n const body = JSON.parse(String(init.body))\n this.checks.push({ organization: org, sub: user.sub, body })\n if (!(this.orgs.get(user.sub) ?? []).some((o) => o.id === org)) return reply(404, { errors: [{ code: 'organization_not_found', message: 'organization not found' }] })\n // An accountant approves invoices up to a million.\n const allowed = body.resource === 'invoices' && body.action === 'approve' && Number(body.attributes?.amount) <= 1_000_000\n return reply(200, { data: allowed\n ? { allowed, reason: 'allowed by role accountant: invoices:approve', rule: { source: 'role', role: 'accountant', kind: '*', resource: 'invoices', action: 'approve', effect: 'allow', condition: 'resource.attrs.amount <= 1000000' } }\n : { allowed, reason: 'no rule allows it' } })\n }\n if (url.pathname === '/v1/oauth/organizations') {\n if (!user) return reply(401, {})\n const system = url.searchParams.get('system')\n const data = (this.orgs.get(user.sub) ?? []).filter((o) => !system || o.system === system)\n return reply(200, { data, total_count: data.length })\n }\n return reply(404, {})\n }\n\n /** The part of /v1/auth/* a password sign-in and the sign-in form read. */\n private ownScreen(url: URL, init: RequestInit | undefined): Response {\n const reply = (status: number, body: unknown) =>\n new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' } })\n const refuse = (status: number, code: string, message: string) => reply(status, { errors: [{ code, message }] })\n if (new Headers(init?.headers).get('x-tenant-key') !== this.tenant) return refuse(404, 'tenant_not_found', 'no such workspace')\n const method = init?.method ?? 'GET'\n if (method === 'POST' && url.pathname === '/v1/auth/login') {\n if (this.settings.allow_password === false) return refuse(403, 'password_disabled', 'this workspace does not take passwords: sign in another way')\n const body = JSON.parse(String(init?.body ?? '{}')) as { email?: string; password?: string }\n const user = [...this.users.values()].find((u) => u.email === body.email)\n const password = body.email === undefined ? undefined : this.passwords.get(body.email)\n if (!user || !user.active || password === undefined || password !== body.password) {\n return refuse(401, 'invalid_credentials', 'invalid email or password')\n }\n // A long session only when the person asked and the client allows it.\n const kind = this.settings.persistent_sessions === true ? new Headers(init?.headers).get('x-cauthy-remember') === '1' : undefined\n return reply(200, { data: this.issue(user.sub, kind) })\n }\n if (method === 'POST' && url.pathname === '/v1/auth/register') {\n if (this.settings.allow_signup === false) return refuse(403, 'signups_disabled', 'this workspace is not accepting new accounts')\n if (this.settings.allow_password === false) return refuse(403, 'password_disabled', 'this workspace does not take passwords: sign in another way')\n const body = JSON.parse(String(init?.body ?? '{}')) as { email?: string; password?: string; display_name?: string }\n if (!body.email || !body.password) return refuse(400, 'validation_error', 'email and password are required')\n if ([...this.users.values()].some((u) => u.email === body.email)) return refuse(409, 'email_taken', 'an account with this email exists')\n // With confirm_email the account waits for its address (verify_grace_days 0 in the fake).\n const confirm = this.settings.confirm_email === true\n const sub = `u-${++this.n}`\n this.users.set(sub, { sub, email: body.email, name: body.display_name ?? '', active: !confirm, restricted: false, verified: !confirm })\n this.passwords.set(body.email, body.password)\n return reply(201, { data: { user_id: sub, email: body.email, ...(confirm ? { verification_required: true } : {}) } })\n }\n if (method === 'GET' && url.pathname === '/v1/auth/settings') return reply(200, { data: this.settings })\n if (method === 'GET' && (url.pathname === '/v1/auth/social/providers' || url.pathname === '/v1/auth/sso/connections')) return reply(200, { data: [] })\n if (method === 'GET' && url.pathname === '/v1/auth/webauthn/place') return reply(200, { data: { here: false, on_cauthy: false } })\n return refuse(404, 'not_found', 'not found')\n }\n}\n\n/**\n * The part of Cauthy's admin API the conformance runner's dashboard-switch\n * checks drive: GET and PUT /v1/settings/auth, GET /v1/oauth/clients,\n * PATCH /v1/oauth/clients/{id}, GET /v1/users and DELETE /v1/users/{id}.\n * null when the request is none of them.\n */\nfunction adminAnswer(fake: FakeCauthy, url: URL, init: RequestInit | undefined): Response | null {\n const method = (init?.method ?? 'GET').toUpperCase()\n const route = `${method} ${url.pathname}`\n const client = method === 'PATCH' ? url.pathname.match(/^\\/v1\\/oauth\\/clients\\/([^/]+)$/) : null\n const user = method === 'DELETE' ? url.pathname.match(/^\\/v1\\/users\\/([^/]+)$/) : null\n const known = ['GET /v1/settings/auth', 'PUT /v1/settings/auth', 'GET /v1/oauth/clients', 'GET /v1/users'].includes(route)\n if (!known && !client && !user) return null\n const reply = (status: number, body: unknown) =>\n new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' } })\n const refuse = (status: number, code: string, message: string) => reply(status, { errors: [{ code, message }] })\n const headers = new Headers(init?.headers)\n if (fake.adminToken === '' || headers.get('authorization') !== `Bearer ${fake.adminToken}`) return refuse(401, 'unauthorized', 'invalid or expired token')\n if (headers.get('x-tenant-key') !== fake.tenant) return refuse(404, 'tenant_not_found', 'no such workspace')\n if (route === 'GET /v1/settings/auth') return reply(200, { data: fake.settings })\n if (route === 'PUT /v1/settings/auth') {\n const change = JSON.parse(String(init?.body ?? '{}')) as Record<string, unknown>\n const next = { ...fake.settings }\n for (const key of ADMIN_SETTINGS) if (change[key] !== undefined && change[key] !== null) next[key] = change[key]\n if (next.allow_password === false && next.allow_magic_link !== true && next.allow_passkeys !== true) {\n return refuse(409, 'last_method', 'the change would leave no way to sign in')\n }\n fake.settings = next\n return reply(200, { data: fake.settings })\n }\n const persistent = () => fake.settings.persistent_sessions === true\n if (route === 'GET /v1/oauth/clients') return reply(200, { data: [{ client_id: fake.clientId, allow_persistent_sessions: persistent() }], total_count: 1 })\n if (client) {\n if (decodeURIComponent(client[1]) !== fake.clientId) return refuse(404, 'client_not_found', 'no such client')\n const change = JSON.parse(String(init?.body ?? '{}')) as { allow_persistent_sessions?: unknown }\n if (typeof change.allow_persistent_sessions === 'boolean') fake.settings = { ...fake.settings, persistent_sessions: change.allow_persistent_sessions }\n return reply(200, { data: { client_id: fake.clientId, allow_persistent_sessions: persistent() } })\n }\n if (route === 'GET /v1/users') {\n const q = (url.searchParams.get('q') ?? '').toLowerCase()\n const data = [...fake.users.values()].filter((u) => u.email.toLowerCase().includes(q)).map((u) => ({ id: u.sub, email: u.email, name: u.name }))\n return reply(200, { data, total_count: data.length })\n }\n const id = decodeURIComponent(user![1])\n const gone = fake.users.get(id)\n if (!gone) return refuse(404, 'user_not_found', 'no such user')\n fake.users.delete(id)\n fake.passwords.delete(gone.email)\n return new Response(null, { status: 204 })\n}\n\n/**\n * Serves a FakeCauthy over HTTP on 127.0.0.1, for a process outside this one\n * (the conformance runner) to reach it, as `--cauthy http://127.0.0.1:<port>`.\n */\nexport function serveFakeCauthy(fake: FakeCauthy, port: number): Promise<Server> {\n const server = createServer(async (req, res) => {\n const chunks: Buffer[] = []\n for await (const chunk of req) chunks.push(chunk as Buffer)\n const headers = new Headers()\n for (const [k, v] of Object.entries(req.headers)) if (typeof v === 'string') headers.set(k, v)\n const answer = await fake.fetch(`${fake.issuer}${req.url ?? '/'}`, {\n method: req.method,\n headers,\n ...(chunks.length > 0 ? { body: Buffer.concat(chunks).toString('utf8') } : {}),\n })\n res.writeHead(answer.status, Object.fromEntries(answer.headers))\n res.end(Buffer.from(await answer.arrayBuffer()))\n })\n return new Promise((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, '127.0.0.1', () => resolve(server))\n })\n}\n\n/** A browser as far as cookies go: it keeps what it is told to and sends it back. */\nexport class Browser {\n jar = new Map<string, string>()\n /** `requestHeader: false`: a browser whose client sends no X-Cauthy-Request, as cauthy-auth 0.42's does not. */\n constructor(readonly origin = 'https://app.test', private readonly options: { requestHeader?: boolean } = {}) {}\n\n request(path: string, init: RequestInit = {}): Request {\n const headers = new Headers(init.headers)\n if (this.jar.size > 0) headers.set('cookie', [...this.jar].map(([k, v]) => `${k}=${v}`).join('; '))\n // What a protocol-1 client sends on every mutation.\n const method = (init.method ?? 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'HEAD' && this.options.requestHeader !== false && !headers.has('x-cauthy-request')) {\n headers.set('x-cauthy-request', '1')\n }\n return new Request(this.origin + path, { ...init, headers })\n }\n\n /** Keeps the cookies of a response, as a browser would. */\n take(source: Response | string[]): void {\n const cookies = Array.isArray(source) ? source : source.headers.getSetCookie()\n for (const c of cookies) {\n const [pair, ...attrs] = c.split('; ')\n const i = pair.indexOf('=')\n const name = pair.slice(0, i)\n const value = pair.slice(i + 1)\n if (value === '' || attrs.includes('Max-Age=0')) this.jar.delete(name)\n else this.jar.set(name, value)\n }\n }\n}\n","// SHA-256 (FIPS 180-4), synchronous and dependency-free: CAUTHY_KEY's\n// checksum is checked when createCauthy runs, before any await. Used for\n// nothing else; WebCrypto does every other hash.\nconst K = new Uint32Array([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n])\n\nexport function sha256(data: Uint8Array): Uint8Array {\n const h = new Uint32Array([0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19])\n const len = data.length\n const padded = new Uint8Array(((len + 9 + 63) >> 6) << 6)\n padded.set(data)\n padded[len] = 0x80\n const view = new DataView(padded.buffer)\n view.setUint32(padded.length - 8, Math.floor(len / 0x20000000))\n view.setUint32(padded.length - 4, (len << 3) >>> 0)\n const w = new Uint32Array(64)\n const rotr = (x: number, n: number) => (x >>> n) | (x << (32 - n))\n for (let off = 0; off < padded.length; off += 64) {\n for (let i = 0; i < 16; i++) w[i] = view.getUint32(off + i * 4)\n for (let i = 16; i < 64; i++) {\n const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ (w[i - 15] >>> 3)\n const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10)\n w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0\n }\n let [a, b, c, d, e, f, g, hh] = h\n for (let i = 0; i < 64; i++) {\n const t1 = (hh + (rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)) + ((e & f) ^ (~e & g)) + K[i] + w[i]) >>> 0\n const t2 = ((rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)) + ((a & b) ^ (a & c) ^ (b & c))) >>> 0\n hh = g; g = f; f = e; e = (d + t1) >>> 0; d = c; c = b; b = a; a = (t1 + t2) >>> 0\n }\n h[0] += a; h[1] += b; h[2] += c; h[3] += d; h[4] += e; h[5] += f; h[6] += g; h[7] += hh\n }\n const out = new Uint8Array(32)\n const ov = new DataView(out.buffer)\n for (let i = 0; i < 8; i++) ov.setUint32(i * 4, h[i])\n return out\n}\n","/** How an application is set up to sign its people in with Cauthy. */\nexport interface CauthyOptions {\n /**\n * The application's CAUTHY_KEY, made in Cauthy's dashboard: Cauthy's\n * address, the application's client and its secret, in one value. Default:\n * `CAUTHY_KEY`. With it nothing else is read: the workspace, the system and\n * the application's addresses come from Cauthy (`GET /v1/connect/self`),\n * and passing `issuer`, `clientId`, `clientSecret`, `secret`, `system`,\n * `tenant`, `audience`, `tenantId`, `systemKey` or `rootKey` is refused.\n */\n key?: string\n /** The key being replaced, during a rotation: read, never written with. Default: `CAUTHY_KEY_PREVIOUS`. */\n keyPrevious?: string\n /** Cauthy's public address, e.g. `https://id.example.com`. Default: `CAUTHY_ISSUER`. */\n issuer?: string\n /** The application's OAuth client id. Default: `CAUTHY_CLIENT_ID`. */\n clientId?: string\n /** Its secret, when the client is confidential. Default: `CAUTHY_CLIENT_SECRET`. */\n clientSecret?: string\n /**\n * The key the session cookie is encrypted with: any string of 32 characters\n * or more, kept on the server. Default: `CAUTHY_SECRET`.\n */\n secret?: string\n /**\n * The system this application is, as its organizations are filed under in\n * Cauthy (`\"acme-crm\"`). When set, a session's organizations are that\n * system's. Default: `CAUTHY_SYSTEM`.\n */\n system?: string\n /**\n * The workspace's slug in Cauthy. Needed for the application's own sign-in\n * screens (`signIn.email`, `signUp.email`…), which sign people in through\n * the application's server instead of Cauthy's page. Default: `CAUTHY_TENANT`.\n */\n tenant?: string\n /** Where the routes are mounted. Default: `/api/auth`. */\n basePath?: string\n /**\n * What happens to a POST without `X-Cauthy-Request: 1` (every POST but\n * `/logout`). `required`, the default, answers `403 csrf_required`, as\n * protocol level strict asks. `warn` lets it through and says so once per\n * process: for the one deploy that moves an application whose browser still\n * runs cauthy-auth 0.42, which does not send the header. Remove it once the\n * browser runs cauthy-auth 0.43 or @cauthyhq/js.\n */\n requestHeader?: 'required' | 'warn'\n /**\n * The audience of the access tokens `auth.verifyToken` accepts: the\n * deployment's `JWT_AUDIENCE`, which Cauthy requires its operator to set and\n * has no default for. Default: `CAUTHY_AUDIENCE`.\n */\n audience?: string\n /**\n * The workspace's id (a lowercase UUID) for `auth.verifyToken`. By contract\n * it comes from the link's `tenant_id`; otherwise: this, or `CAUTHY_TENANT_ID`.\n */\n tenantId?: string\n /**\n * The application's own public origin (`https://app.example.com`): the\n * redirect URI, the cookies' `Secure` flag and which forms are its own come\n * from it. Default: `CAUTHY_BASE_URL`, else the origin of each request, with\n * the scheme a proxy names in `x-forwarded-proto`. Set it whenever a proxy\n * also changes the host or the port.\n */\n baseURL?: string\n /** Where a person lands after signing in, when no `return_to` was asked. Default: `/`. */\n afterSignIn?: string\n /** Where a person lands after signing out. Default: `/`. */\n afterSignOut?: string\n /** Name of the session cookie. Default: `cauthy_session`. */\n cookieName?: string\n /**\n * A key the application proves who it is with when it acts on its own\n * (`auth.system`), instead of its secret. Default: `CAUTHY_SYSTEM_KEY` and\n * `CAUTHY_SYSTEM_KEY_ID`.\n */\n systemKey?: import('./system').SystemKey\n /**\n * The application's own pages for what arrives by mail. A link Cauthy mails\n * opens the page named here, with a `token` to give back; with none, it\n * opens Cauthy's own page.\n *\n * signIn where `<SignIn />` is: a sign-in link opens it\n * resetPassword where `<ResetPassword />` is\n * verifyEmail where `<VerifyEmail />` is\n * acceptInvite where `<AcceptInvite />` is: an invitation opens it\n *\n * An invitation sent by `auth.system.members.add` or `auth.system.invite`\n * opens `acceptInvite` only when the application knows its public origin\n * (`baseURL`): that call has no request to read it from.\n */\n pages?: { signIn?: string; resetPassword?: string; verifyEmail?: string; acceptInvite?: string }\n /**\n * The address of the person a request comes from, passed on to Cauthy so\n * its limits on attempts count the person and not this server. Default: the\n * last address in `X-Forwarded-For`, which is the one the proxy in front of\n * the application wrote. With several proxies, or none, give your own.\n */\n clientIp?: (request: Request) => string | undefined\n /** `fetch` to reach Cauthy with. Default: the global one. */\n fetch?: typeof fetch\n /**\n * How long a call to Cauthy may take before it fails, in milliseconds.\n * `system`: the application's own calls (`auth.system`, and the proof its\n * sign-ins carry). Default 2000. `request`: the sign-in screens' calls and\n * the code, refresh and userinfo calls of a session. Default 5000. The\n * push-approval event stream has no deadline.\n */\n timeouts?: { system?: number; request?: number }\n /**\n * Run by contract: the issuer, the workspace, the clients and where the\n * application's keys are published all come from its link in the network's\n * context graph, and none of the CAUTHY_* variables may be set. See\n * \"Connect a system by contract\".\n *\n * createCauthy({ contract: { graph, system: 'acme-crm' }, keys: postgresKeyStore(query) })\n */\n contract?: import('./contract').ContractOptions\n /** Where the application keeps its own key, with a contract (@cauthyhq/node/keys). */\n keys?: import('./keys').KeyStore\n /**\n * The one secret of a contracted application: every key it needs is\n * derived from it. 32 random bytes or more. Default: `PACT_ROOT_KEY`.\n */\n rootKey?: string\n /** The root key before the current one, while rotating it. Default: `PACT_ROOT_KEY_PREVIOUS`. */\n rootKeyPrevious?: string\n /**\n * Where `auth.webhook` remembers the deliveries it took, so one sent twice\n * is refused. Default: this process's memory. Give a shared one (a table)\n * when the application runs on several instances.\n */\n webhookReplay?: import('./webhook').ReplayStore\n}\n\n/** The person signed in. Every moment is UTC. */\nexport interface SessionUser {\n id: string\n email: string\n name: string\n emailVerified: boolean\n /**\n * True when the account ran out of time to confirm its address: let it read\n * and refuse its writes.\n */\n restricted: boolean\n /** When an unconfirmed address stops being enough (ISO 8601, UTC), if there is a deadline. */\n emailVerifyBy?: string\n /** The language the person reads in (\"es\", \"en-US\"), when Cauthy knows it. */\n locale?: string\n /** A guest: a visitor's session before they sign up. No email; `account.upgrade` makes it an account, keeping `id`. */\n isAnonymous?: boolean\n}\n\n/** A value JSON can carry. What crosses from server to browser is typed as this, never `unknown`. */\nexport type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }\n\n/** An organization the person belongs to, with their role in it. */\nexport interface SessionOrganization {\n id: string\n system: string\n name: string\n kind: string\n externalId?: string\n role: string\n /** What the system keeps about the organization, as it filed it in Cauthy. */\n metadata: Record<string, JsonValue>\n /**\n * What the person may do here, as \"resource:action\" entries (\"*\" is any):\n * their role's permissions, what it inherits, and anything given to them\n * apart from it. Absent when the system keeps no catalog of roles in Cauthy.\n * Check with `can(organization, resource, action)`.\n */\n permissions?: string[]\n /** What the person is denied here, whatever else allows it. */\n denied?: string[]\n /**\n * Permissions that hold only when their condition does (an amount under a\n * limit, the person's own records…). They are in neither list above: `can`\n * answers no for them, and `auth.check` asks Cauthy. Present, maybe empty,\n * whenever `permissions` is.\n */\n conditional?: ConditionalPermission[]\n}\n\n/** A permission with a condition: Cauthy decides it per question (`auth.check`). */\nexport interface ConditionalPermission {\n resource: string\n action: string\n effect: 'allow' | 'deny'\n}\n\n/** What to ask Cauthy about: an action on a resource, with what the conditions read of it. */\nexport interface CheckQuestion {\n resource: string\n action: string\n /** What a condition reads as `resource.attrs`: `{ amount: 500 }`. */\n attributes?: Record<string, unknown>\n /** The record's id, as a condition reads it (`resource.id`). */\n resourceId?: string\n /** Whose the record is (`resource.owner_id`): `resource.owner_id == subject.id`. */\n ownerId?: string\n}\n\n/** An organization, by its id or as the session has it. */\nexport type OrganizationRef = string | { id: string }\n\n/** The rule that decided a check. */\nexport interface CheckRule {\n /** A permission of the person's role, or an exception given to the person. */\n source: 'role' | 'exception'\n /** The role, when `source` is `role`. */\n role?: string\n kind: string\n resource: string\n action: string\n effect: 'allow' | 'deny'\n condition?: string\n}\n\n/** Cauthy's answer to a check. */\nexport interface CheckResult {\n allowed: boolean\n /** Why, in words: \"allowed by role accountant: invoices:approve\". */\n reason: string\n /** The rule that decided; absent when none did (nothing allows it). */\n rule?: CheckRule\n}\n\n/** A signed-in person, as the server sees them. */\nexport interface Session {\n user: SessionUser\n /** The Cauthy tenant the account is of. */\n tenantId: string\n /** The roles the account holds in that tenant. */\n roles: string[]\n /**\n * Cauthy's access token for this person: send it as `Authorization: Bearer`\n * to another service that verifies Cauthy's tokens. Never give it to the browser.\n */\n accessToken: string\n /** When the access token expires and the session is checked with Cauthy again (ISO 8601, UTC). */\n expiresAt: string\n}\n\n/** What the browser is told of a session: who, and their organizations — never a token. */\nexport interface PublicSession {\n user: SessionUser\n tenantId: string\n roles: string[]\n organizations: SessionOrganization[]\n expiresAt: string\n}\n\n/** Why there is no session. */\nexport type SessionStatus =\n | 'authenticated'\n | 'unauthenticated' // nobody is signed in\n | 'expired' // signed in, but the session must be renewed first (see `Cauthy.resolve`)\n\nexport class CauthyConfigError extends Error {\n constructor(message: string) {\n super(`@cauthyhq/node: ${message}`)\n this.name = 'CauthyConfigError'\n }\n}\n\n/** Why a CAUTHY_KEY was refused (key/cauthy-key.md §3, §8, §9). */\nexport type CauthyKeyReason = 'key_format' | 'key_version' | 'key_checksum' | 'key_issuer' | 'key_client' | 'key_secret'\n | 'key_contract_issuer' | 'key_previous_mismatch'\n\n/** A refused CAUTHY_KEY. The message names the reason, never the key. */\nexport class CauthyKeyError extends CauthyConfigError {\n constructor(readonly reason: CauthyKeyReason, message: string) {\n super(`CAUTHY_KEY: ${message} (${reason})`)\n this.name = 'CauthyKeyError'\n }\n}\n","// CAUTHY_KEY (key/cauthy-key.md in cauthy-sdk-spec): parsing and composing,\n// synchronously. Derivation and signing are in keyderive.ts.\nimport { sha256 } from './sha256'\nimport { CauthyConfigError, CauthyKeyError, type CauthyKeyReason } from './types'\n\nexport const KEY_PREFIX = 'cauthy1'\n\n/**\n * The variables CAUTHY_KEY replaces: set beside it, each is a second source\n * of the same thing, and the application refuses to start (key/cauthy-key.md §10).\n */\nexport const KEY_REPLACES = ['CAUTHY_ISSUER', 'CAUTHY_CLIENT_ID', 'CAUTHY_CLIENT_SECRET', 'CAUTHY_SYSTEM', 'CAUTHY_TENANT',\n 'CAUTHY_SYSTEM_KEY', 'CAUTHY_SYSTEM_KEY_ID', 'CAUTHY_BASE_URL', 'CAUTHY_AUDIENCE', 'CAUTHY_TENANT_ID'] as const\n\n/** Names the KEY_REPLACES variables set in `environment`, refusing them beside CAUTHY_KEY. */\nexport function refuseBesideKey(environment: Record<string, string | undefined> | undefined): void {\n const set = KEY_REPLACES.filter(n => (environment?.[n] ?? '') !== '')\n if (set.length > 0) {\n throw new CauthyConfigError(`the application runs on CAUTHY_KEY, and ${set.join(', ')} ${set.length === 1 ? 'is' : 'are'} also set: ` +\n `remove ${set.length === 1 ? 'it' : 'them'} from the environment. Everything comes from the key.`)\n }\n}\nconst MAX_LEN = 512\nconst enc = new TextEncoder()\nconst dec = new TextDecoder('utf-8', { fatal: true })\n\n/** What a CAUTHY_KEY names. Never logged, never printed. */\nexport interface CauthyKey {\n /** Cauthy's origin, e.g. https://cauthy.api.example.com */\n issuer: string\n clientId: string\n /** The 43-character secret segment: the HKDF input, as text. */\n secret: string\n}\n\nexport function b64uEncode(b: Uint8Array): string {\n let bin = ''\n for (const x of b) bin += String.fromCharCode(x)\n return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\n/** Strict base64url: no padding, alphabet only, canonical. Null when it is not. */\nexport function b64uDecode(s: string): Uint8Array | null {\n if (!/^[A-Za-z0-9_-]*$/.test(s) || s.length % 4 === 1) return null\n const bin = atob(s.replace(/-/g, '+').replace(/_/g, '/') + '='.repeat((4 - (s.length % 4)) % 4))\n const out = Uint8Array.from(bin, c => c.charCodeAt(0))\n return b64uEncode(out) === s ? out : null\n}\n\nfunction checksum(body: string): string {\n return b64uEncode(sha256(enc.encode(body)).slice(0, 6))\n}\n\nfunction printableAscii(s: string): boolean {\n return /^[\\x21-\\x7e]*$/.test(s)\n}\n\n/**\n * An origin as key/cauthy-key.md §3 rule 5 accepts it: exactly as a WHATWG\n * URL serializes its origin, https (http only on loopback), never an\n * IPv4-mapped IPv6 address.\n */\nexport function validIssuer(s: string): boolean {\n if (s === '' || !printableAscii(s)) return false\n let u: URL\n try { u = new URL(s) } catch { return false }\n if (u.origin !== s) return false\n if (/^\\[::ffff:/i.test(u.host)) return false\n if (u.protocol === 'https:') return true\n return u.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(u.hostname)\n}\n\nconst fail = (reason: CauthyKeyReason, message: string): never => {\n throw new CauthyKeyError(reason, message)\n}\n\nconst NOT_A_KEY = 'not a Cauthy key (copy it again from the dashboard)'\n\n/** Reads a CAUTHY_KEY; throws CauthyKeyError with the reason, never the value. */\nexport function parseKey(text: string): CauthyKey {\n const s = text.replace(/^[ \\t\\r\\n]+|[ \\t\\r\\n]+$/g, '')\n if (s === '' || s.length > MAX_LEN || !printableAscii(s)) fail('key_format', NOT_A_KEY)\n const head = s.split('.', 1)[0]\n if (head !== KEY_PREFIX) {\n if (/^cauthy[0-9]+$/.test(head)) fail('key_version', `this key is for a newer SDK (${head}): update @cauthyhq/node`)\n fail('key_format', NOT_A_KEY)\n }\n const parts = s.split('.')\n if (parts.length !== 5 || parts.some(p => p === '')) fail('key_format', NOT_A_KEY)\n if (!constantTimeEqual(checksum(parts.slice(0, 4).join('.')), parts[4])) {\n fail('key_checksum', 'the key is damaged (part of it is missing or changed): copy it again from the dashboard')\n }\n const issuerBytes = b64uDecode(parts[1])\n let issuer = ''\n try { issuer = issuerBytes ? dec.decode(issuerBytes) : '' } catch { issuer = '' }\n if (!validIssuer(issuer)) fail('key_issuer', 'the key names no valid Cauthy address')\n const clientBytes = b64uDecode(parts[2])\n const clientId = clientBytes ? String.fromCharCode(...clientBytes) : ''\n if (!/^[A-Za-z0-9._-]{1,128}$/.test(clientId)) fail('key_client', 'the key names no valid client')\n const secret = b64uDecode(parts[3])\n if (parts[3].length !== 43 || !secret || secret.length !== 32) fail('key_secret', 'the key\\'s secret is not 32 bytes')\n return { issuer, clientId, secret: parts[3] }\n}\n\n/** Writes a CAUTHY_KEY (what the dashboard does in the browser; here for tests and tools). */\nexport function composeKey(issuer: string, clientId: string, secret: string): string {\n const body = `${KEY_PREFIX}.${b64uEncode(enc.encode(issuer))}.${b64uEncode(enc.encode(clientId))}.${secret}`\n const key = `${body}.${checksum(body)}`\n parseKey(key) // refuses what a reader would\n return key\n}\n\nfunction constantTimeEqual(a: string, b: string): boolean {\n if (a.length !== b.length) return false\n let d = 0\n for (let i = 0; i < a.length; i++) d |= a.charCodeAt(i) ^ b.charCodeAt(i)\n return d === 0\n}\n","// One secret, many keys.\n//\n// A contracted application keeps one secret in its environment, its root key\n// (PACT_ROOT_KEY). Each job gets a key of its own derived from it with HKDF\n// and a label, so no key does two jobs:\n//\n// pact/seal/v1 seals the application's private key at rest\n// pact/cookie/v1 encrypts the session cookie\n// pact/flow/v1 encrypts what a sign-in carries between its steps\n//\n// Rotating the root: set the new one as PACT_ROOT_KEY and the old one as\n// PACT_ROOT_KEY_PREVIOUS for a deploy. What is written uses the new root;\n// what is read is tried with the new root, then the previous one. Sessions\n// survive, and `auth.pact.keys.reseal()` seals the private key again with the\n// new root. Then the previous one is removed.\n\nimport { CauthyConfigError } from './types'\n\n/** What each derived key is for. */\nexport type KeyPurpose = 'seal' | 'cookie' | 'flow'\n\nconst encoder = new TextEncoder()\n\n/** The least a root key may be: 32 characters, as 32 random bytes in base64url are 43. */\nexport const MIN_ROOT_KEY = 32\n\n/** The keys of one purpose: the first writes, every one is tried to read. */\nexport type KeyRing = Uint8Array[]\n\nasync function derive(root: string, purpose: KeyPurpose): Promise<Uint8Array> {\n const ikm = await crypto.subtle.importKey('raw', encoder.encode(root), 'HKDF', false, ['deriveBits'])\n const bits = await crypto.subtle.deriveBits(\n { name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(0), info: encoder.encode(`pact/${purpose}/v1`) }, ikm, 256)\n return new Uint8Array(bits)\n}\n\n/** The keys a root key (and the one before it, while rotating) stand for. */\nexport interface DerivedKeys {\n seal: Promise<KeyRing>\n cookie: Promise<KeyRing>\n flow: Promise<KeyRing>\n}\n\nexport function deriveKeys(root: string, previous?: string): DerivedKeys {\n if (root.length < MIN_ROOT_KEY) {\n throw new CauthyConfigError(`rootKey (PACT_ROOT_KEY) must be at least ${MIN_ROOT_KEY} characters: 32 random bytes, e.g. openssl rand -base64 32`)\n }\n if (previous !== undefined && previous !== '' && previous.length < MIN_ROOT_KEY) {\n throw new CauthyConfigError(`the previous root key (PACT_ROOT_KEY_PREVIOUS) must be at least ${MIN_ROOT_KEY} characters`)\n }\n const roots = previous && previous !== root ? [root, previous] : [root]\n const ring = (purpose: KeyPurpose) => Promise.all(roots.map(r => derive(r, purpose)))\n return { seal: ring('seal'), cookie: ring('cookie'), flow: ring('flow') }\n}\n","// Everything a CAUTHY_KEY stands for (key/cauthy-key.md §4–§6): the cookie\n// and flow rings (rootkey.ts's derivation, with the secret segment as the\n// root) and the Ed25519 key the application proves itself with.\n//\n// The assertion is written by hand, member by member, as §6 fixes it: the\n// vectors pin it byte for byte. WebCrypto signs; nothing here needs node:crypto.\nimport { b64uEncode, type CauthyKey } from './cauthykey'\nimport { deriveKeys, type KeyRing } from './rootkey'\n\nconst enc = new TextEncoder()\nconst PKCS8_ED25519 = Uint8Array.from([0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20])\n\nexport const ASSERTION_TYPE = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'\n\nexport type PublicJwk = { kty: 'OKP'; crv: 'Ed25519'; x: string }\n\nexport interface Signer {\n kid: string\n publicJwk: PublicJwk\n /** §6's assertion. iat in seconds; jti a fresh UUID v4. */\n assertion(clientId: string, tokenEndpoint: string, jti?: string, iat?: number): Promise<string>\n}\n\nexport interface KeyDerived {\n cookie: Promise<KeyRing>\n flow: Promise<KeyRing>\n signer: Promise<Signer>\n /** The previous key's public JWK, published during a rotation (contract). */\n previousJwk?: Promise<PublicJwk & { kid: string }>\n}\n\nasync function seedOf(secret: string): Promise<Uint8Array> {\n const ikm = await crypto.subtle.importKey('raw', enc.encode(secret), 'HKDF', false, ['deriveBits'])\n return new Uint8Array(await crypto.subtle.deriveBits(\n { name: 'HKDF', hash: 'SHA-256', salt: new Uint8Array(0), info: enc.encode('cauthy/sign/v1') }, ikm, 256))\n}\n\n/** RFC 7638 thumbprint of an Ed25519 public key. */\nexport async function thumbprint(x: string): Promise<string> {\n return b64uEncode(new Uint8Array(await crypto.subtle.digest('SHA-256', enc.encode(`{\"crv\":\"Ed25519\",\"kty\":\"OKP\",\"x\":\"${x}\"}`))))\n}\n\nasync function signerOf(secret: string): Promise<Signer> {\n const seed = await seedOf(secret)\n const der = new Uint8Array(PKCS8_ED25519.length + 32)\n der.set(PKCS8_ED25519)\n der.set(seed, PKCS8_ED25519.length)\n // Extractable only to read x; the key that signs from here on is not.\n const readable = await crypto.subtle.importKey('pkcs8', der, { name: 'Ed25519' }, true, ['sign'])\n const { x } = await crypto.subtle.exportKey('jwk', readable)\n const signing = await crypto.subtle.importKey('pkcs8', der, { name: 'Ed25519' }, false, ['sign'])\n der.fill(0)\n seed.fill(0)\n const publicJwk: PublicJwk = { kty: 'OKP', crv: 'Ed25519', x: x as string }\n const kid = await thumbprint(publicJwk.x)\n const header = b64uEncode(enc.encode(JSON.stringify({ alg: 'EdDSA', kid })))\n return {\n kid,\n publicJwk,\n async assertion(clientId, tokenEndpoint, jti = crypto.randomUUID(), iat = Math.floor(Date.now() / 1000)) {\n const claims = { iss: clientId, sub: clientId, aud: tokenEndpoint, jti, iat, exp: iat + 120 }\n const input = `${header}.${b64uEncode(enc.encode(JSON.stringify(claims)))}`\n const sig = new Uint8Array(await crypto.subtle.sign({ name: 'Ed25519' }, signing, enc.encode(input)))\n return `${input}.${b64uEncode(sig)}`\n },\n }\n}\n\n/** The rings and the signer of a key, and of the one it replaces during a rotation. */\nexport function deriveFromKey(key: CauthyKey, previous?: CauthyKey): KeyDerived {\n const other = previous && previous.secret !== key.secret ? previous : undefined\n const rings = deriveKeys(key.secret, other?.secret)\n return {\n cookie: rings.cookie,\n flow: rings.flow,\n signer: signerOf(key.secret),\n ...(other ? { previousJwk: signerOf(other.secret).then(s => ({ ...s.publicJwk, kid: s.kid })) } : {}),\n }\n}\n"],"mappings":"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","names":["enc","dec","sub"]}
|