@atlasauth/pca-fetch 0.0.0-stage → 0.2.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Atlas Authorization
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,51 @@
1
- # Temporary Holding Version
1
+ # @atlasauth/pca-fetch
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Runtime-neutral Web Fetch guard for Proof-Carrying Authority. It verifies an inbound PCActn in any `Request -> Response` runtime (Cloudflare Workers, Deno, Bun, Vercel Edge, Netlify, a Lambda Function URL) using only the web platform, with no Node APIs. It wraps the `requirePCA` guard from `@atlasauth/pca`, which is default-deny: a required check that is not enforced denies.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ npm i @atlasauth/pca-fetch
9
+ ```
10
+
11
+ Depends on `@atlasauth/pca`.
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import { withPCA } from '@atlasauth/pca-fetch';
17
+ import { memoryPcaStore } from '@atlasauth/pca';
18
+
19
+ export default {
20
+ fetch: withPCA(
21
+ {
22
+ audience: 'ins_acme', // the audience a PCActn must name
23
+ resolveGrant: async (grantRef) => grants.get(grantRef) ?? null, // your root-grant store
24
+ budgetStore: memoryPcaStore(), // replay counter + budget; use a shared store in production
25
+ hooks: { revocation: myRevocationChecker }, // e.g. built with createRevocationChecker from @atlasauth/pca
26
+ context: async (req, pcactn) => ({
27
+ plan: knownPlan(pcactn), // the plan your server knows for this action
28
+ planAuthorized: true, // only if the principal authorized that plan root
29
+ params: { amount: 20, currency: 'usd' },
30
+ risk: { reversibility: 1, blastRadius: 0, confidence: 1, semanticDistance: 0, taint: 0 },
31
+ budget: { B: 500, tau: 0, asOf: Date.now() },
32
+ }),
33
+ },
34
+ async (req, { pca }) => Response.json({ ok: true, verb: pca.pcactn.action.verb }),
35
+ ),
36
+ };
37
+ ```
38
+
39
+ On success your handler runs with `{ pca: { verdict, pcactn } }`. Otherwise a `401` or `403` `Response` is returned with a `WWW-Authenticate: PCA realm=...` challenge and a JSON body (shape it with `denyBody`). `createPcaFetchGuard(opts)` returns the raw guard result if you want to compose it into your own router.
40
+
41
+ The client sends the PCActn as a `PCA-Action: <base64url>` header (see `pcaHeaders` in `@atlasauth/pca`) or a JSON body `{ pcactn }`. By default the following checks must pass or the request is denied: `counter` (needs a `budgetStore`), `revocation` (needs `hooks.revocation`), `plan_root_authorized` (needs `context()` returning `planAuthorized: true`), plus audience, validity and grant binding. Options are those of `requirePCA`; see `@atlasauth/pca`. `insecureAllowUnenforced: true` disables the default-deny profile and is for local development only.
42
+
43
+ Nothing here authorizes on its own: the verifier decides, not the client's proof.
44
+
45
+ ## Status
46
+
47
+ Experimental. Part of Proof-Carrying Authority; the cryptography is unaudited.
48
+
49
+ ## License
50
+
51
+ MIT - see LICENSE
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Runtime-neutral Web Fetch guard for Proof-Carrying Authority.
3
+ *
4
+ * Works in any `Request → Response` runtime — Cloudflare Workers, Deno, Bun, Vercel Edge, Netlify, a
5
+ * Lambda Function URL — using only the web platform (no Node APIs), so the same code protects a
6
+ * resource server wherever it runs on the internet:
7
+ *
8
+ * export default { fetch: withPCA({ audience, resolveGrant, context }, async (req, { pca }) => {
9
+ * return Response.json({ ok: true, verdict: pca.verdict });
10
+ * }) };
11
+ *
12
+ * It verifies the inbound PCActn with the framework-agnostic `requirePCA` guard from
13
+ * `@atlasauth/pca` (default-deny) and, on success, calls your handler with the verdict + PCActn;
14
+ * otherwise it returns a 401/403 `Response` with a WWW-Authenticate challenge. The client sends the
15
+ * PCActn as `PCA-Action: <base64url>` (see `@atlasauth/pca` `pcaHeaders`) or a JSON body `{ pcactn }`.
16
+ * Nothing here authorizes on its own — the resource server's verifier decides.
17
+ */
18
+ import { requirePCA, defaultExtract, memoryPcaStore } from '@atlasauth/pca';
19
+ import type { PcaGuardResult, RequirePcaOptions, PcaStateStore } from '@atlasauth/pca';
20
+ import type { PCActn } from '@atlasauth/pca';
21
+ /** The verdict + PCActn handed to a guarded handler on success. */
22
+ export interface PcaContextValue {
23
+ verdict: Extract<PcaGuardResult, {
24
+ ok: true;
25
+ }>['verdict'];
26
+ pcactn: PCActn;
27
+ }
28
+ export interface PcaFetchOptions extends RequirePcaOptions {
29
+ /** Shape the 401/403 body (default: `{ error, verdict }`). */
30
+ denyBody?: (result: Extract<PcaGuardResult, {
31
+ ok: false;
32
+ }>) => unknown;
33
+ }
34
+ /**
35
+ * Build a verifier over a web `Request`. Returns the full guard result so you can compose it yourself
36
+ * (e.g. inside an existing router). Use `withPCA` for the common wrap-a-handler case.
37
+ */
38
+ export declare function createPcaFetchGuard(options: PcaFetchOptions): (req: Request) => Promise<PcaGuardResult>;
39
+ export type PcaFetchHandler = (req: Request, ctx: {
40
+ pca: PcaContextValue;
41
+ }) => Response | Promise<Response>;
42
+ /** Wrap a Fetch handler so the inbound PCActn is verified first; on success your handler runs with the verdict. */
43
+ export declare function withPCA(options: PcaFetchOptions, handler: PcaFetchHandler): (req: Request) => Promise<Response>;
44
+ export { requirePCA, defaultExtract, memoryPcaStore };
45
+ export type { PcaGuardResult, RequirePcaOptions, PcaStateStore };
package/dist/index.js ADDED
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * Runtime-neutral Web Fetch guard for Proof-Carrying Authority.
4
+ *
5
+ * Works in any `Request → Response` runtime — Cloudflare Workers, Deno, Bun, Vercel Edge, Netlify, a
6
+ * Lambda Function URL — using only the web platform (no Node APIs), so the same code protects a
7
+ * resource server wherever it runs on the internet:
8
+ *
9
+ * export default { fetch: withPCA({ audience, resolveGrant, context }, async (req, { pca }) => {
10
+ * return Response.json({ ok: true, verdict: pca.verdict });
11
+ * }) };
12
+ *
13
+ * It verifies the inbound PCActn with the framework-agnostic `requirePCA` guard from
14
+ * `@atlasauth/pca` (default-deny) and, on success, calls your handler with the verdict + PCActn;
15
+ * otherwise it returns a 401/403 `Response` with a WWW-Authenticate challenge. The client sends the
16
+ * PCActn as `PCA-Action: <base64url>` (see `@atlasauth/pca` `pcaHeaders`) or a JSON body `{ pcactn }`.
17
+ * Nothing here authorizes on its own — the resource server's verifier decides.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.memoryPcaStore = exports.defaultExtract = exports.requirePCA = void 0;
21
+ exports.createPcaFetchGuard = createPcaFetchGuard;
22
+ exports.withPCA = withPCA;
23
+ const pca_1 = require("@atlasauth/pca");
24
+ Object.defineProperty(exports, "requirePCA", { enumerable: true, get: function () { return pca_1.requirePCA; } });
25
+ Object.defineProperty(exports, "defaultExtract", { enumerable: true, get: function () { return pca_1.defaultExtract; } });
26
+ Object.defineProperty(exports, "memoryPcaStore", { enumerable: true, get: function () { return pca_1.memoryPcaStore; } });
27
+ /** Adapt a web `Request` to the framework-agnostic `PcaRequestLike` (headers getter + parsed JSON body). */
28
+ async function toPcaRequest(req) {
29
+ let body;
30
+ try {
31
+ // clone so the handler can still read the body; ignore non-JSON bodies.
32
+ body = await req.clone().json();
33
+ }
34
+ catch {
35
+ body = undefined;
36
+ }
37
+ return { headers: { get: (name) => req.headers.get(name) }, body };
38
+ }
39
+ function denyResponse(result, denyBody) {
40
+ const body = denyBody ? denyBody(result) : { error: result.verdict.reasons?.[0] ?? 'forbidden', verdict: result.verdict };
41
+ const headers = { 'content-type': 'application/json' };
42
+ if (result.wwwAuthenticate)
43
+ headers['WWW-Authenticate'] = result.wwwAuthenticate;
44
+ return new Response(JSON.stringify(body), { status: result.status, headers });
45
+ }
46
+ /**
47
+ * Build a verifier over a web `Request`. Returns the full guard result so you can compose it yourself
48
+ * (e.g. inside an existing router). Use `withPCA` for the common wrap-a-handler case.
49
+ */
50
+ function createPcaFetchGuard(options) {
51
+ const guard = (0, pca_1.requirePCA)(options);
52
+ return async (req) => guard(await toPcaRequest(req));
53
+ }
54
+ /** Wrap a Fetch handler so the inbound PCActn is verified first; on success your handler runs with the verdict. */
55
+ function withPCA(options, handler) {
56
+ const guard = createPcaFetchGuard(options);
57
+ return async (req) => {
58
+ const result = await guard(req);
59
+ if (result.ok)
60
+ return handler(req, { pca: { verdict: result.verdict, pcactn: result.pcactn } });
61
+ return denyResponse(result, options.denyBody);
62
+ };
63
+ }
64
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG;;;AAwCH,kDAGC;AAKD,0BAOC;AArDD,wCAA4E;AAuDnE,2FAvDA,gBAAU,OAuDA;AAAE,+FAvDA,oBAAc,OAuDA;AAAE,+FAvDA,oBAAc,OAuDA;AAxCnD,4GAA4G;AAC5G,KAAK,UAAU,YAAY,CAAC,GAAY;IACtC,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACH,wEAAwE;QACxE,IAAI,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,GAAG,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC;AAC7E,CAAC;AAED,SAAS,YAAY,CAAC,MAA8C,EAAE,QAAsC;IAC1G,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;IAC1H,MAAM,OAAO,GAA2B,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC;IAC/E,IAAI,MAAM,CAAC,eAAe;QAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,MAAM,CAAC,eAAe,CAAC;IACjF,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AAChF,CAAC;AAED;;;GAGG;AACH,SAAgB,mBAAmB,CAAC,OAAwB;IAC1D,MAAM,KAAK,GAAG,IAAA,gBAAU,EAAC,OAAO,CAAC,CAAC;IAClC,OAAO,KAAK,EAAE,GAAY,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;AAChE,CAAC;AAID,mHAAmH;AACnH,SAAgB,OAAO,CAAC,OAAwB,EAAE,OAAwB;IACxE,MAAM,KAAK,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC3C,OAAO,KAAK,EAAE,GAAY,EAAqB,EAAE;QAC/C,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,MAAM,CAAC,EAAE;YAAE,OAAO,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAChG,OAAO,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChD,CAAC,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,58 @@
1
1
  {
2
2
  "name": "@atlasauth/pca-fetch",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.2.0",
4
+ "description": "Runtime-neutral Web Fetch guard for Proof-Carrying Authority: verify an inbound PCActn in any Request→Response runtime (Cloudflare Workers, Deno, Bun, Vercel Edge, Lambda).",
5
+ "keywords": [
6
+ "web fetch",
7
+ "cloudflare workers",
8
+ "deno",
9
+ "bun",
10
+ "edge",
11
+ "agent authorization",
12
+ "proof-carrying authority",
13
+ "pca",
14
+ "ai agents"
15
+ ],
16
+ "license": "MIT",
17
+ "homepage": "https://github.com/Atlas-Authorization/pca#readme",
18
+ "bugs": {
19
+ "url": "https://github.com/Atlas-Authorization/pca/issues"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/Atlas-Authorization/pca.git",
24
+ "directory": "packages/pca-fetch"
25
+ },
26
+ "main": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "default": "./dist/index.js"
32
+ },
33
+ "./package.json": "./package.json"
34
+ },
35
+ "dependencies": {
36
+ "@atlasauth/pca": "0.2.0"
37
+ },
38
+ "devDependencies": {
39
+ "typescript": "^5.6.3",
40
+ "vitest": "^2.1.4"
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "README.md",
45
+ "LICENSE"
46
+ ],
47
+ "engines": {
48
+ "node": ">=20"
49
+ },
50
+ "publishConfig": {
51
+ "access": "public"
52
+ },
53
+ "scripts": {
54
+ "build": "tsc -p tsconfig.build.json",
55
+ "typecheck": "tsc -p tsconfig.json --noEmit",
56
+ "test": "vitest run"
57
+ }
6
58
  }