@atlasauth/pca-fetch 0.0.0-stage → 0.1.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/README.md +36 -2
- package/dist/index.d.ts +45 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/package.json +31 -3
package/README.md
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @atlasauth/pca-fetch
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Runtime-neutral Web **Fetch** guard for Proof-Carrying Authority — verify an inbound PCActn in any
|
|
4
|
+
`Request → Response` runtime: Cloudflare Workers, Deno, Bun, Vercel Edge, Netlify, a Lambda Function
|
|
5
|
+
URL. Web platform only, no Node APIs.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm i @atlasauth/pca-fetch
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { withPCA } from '@atlasauth/pca-fetch';
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
fetch: withPCA(
|
|
20
|
+
{
|
|
21
|
+
audience: 'ins_acme',
|
|
22
|
+
resolveGrant: async (grantRef) => grants.get(grantRef), // your grant store
|
|
23
|
+
context: async () => ({ params: { amount: 20, currency: 'usd' }, budget: { B: 500, tau: 0 }, planAuthorized: true }),
|
|
24
|
+
},
|
|
25
|
+
async (req, { pca }) => Response.json({ ok: true, verdict: pca.verdict }),
|
|
26
|
+
),
|
|
27
|
+
};
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
On success your handler runs with `{ pca: { verdict, pcactn } }`; otherwise a `401/403` `Response`
|
|
31
|
+
with a `WWW-Authenticate` challenge is returned. `createPcaFetchGuard(opts)` returns the raw guard
|
|
32
|
+
result if you want to compose it into your own router. The client sends the PCActn as
|
|
33
|
+
`PCA-Action: <base64url>` or a JSON body `{ pcactn }`.
|
|
34
|
+
|
|
35
|
+
Nothing here authorizes on its own — the resource server's verifier (default-deny) decides.
|
|
36
|
+
|
|
37
|
+
Part of Proof-Carrying Authority — see [`@atlasauth/pca`](../pca).
|
package/dist/index.d.ts
ADDED
|
@@ -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/backend` (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/backend';
|
|
19
|
+
import type { PcaGuardResult, RequirePcaOptions, PcaStateStore } from '@atlasauth/backend';
|
|
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/backend` (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 backend_1 = require("@atlasauth/backend");
|
|
24
|
+
Object.defineProperty(exports, "requirePCA", { enumerable: true, get: function () { return backend_1.requirePCA; } });
|
|
25
|
+
Object.defineProperty(exports, "defaultExtract", { enumerable: true, get: function () { return backend_1.defaultExtract; } });
|
|
26
|
+
Object.defineProperty(exports, "memoryPcaStore", { enumerable: true, get: function () { return backend_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, backend_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,gDAAgF;AAuDvE,2FAvDA,oBAAU,OAuDA;AAAE,+FAvDA,wBAAc,OAuDA;AAAE,+FAvDA,wBAAc,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,oBAAU,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,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlasauth/pca-fetch",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.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
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@atlasauth/pca": "0.1.0",
|
|
16
|
+
"@atlasauth/backend": "0.3.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"typescript": "^5.6.3",
|
|
20
|
+
"vitest": "^2.1.4"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc -p tsconfig.build.json",
|
|
31
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
32
|
+
"test": "vitest run"
|
|
33
|
+
}
|
|
6
34
|
}
|