@atlasauth/pca-next 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 +21 -0
- package/README.md +52 -2
- package/dist/index.d.ts +42 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -3
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,53 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @atlasauth/pca-next
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Next.js (App Router) helper for Proof-Carrying Authority (PCA). `withPCA` wraps a Route Handler so the inbound PCActn is verified with the resource-server guard from `@atlasauth/pca` before your handler runs; otherwise it returns a 401/403 JSON response with a `WWW-Authenticate` challenge. Verification is default-deny.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm i @atlasauth/pca-next @atlasauth/pca
|
|
9
|
+
npm i next # optional peer dependency (^14 || ^15)
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Usage
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
// app/refunds/route.ts
|
|
16
|
+
import { withPCA } from '@atlasauth/pca-next';
|
|
17
|
+
import { memoryPcaStore } from '@atlasauth/pca';
|
|
18
|
+
|
|
19
|
+
export const POST = withPCA(
|
|
20
|
+
{
|
|
21
|
+
audience: 'ins_acme', // this resource server's id; the PCActn's signed `aud` must match
|
|
22
|
+
resolveGrant: async (ref) => grants.get(ref) ?? null, // look up the Root Intent Grant by grant_ref
|
|
23
|
+
budgetStore: memoryPcaStore(), // replay counter store (use a durable one in production)
|
|
24
|
+
hooks: { revocation: async () => ({ enforced: true, ok: !(await isRevoked()) }) }, // revocation checker
|
|
25
|
+
context: async (req, pcactn) => ({ // facts only the resource server can vouch for
|
|
26
|
+
params: { amount: 20, currency: 'usd' },
|
|
27
|
+
plan: authorizedPlanNodes, // your copy of the principal-authorized plan
|
|
28
|
+
planAuthorized: true,
|
|
29
|
+
budget: { B: 500, tau: 0 },
|
|
30
|
+
risk: { blastRadius: 0.04 }, // risk inputs you can vouch for (missing ones fail closed to worst case)
|
|
31
|
+
}),
|
|
32
|
+
},
|
|
33
|
+
async (req, { pca }) => {
|
|
34
|
+
const { verdict, pcactn } = pca; // handler only runs for a valid PCActn
|
|
35
|
+
return Response.json({ ok: true });
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The client sends the PCActn as a `PCA-Action: <base64url>` header (see `pcaHeaders` in `@atlasauth/pca`) or as a JSON body `{ pcactn }`; the default extractor reads both. The wrapper reads a clone of the request body, so your handler can still read it. By default a deployment must supply `budgetStore`, a `hooks.revocation` checker, and a `context()` returning the server's own `plan` and `planAuthorized: true`; otherwise requests are denied. `insecureAllowUnenforced: true` relaxes this for local development only.
|
|
41
|
+
|
|
42
|
+
## API
|
|
43
|
+
|
|
44
|
+
- `withPCA(options, handler)` - returns `(req: Request) => Promise<Response>`. Options extend `RequirePcaOptions` from `@atlasauth/pca`, plus `onDeny(result, req)` to customize the error response.
|
|
45
|
+
- Re-exports `requirePCA`, `defaultExtract`, `memoryPcaStore`.
|
|
46
|
+
|
|
47
|
+
## Status
|
|
48
|
+
|
|
49
|
+
This package only wires the guard into Next.js route handlers; all verification logic lives in `@atlasauth/pca`. PCA cryptography has not been independently audited.
|
|
50
|
+
|
|
51
|
+
## License
|
|
52
|
+
|
|
53
|
+
MIT - see LICENSE
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Next.js App-Router helper for Proof-Carrying Authority.
|
|
3
|
+
*
|
|
4
|
+
* export const POST = withPCA(
|
|
5
|
+
* { audience: 'ins_acme', resolveGrant, budgetStore, hooks, context },
|
|
6
|
+
* async (req, { pca }) => { const { verdict, pcactn } = pca; return Response.json({ ok: true }); },
|
|
7
|
+
* );
|
|
8
|
+
*
|
|
9
|
+
* It wraps a Route Handler so the inbound PCActn is verified with the framework-agnostic `requirePCA` guard
|
|
10
|
+
* from `@atlasauth/pca` (default-deny: a required check that is unenforced DENIES) before your handler
|
|
11
|
+
* runs. On success it calls your handler with `{ pca: { verdict, pcactn } }`; on failure it returns a 401/403
|
|
12
|
+
* JSON response with a WWW-Authenticate challenge. The client sends the PCActn as `PCA-Action: <base64url>`
|
|
13
|
+
* (see `@atlasauth/pca` `pcaHeaders`) or a JSON body `{ pcactn }`; that is exactly what the default extractor
|
|
14
|
+
* reads, so no wiring is needed.
|
|
15
|
+
*
|
|
16
|
+
* `next` is an OPTIONAL peer dependency and is never imported — the handler takes the web `Request` and returns
|
|
17
|
+
* a web `Response`, which is exactly what an App-Router Route Handler is.
|
|
18
|
+
*/
|
|
19
|
+
import { requirePCA, defaultExtract, memoryPcaStore } from '@atlasauth/pca';
|
|
20
|
+
import type { PcaGuardResult, RequirePcaOptions, PcaStateStore } from '@atlasauth/pca';
|
|
21
|
+
import type { PCActn } from '@atlasauth/pca';
|
|
22
|
+
/** The verdict + PCActn handed to the wrapped handler on success. */
|
|
23
|
+
export interface PcaAttachment {
|
|
24
|
+
verdict: Extract<PcaGuardResult, {
|
|
25
|
+
ok: true;
|
|
26
|
+
}>['verdict'];
|
|
27
|
+
pcactn: PCActn;
|
|
28
|
+
}
|
|
29
|
+
/** A Next.js App-Router Route Handler that receives the verified PCActn in its context. */
|
|
30
|
+
export type PcaRouteHandler = (req: Request, ctx: {
|
|
31
|
+
pca: PcaAttachment;
|
|
32
|
+
}) => Response | Promise<Response>;
|
|
33
|
+
export interface PcaNextOptions extends RequirePcaOptions {
|
|
34
|
+
/** Override the 401/403 response (otherwise a JSON error + WWW-Authenticate is returned). */
|
|
35
|
+
onDeny?: (result: Extract<PcaGuardResult, {
|
|
36
|
+
ok: false;
|
|
37
|
+
}>, req: Request) => Response | Promise<Response>;
|
|
38
|
+
}
|
|
39
|
+
/** Wrap an App-Router Route Handler so it only runs for a valid PCActn. */
|
|
40
|
+
export declare function withPCA(options: PcaNextOptions, handler: PcaRouteHandler): (req: Request) => Promise<Response>;
|
|
41
|
+
export { requirePCA, defaultExtract, memoryPcaStore };
|
|
42
|
+
export type { PcaGuardResult, RequirePcaOptions, PcaStateStore };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Next.js App-Router helper for Proof-Carrying Authority.
|
|
4
|
+
*
|
|
5
|
+
* export const POST = withPCA(
|
|
6
|
+
* { audience: 'ins_acme', resolveGrant, budgetStore, hooks, context },
|
|
7
|
+
* async (req, { pca }) => { const { verdict, pcactn } = pca; return Response.json({ ok: true }); },
|
|
8
|
+
* );
|
|
9
|
+
*
|
|
10
|
+
* It wraps a Route Handler so the inbound PCActn is verified with the framework-agnostic `requirePCA` guard
|
|
11
|
+
* from `@atlasauth/pca` (default-deny: a required check that is unenforced DENIES) before your handler
|
|
12
|
+
* runs. On success it calls your handler with `{ pca: { verdict, pcactn } }`; on failure it returns a 401/403
|
|
13
|
+
* JSON response with a WWW-Authenticate challenge. The client sends the PCActn as `PCA-Action: <base64url>`
|
|
14
|
+
* (see `@atlasauth/pca` `pcaHeaders`) or a JSON body `{ pcactn }`; that is exactly what the default extractor
|
|
15
|
+
* reads, so no wiring is needed.
|
|
16
|
+
*
|
|
17
|
+
* `next` is an OPTIONAL peer dependency and is never imported — the handler takes the web `Request` and returns
|
|
18
|
+
* a web `Response`, which is exactly what an App-Router Route Handler is.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.memoryPcaStore = exports.defaultExtract = exports.requirePCA = void 0;
|
|
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
|
+
/** Wrap an App-Router Route Handler so it only runs for a valid PCActn. */
|
|
28
|
+
function withPCA(options, handler) {
|
|
29
|
+
const onDeny = options.onDeny;
|
|
30
|
+
const guard = (0, pca_1.requirePCA)(options);
|
|
31
|
+
return async (req) => {
|
|
32
|
+
const like = {
|
|
33
|
+
headers: { get: (n) => req.headers.get(n) },
|
|
34
|
+
body: await req.clone().json().catch(() => undefined),
|
|
35
|
+
};
|
|
36
|
+
const result = await guard(like);
|
|
37
|
+
if (result.ok) {
|
|
38
|
+
return handler(req, { pca: { verdict: result.verdict, pcactn: result.pcactn } });
|
|
39
|
+
}
|
|
40
|
+
if (onDeny)
|
|
41
|
+
return onDeny(result, req);
|
|
42
|
+
return new Response(JSON.stringify({ error: result.verdict.reasons?.[0] ?? 'forbidden', verdict: result.verdict }), {
|
|
43
|
+
status: result.status,
|
|
44
|
+
headers: {
|
|
45
|
+
'content-type': 'application/json',
|
|
46
|
+
...(result.wwwAuthenticate ? { 'WWW-Authenticate': result.wwwAuthenticate } : {}),
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;GAiBG;;;AAqBH,0BAqBC;AAxCD,wCAA4E;AA0CnE,2FA1CA,gBAAU,OA0CA;AAAE,+FA1CA,oBAAc,OA0CA;AAAE,+FA1CA,oBAAc,OA0CA;AAxBnD,2EAA2E;AAC3E,SAAgB,OAAO,CAAC,OAAuB,EAAE,OAAwB;IACvE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9B,MAAM,KAAK,GAAG,IAAA,gBAAU,EAAC,OAAO,CAAC,CAAC;IAClC,OAAO,KAAK,EAAE,GAAY,EAAqB,EAAE;QAC/C,MAAM,IAAI,GAAmB;YAC3B,OAAO,EAAE,EAAE,GAAG,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;YACnD,IAAI,EAAE,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;SACtD,CAAC;QACF,MAAM,MAAM,GAAmB,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,OAAO,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACnF,CAAC;QACD,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACvC,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE;YAClH,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAClF;SACF,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlasauth/pca-next",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Next.js helper for Proof-Carrying Authority: wrap a Route Handler so the inbound PCActn is verified before your handler runs, with a 401/403 response otherwise.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"next.js",
|
|
7
|
+
"route handler",
|
|
8
|
+
"middleware",
|
|
9
|
+
"agent authorization",
|
|
10
|
+
"proof-carrying authority",
|
|
11
|
+
"pca",
|
|
12
|
+
"ai agents"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"homepage": "https://github.com/Atlas-Authorization/pca#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/Atlas-Authorization/pca/issues"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/Atlas-Authorization/pca.git",
|
|
22
|
+
"directory": "packages/pca-next"
|
|
23
|
+
},
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"default": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@atlasauth/pca": "0.2.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"next": "^14 || ^15"
|
|
38
|
+
},
|
|
39
|
+
"peerDependenciesMeta": {
|
|
40
|
+
"next": {
|
|
41
|
+
"optional": true
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"typescript": "^5.6.3",
|
|
46
|
+
"vitest": "^2.1.4"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist",
|
|
50
|
+
"README.md",
|
|
51
|
+
"LICENSE"
|
|
52
|
+
],
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=20"
|
|
55
|
+
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsc -p tsconfig.build.json",
|
|
61
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
62
|
+
"test": "vitest run"
|
|
63
|
+
}
|
|
6
64
|
}
|