@atlasauth/pca-next 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 +35 -2
- package/dist/index.d.ts +42 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -3
package/README.md
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @atlasauth/pca-next
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm i @atlasauth/pca-next
|
|
9
|
+
# peer dependency, install alongside:
|
|
10
|
+
npm i next
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`next` (v14 or v15) is an optional peer dependency.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
// app/refunds/route.ts
|
|
19
|
+
import { withPCA } from '@atlasauth/pca-next';
|
|
20
|
+
|
|
21
|
+
export const POST = withPCA(
|
|
22
|
+
{
|
|
23
|
+
audience: 'ins_acme', // this resource server's / instance id the PCActn must name
|
|
24
|
+
resolveGrant, // (ref) => Capability — look up the root grant
|
|
25
|
+
budgetStore, // per-holder replay/budget store
|
|
26
|
+
},
|
|
27
|
+
async (req, { pca }) => {
|
|
28
|
+
const { verdict, pcactn } = pca; // handler only runs on a valid PCActn
|
|
29
|
+
return Response.json({ ok: true });
|
|
30
|
+
},
|
|
31
|
+
);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The client sends the PCActn as `PCA-Action: <base64url>` (see `@atlasauth/pca` `pcaHeaders`) or a JSON body `{ pcactn }` — the default extractor reads both. The wrapper verifies and is default-deny; it is the verifier that decides, not the client's proof.
|
|
35
|
+
|
|
36
|
+
Part of Proof-Carrying Authority — see `@atlasauth/pca`.
|
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/backend` (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/backend';
|
|
20
|
+
import type { PcaGuardResult, RequirePcaOptions, PcaStateStore } from '@atlasauth/backend';
|
|
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/backend` (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 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
|
+
/** 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, backend_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,gDAAgF;AA0CvE,2FA1CA,oBAAU,OA0CA;AAAE,+FA1CA,wBAAc,OA0CA;AAAE,+FA1CA,wBAAc,OA0CA;AAxBnD,2EAA2E;AAC3E,SAAgB,OAAO,CAAC,OAAuB,EAAE,OAAwB;IACvE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC9B,MAAM,KAAK,GAAG,IAAA,oBAAU,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,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlasauth/pca-next",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.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
|
+
"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
|
+
"peerDependencies": {
|
|
19
|
+
"next": "^14 || ^15"
|
|
20
|
+
},
|
|
21
|
+
"peerDependenciesMeta": {
|
|
22
|
+
"next": {
|
|
23
|
+
"optional": true
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"typescript": "^5.6.3",
|
|
28
|
+
"vitest": "^2.1.4"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc -p tsconfig.build.json",
|
|
39
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
40
|
+
"test": "vitest run"
|
|
41
|
+
}
|
|
6
42
|
}
|