@flarcos/kiota-authentication-gnap 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.
Files changed (38) hide show
  1. package/README.md +192 -0
  2. package/dist/contentDigest.d.ts +17 -0
  3. package/dist/contentDigest.d.ts.map +1 -0
  4. package/dist/contentDigest.js +23 -0
  5. package/dist/contentDigest.js.map +1 -0
  6. package/dist/errors.d.ts +25 -0
  7. package/dist/errors.d.ts.map +1 -0
  8. package/dist/errors.js +46 -0
  9. package/dist/errors.js.map +1 -0
  10. package/dist/gnapAccessTokenProvider.d.ts +50 -0
  11. package/dist/gnapAccessTokenProvider.d.ts.map +1 -0
  12. package/dist/gnapAccessTokenProvider.js +176 -0
  13. package/dist/gnapAccessTokenProvider.js.map +1 -0
  14. package/dist/gnapAuthenticationProvider.d.ts +68 -0
  15. package/dist/gnapAuthenticationProvider.d.ts.map +1 -0
  16. package/dist/gnapAuthenticationProvider.js +216 -0
  17. package/dist/gnapAuthenticationProvider.js.map +1 -0
  18. package/dist/gnapTokenStore.d.ts +11 -0
  19. package/dist/gnapTokenStore.d.ts.map +1 -0
  20. package/dist/gnapTokenStore.js +27 -0
  21. package/dist/gnapTokenStore.js.map +1 -0
  22. package/dist/httpMessageSigner.d.ts +41 -0
  23. package/dist/httpMessageSigner.d.ts.map +1 -0
  24. package/dist/httpMessageSigner.js +74 -0
  25. package/dist/httpMessageSigner.js.map +1 -0
  26. package/dist/index.d.ts +20 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +25 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/keyManagement.d.ts +36 -0
  31. package/dist/keyManagement.d.ts.map +1 -0
  32. package/dist/keyManagement.js +75 -0
  33. package/dist/keyManagement.js.map +1 -0
  34. package/dist/types.d.ts +145 -0
  35. package/dist/types.d.ts.map +1 -0
  36. package/dist/types.js +6 -0
  37. package/dist/types.js.map +1 -0
  38. package/package.json +44 -0
package/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # @flarcos/kiota-authentication-gnap
2
+
3
+ GNAP ([RFC 9635](https://www.rfc-editor.org/rfc/rfc9635.html)) authentication provider for [Microsoft Kiota](https://github.com/microsoft/kiota)-generated API clients.
4
+
5
+ Enables Kiota-generated SDKs to authenticate with APIs that use the Grant Negotiation and Authorization Protocol (GNAP), such as [Open Payments](https://openpayments.dev/).
6
+
7
+ ## Features
8
+
9
+ - ✅ **Kiota `AuthenticationProvider` interface** — drop-in integration
10
+ - ✅ **RFC 9635 GNAP** — full grant lifecycle (request, continue, rotate, revoke)
11
+ - ✅ **RFC 9421 HTTP Message Signatures** — Ed25519 request signing
12
+ - ✅ **RFC 9530 Content-Digest** — SHA-256 body integrity
13
+ - ✅ **RFC 7638 JWK Thumbprint** — automatic key ID derivation
14
+ - ✅ **Token caching** — in-memory store with TTL expiration
15
+ - ✅ **Interactive grants** — pluggable `InteractionHandler` for redirect flows
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @flarcos/kiota-authentication-gnap @microsoft/kiota-abstractions
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ```typescript
26
+ import { GnapAuthenticationProvider } from "@flarcos/kiota-authentication-gnap";
27
+ import { FetchRequestAdapter } from "@microsoft/kiota-http-fetchlibrary";
28
+ import fs from "node:fs";
29
+
30
+ // Create the GNAP auth provider
31
+ const authProvider = new GnapAuthenticationProvider({
32
+ authServerUrl: "https://auth.wallet.example/.gnap",
33
+ privateKey: fs.readFileSync("private-key.pem", "utf-8"),
34
+ clientIdentifier: "https://wallet.example/alice",
35
+ accessRights: [
36
+ { type: "incoming-payment", actions: ["create", "read"] },
37
+ { type: "outgoing-payment", actions: ["create", "read"] },
38
+ { type: "quote", actions: ["create", "read"] },
39
+ ],
40
+ });
41
+
42
+ // Use with a Kiota-generated client
43
+ const adapter = new FetchRequestAdapter(authProvider);
44
+ // const client = new OpenPaymentsClient(adapter);
45
+ ```
46
+
47
+ ## Usage with Open Payments
48
+
49
+ Open Payments uses GNAP for authorization. Here's how to create a payment:
50
+
51
+ ```typescript
52
+ import { GnapAuthenticationProvider } from "@flarcos/kiota-authentication-gnap";
53
+
54
+ const authProvider = new GnapAuthenticationProvider({
55
+ // The receiving wallet's auth server
56
+ authServerUrl: "https://auth.interledger-test.dev/",
57
+ // Your Ed25519 private key (PEM format)
58
+ privateKey: process.env.PRIVATE_KEY!,
59
+ // Your wallet address URL
60
+ clientIdentifier: "https://ilp.interledger-test.dev/alice",
61
+ // Request access to create incoming payments
62
+ accessRights: [
63
+ {
64
+ type: "incoming-payment",
65
+ actions: ["create", "read", "complete"],
66
+ identifier: "https://ilp.interledger-test.dev/bob",
67
+ },
68
+ ],
69
+ });
70
+ ```
71
+
72
+ ### Interactive Grants (with user consent)
73
+
74
+ For operations that require user interaction (e.g., outgoing payments), provide an `InteractionHandler`:
75
+
76
+ ```typescript
77
+ import type { InteractionHandler, InteractionResult } from "@flarcos/kiota-authentication-gnap";
78
+
79
+ class MyInteractionHandler implements InteractionHandler {
80
+ async handleRedirect(redirectUrl: string, nonce: string): Promise<InteractionResult> {
81
+ // Open browser for user to approve
82
+ console.log(`Please visit: ${redirectUrl}`);
83
+
84
+ // Wait for callback and return the interact_ref
85
+ return {
86
+ interactRef: "received-interact-ref",
87
+ hash: "received-hash",
88
+ };
89
+ }
90
+ }
91
+
92
+ const authProvider = new GnapAuthenticationProvider({
93
+ authServerUrl: "https://auth.interledger-test.dev/",
94
+ privateKey: process.env.PRIVATE_KEY!,
95
+ clientIdentifier: "https://ilp.interledger-test.dev/alice",
96
+ accessRights: [
97
+ {
98
+ type: "outgoing-payment",
99
+ actions: ["create", "read"],
100
+ identifier: "https://ilp.interledger-test.dev/alice",
101
+ },
102
+ ],
103
+ interact: {
104
+ start: ["redirect"],
105
+ finish: {
106
+ method: "redirect",
107
+ uri: "http://localhost:3000/callback",
108
+ nonce: crypto.randomUUID(),
109
+ },
110
+ },
111
+ interactionHandler: new MyInteractionHandler(),
112
+ });
113
+ ```
114
+
115
+ ## API Reference
116
+
117
+ ### `GnapAuthenticationProvider`
118
+
119
+ Main class implementing Kiota's `AuthenticationProvider`.
120
+
121
+ #### Constructor Options
122
+
123
+ | Option | Type | Required | Description |
124
+ |--------|------|----------|-------------|
125
+ | `authServerUrl` | `string` | ✅ | Authorization Server grant endpoint |
126
+ | `privateKey` | `string` | ✅ | Ed25519 private key (PEM format) |
127
+ | `clientIdentifier` | `string` | ✅ | Client identifier (wallet address URL) |
128
+ | `accessRights` | `GnapAccessRight[]` | ✅ | Access rights to request |
129
+ | `interact` | `object` | ❌ | Interaction mode configuration |
130
+ | `interactionHandler` | `InteractionHandler` | ❌ | Handler for interactive grants |
131
+ | `tokenStore` | `GnapTokenStore` | ❌ | Token cache (defaults to in-memory) |
132
+ | `allowedHosts` | `string[]` | ❌ | Restrict auth to specific hosts |
133
+
134
+ #### Methods
135
+
136
+ - `authenticateRequest(request)` — Authenticate a Kiota request (called automatically)
137
+ - `rotateToken()` — Manually rotate the current access token
138
+ - `revokeToken()` — Revoke the current access token
139
+
140
+ ### Standalone Utilities
141
+
142
+ ```typescript
143
+ import {
144
+ createHttpSignature, // RFC 9421 signing
145
+ computeContentDigest, // RFC 9530 body hash
146
+ computeJwkThumbprint, // RFC 7638 key ID
147
+ loadPrivateKey, // PEM → KeyObject
148
+ } from "@flarcos/kiota-authentication-gnap";
149
+ ```
150
+
151
+ ## How It Works
152
+
153
+ When `authenticateRequest` is called, the provider:
154
+
155
+ 1. **Checks the token cache** for a valid, non-expired GNAP access token
156
+ 2. **Negotiates a new grant** if no cached token exists (POST to AS)
157
+ 3. **Handles interaction** if the AS requires user consent (via `InteractionHandler`)
158
+ 4. **Sets the `Authorization: GNAP <token>` header** on the request
159
+ 5. **Computes `Content-Digest`** (SHA-256) for requests with a body
160
+ 6. **Signs the request** with RFC 9421 HTTP Message Signatures (Ed25519)
161
+
162
+ ## Standards Compliance
163
+
164
+ | Standard | Description | Coverage |
165
+ |----------|-------------|----------|
166
+ | [RFC 9635](https://www.rfc-editor.org/rfc/rfc9635.html) | GNAP Core Protocol | §2 Grant Request, §3 Response, §5 Continuation, §6 Token Management |
167
+ | [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421.html) | HTTP Message Signatures | §2 Signature Base, §3 Creating Signatures (Ed25519) |
168
+ | [RFC 9530](https://www.rfc-editor.org/rfc/rfc9530.html) | Content-Digest | SHA-256 digest |
169
+ | [RFC 7638](https://www.rfc-editor.org/rfc/rfc7638.html) | JWK Thumbprint | SHA-256 thumbprint for key identification |
170
+
171
+ ## Project Structure
172
+
173
+ ```
174
+ src/
175
+ ├── index.ts # Public exports
176
+ ├── gnapAuthenticationProvider.ts # Kiota AuthenticationProvider implementation
177
+ ├── gnapAccessTokenProvider.ts # GNAP grant lifecycle
178
+ ├── gnapTokenStore.ts # In-memory token cache
179
+ ├── httpMessageSigner.ts # RFC 9421 HTTP Message Signatures
180
+ ├── contentDigest.ts # RFC 9530 Content-Digest
181
+ ├── keyManagement.ts # Ed25519 key utilities
182
+ ├── types.ts # Type definitions
183
+ └── errors.ts # Error classes
184
+ ```
185
+
186
+ ## License
187
+
188
+ Apache-2.0
189
+
190
+ ## Acknowledgements
191
+
192
+ This package was created as part of the [Interledger Foundation Open Payments SDK Grant Program](https://interledger.org/grant/open-payments-sdk).
@@ -0,0 +1,17 @@
1
+ /**
2
+ * RFC 9530 Content-Digest computation.
3
+ * @see https://www.rfc-editor.org/rfc/rfc9530.html
4
+ */
5
+ /**
6
+ * Compute the Content-Digest header value for an HTTP request body.
7
+ * Uses SHA-256 as required by Open Payments.
8
+ *
9
+ * @param body - The request body as a Buffer or Uint8Array
10
+ * @returns The Content-Digest header value (e.g. "sha-256=:base64value:")
11
+ */
12
+ export declare function computeContentDigest(body: Uint8Array): string;
13
+ /**
14
+ * Compute Content-Digest from a string body (UTF-8 encoded).
15
+ */
16
+ export declare function computeContentDigestFromString(body: string): string;
17
+ //# sourceMappingURL=contentDigest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentDigest.d.ts","sourceRoot":"","sources":["../src/contentDigest.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAG7D;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnE"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * RFC 9530 Content-Digest computation.
3
+ * @see https://www.rfc-editor.org/rfc/rfc9530.html
4
+ */
5
+ import { createHash } from "node:crypto";
6
+ /**
7
+ * Compute the Content-Digest header value for an HTTP request body.
8
+ * Uses SHA-256 as required by Open Payments.
9
+ *
10
+ * @param body - The request body as a Buffer or Uint8Array
11
+ * @returns The Content-Digest header value (e.g. "sha-256=:base64value:")
12
+ */
13
+ export function computeContentDigest(body) {
14
+ const hash = createHash("sha-256").update(body).digest("base64");
15
+ return `sha-256=:${hash}:`;
16
+ }
17
+ /**
18
+ * Compute Content-Digest from a string body (UTF-8 encoded).
19
+ */
20
+ export function computeContentDigestFromString(body) {
21
+ return computeContentDigest(Buffer.from(body, "utf-8"));
22
+ }
23
+ //# sourceMappingURL=contentDigest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentDigest.js","sourceRoot":"","sources":["../src/contentDigest.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAgB;IACnD,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACjE,OAAO,YAAY,IAAI,GAAG,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,8BAA8B,CAAC,IAAY;IACzD,OAAO,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * GNAP-specific error classes.
3
+ */
4
+ export declare class GnapError extends Error {
5
+ readonly code?: string | undefined;
6
+ readonly statusCode?: number | undefined;
7
+ constructor(message: string, code?: string | undefined, statusCode?: number | undefined);
8
+ }
9
+ export declare class GnapGrantError extends GnapError {
10
+ constructor(code: string, description?: string);
11
+ }
12
+ export declare class GnapInteractionRequiredError extends GnapError {
13
+ readonly redirectUrl: string;
14
+ readonly continueUri: string;
15
+ readonly continueAccessToken: string;
16
+ readonly finishNonce?: string | undefined;
17
+ constructor(redirectUrl: string, continueUri: string, continueAccessToken: string, finishNonce?: string | undefined);
18
+ }
19
+ export declare class GnapTokenExpiredError extends GnapError {
20
+ constructor();
21
+ }
22
+ export declare class GnapSignatureError extends GnapError {
23
+ constructor(message: string);
24
+ }
25
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,qBAAa,SAAU,SAAQ,KAAK;aAGhB,IAAI,CAAC,EAAE,MAAM;aACb,UAAU,CAAC,EAAE,MAAM;gBAFnC,OAAO,EAAE,MAAM,EACC,IAAI,CAAC,EAAE,MAAM,YAAA,EACb,UAAU,CAAC,EAAE,MAAM,YAAA;CAKtC;AAED,qBAAa,cAAe,SAAQ,SAAS;gBAC/B,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM;CAI/C;AAED,qBAAa,4BAA6B,SAAQ,SAAS;aAEvC,WAAW,EAAE,MAAM;aACnB,WAAW,EAAE,MAAM;aACnB,mBAAmB,EAAE,MAAM;aAC3B,WAAW,CAAC,EAAE,MAAM;gBAHpB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,mBAAmB,EAAE,MAAM,EAC3B,WAAW,CAAC,EAAE,MAAM,YAAA;CAKvC;AAED,qBAAa,qBAAsB,SAAQ,SAAS;;CAKnD;AAED,qBAAa,kBAAmB,SAAQ,SAAS;gBACnC,OAAO,EAAE,MAAM;CAI5B"}
package/dist/errors.js ADDED
@@ -0,0 +1,46 @@
1
+ /**
2
+ * GNAP-specific error classes.
3
+ */
4
+ export class GnapError extends Error {
5
+ code;
6
+ statusCode;
7
+ constructor(message, code, statusCode) {
8
+ super(message);
9
+ this.code = code;
10
+ this.statusCode = statusCode;
11
+ this.name = "GnapError";
12
+ }
13
+ }
14
+ export class GnapGrantError extends GnapError {
15
+ constructor(code, description) {
16
+ super(description ?? `GNAP grant error: ${code}`, code);
17
+ this.name = "GnapGrantError";
18
+ }
19
+ }
20
+ export class GnapInteractionRequiredError extends GnapError {
21
+ redirectUrl;
22
+ continueUri;
23
+ continueAccessToken;
24
+ finishNonce;
25
+ constructor(redirectUrl, continueUri, continueAccessToken, finishNonce) {
26
+ super("GNAP grant requires user interaction");
27
+ this.redirectUrl = redirectUrl;
28
+ this.continueUri = continueUri;
29
+ this.continueAccessToken = continueAccessToken;
30
+ this.finishNonce = finishNonce;
31
+ this.name = "GnapInteractionRequiredError";
32
+ }
33
+ }
34
+ export class GnapTokenExpiredError extends GnapError {
35
+ constructor() {
36
+ super("GNAP access token has expired");
37
+ this.name = "GnapTokenExpiredError";
38
+ }
39
+ }
40
+ export class GnapSignatureError extends GnapError {
41
+ constructor(message) {
42
+ super(message);
43
+ this.name = "GnapSignatureError";
44
+ }
45
+ }
46
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,OAAO,SAAU,SAAQ,KAAK;IAGhB;IACA;IAHlB,YACE,OAAe,EACC,IAAa,EACb,UAAmB;QAEnC,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,SAAI,GAAJ,IAAI,CAAS;QACb,eAAU,GAAV,UAAU,CAAS;QAGnC,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF;AAED,MAAM,OAAO,cAAe,SAAQ,SAAS;IAC3C,YAAY,IAAY,EAAE,WAAoB;QAC5C,KAAK,CAAC,WAAW,IAAI,qBAAqB,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;QACxD,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AAED,MAAM,OAAO,4BAA6B,SAAQ,SAAS;IAEvC;IACA;IACA;IACA;IAJlB,YACkB,WAAmB,EACnB,WAAmB,EACnB,mBAA2B,EAC3B,WAAoB;QAEpC,KAAK,CAAC,sCAAsC,CAAC,CAAC;QAL9B,gBAAW,GAAX,WAAW,CAAQ;QACnB,gBAAW,GAAX,WAAW,CAAQ;QACnB,wBAAmB,GAAnB,mBAAmB,CAAQ;QAC3B,gBAAW,GAAX,WAAW,CAAS;QAGpC,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAC;IAC7C,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,SAAS;IAClD;QACE,KAAK,CAAC,+BAA+B,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,MAAM,OAAO,kBAAmB,SAAQ,SAAS;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * GNAP Access Token Provider.
3
+ * Handles the full GNAP (RFC 9635) grant lifecycle:
4
+ * - Grant request (§2)
5
+ * - Grant continuation (§5)
6
+ * - Token rotation (§6.1)
7
+ * - Token revocation (§6.2)
8
+ *
9
+ * All requests to the Authorization Server are signed with RFC 9421.
10
+ */
11
+ import type { KeyObject } from "node:crypto";
12
+ import type { GnapGrantRequest, GnapAccessToken, GnapAccessRight } from "./types.js";
13
+ export interface GrantRequestParams {
14
+ /** Authorization Server grant endpoint URL */
15
+ authServerUrl: string;
16
+ /** Client identifier (wallet address URL) */
17
+ clientIdentifier: string;
18
+ /** Client public key in JWK format */
19
+ clientKeyJwk: JsonWebKey;
20
+ /** Client private key for signing */
21
+ privateKey: KeyObject;
22
+ /** JWK thumbprint key ID */
23
+ keyId: string;
24
+ /** Access rights to request */
25
+ accessRights: GnapAccessRight[];
26
+ /** Interaction configuration */
27
+ interact?: GnapGrantRequest["interact"];
28
+ }
29
+ export declare class GnapAccessTokenProvider {
30
+ /**
31
+ * Request a new grant from the Authorization Server.
32
+ * Returns the access token for non-interactive grants,
33
+ * or throws GnapInteractionRequiredError if user consent is needed.
34
+ */
35
+ requestGrant(params: GrantRequestParams): Promise<GnapAccessToken>;
36
+ /**
37
+ * Continue a grant after user interaction (§5.1).
38
+ * Called with the interact_ref obtained from the interaction callback.
39
+ */
40
+ continueGrant(continueUri: string, continueAccessToken: string, interactRef: string, privateKey: KeyObject, keyId: string): Promise<GnapAccessToken>;
41
+ /**
42
+ * Rotate an access token (§6.1).
43
+ */
44
+ rotateToken(manageUrl: string, existingToken: string, privateKey: KeyObject, keyId: string): Promise<GnapAccessToken>;
45
+ /**
46
+ * Revoke an access token (§6.2).
47
+ */
48
+ revokeToken(manageUrl: string, existingToken: string, privateKey: KeyObject, keyId: string): Promise<void>;
49
+ }
50
+ //# sourceMappingURL=gnapAccessTokenProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gnapAccessTokenProvider.d.ts","sourceRoot":"","sources":["../src/gnapAccessTokenProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,OAAO,KAAK,EACV,gBAAgB,EAEhB,eAAe,EACf,eAAe,EAEhB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,kBAAkB;IACjC,8CAA8C;IAC9C,aAAa,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,gBAAgB,EAAE,MAAM,CAAC;IACzB,sCAAsC;IACtC,YAAY,EAAE,UAAU,CAAC;IACzB,qCAAqC;IACrC,UAAU,EAAE,SAAS,CAAC;IACtB,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,YAAY,EAAE,eAAe,EAAE,CAAC;IAChC,gCAAgC;IAChC,QAAQ,CAAC,EAAE,gBAAgB,CAAC,UAAU,CAAC,CAAC;CACzC;AAED,qBAAa,uBAAuB;IAClC;;;;OAIG;IACG,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,eAAe,CAAC;IA4FxE;;;OAGG;IACG,aAAa,CACjB,WAAW,EAAE,MAAM,EACnB,mBAAmB,EAAE,MAAM,EAC3B,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,SAAS,EACrB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,eAAe,CAAC;IAmD3B;;OAEG;IACG,WAAW,CACf,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,SAAS,EACrB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,eAAe,CAAC;IAkC3B;;OAEG;IACG,WAAW,CACf,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,SAAS,EACrB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC;CA6BjB"}
@@ -0,0 +1,176 @@
1
+ /**
2
+ * GNAP Access Token Provider.
3
+ * Handles the full GNAP (RFC 9635) grant lifecycle:
4
+ * - Grant request (§2)
5
+ * - Grant continuation (§5)
6
+ * - Token rotation (§6.1)
7
+ * - Token revocation (§6.2)
8
+ *
9
+ * All requests to the Authorization Server are signed with RFC 9421.
10
+ */
11
+ import { createHttpSignature } from "./httpMessageSigner.js";
12
+ import { computeContentDigestFromString } from "./contentDigest.js";
13
+ import { GnapGrantError, GnapInteractionRequiredError } from "./errors.js";
14
+ export class GnapAccessTokenProvider {
15
+ /**
16
+ * Request a new grant from the Authorization Server.
17
+ * Returns the access token for non-interactive grants,
18
+ * or throws GnapInteractionRequiredError if user consent is needed.
19
+ */
20
+ async requestGrant(params) {
21
+ const { authServerUrl, clientIdentifier, clientKeyJwk, privateKey, keyId, accessRights, interact, } = params;
22
+ // Build the GNAP grant request body (§2)
23
+ // Open Payments uses a simplified client format:
24
+ // - string: wallet address URL (backwards compatible)
25
+ // - { walletAddress: "..." }: wallet address in object form
26
+ // - { jwk: {...} }: directed identity (non-interactive only)
27
+ const grantRequest = {
28
+ access_token: {
29
+ access: accessRights,
30
+ },
31
+ client: clientIdentifier,
32
+ };
33
+ if (interact) {
34
+ grantRequest.interact = interact;
35
+ }
36
+ const body = JSON.stringify(grantRequest);
37
+ // Compute Content-Digest
38
+ const contentDigest = computeContentDigestFromString(body);
39
+ // Build headers
40
+ const headers = new Map();
41
+ headers.set("content-type", "application/json");
42
+ headers.set("content-digest", contentDigest);
43
+ headers.set("content-length", Buffer.byteLength(body).toString());
44
+ // Sign the request (RFC 9421)
45
+ const { signature, signatureInput } = await createHttpSignature({
46
+ privateKey,
47
+ keyId,
48
+ method: "POST",
49
+ targetUri: authServerUrl,
50
+ headers,
51
+ hasBody: true,
52
+ });
53
+ // Send the grant request
54
+ const response = await fetch(authServerUrl, {
55
+ method: "POST",
56
+ headers: {
57
+ "Content-Type": "application/json",
58
+ "Content-Digest": contentDigest,
59
+ "Content-Length": Buffer.byteLength(body).toString(),
60
+ Signature: signature,
61
+ "Signature-Input": signatureInput,
62
+ },
63
+ body,
64
+ });
65
+ const grantResponse = (await response.json());
66
+ // Handle error response
67
+ if (grantResponse.error) {
68
+ throw new GnapGrantError(grantResponse.error.code, grantResponse.error.description);
69
+ }
70
+ // Non-interactive: access token returned directly
71
+ if (grantResponse.access_token) {
72
+ return grantResponse.access_token;
73
+ }
74
+ // Interactive: AS requires user consent
75
+ if (grantResponse.interact?.redirect && grantResponse.continue) {
76
+ throw new GnapInteractionRequiredError(grantResponse.interact.redirect, grantResponse.continue.uri, grantResponse.continue.access_token.value, grantResponse.interact.finish);
77
+ }
78
+ throw new GnapGrantError("invalid_grant_response", `Unexpected grant response (HTTP ${response.status}): no access_token or interact field`);
79
+ }
80
+ /**
81
+ * Continue a grant after user interaction (§5.1).
82
+ * Called with the interact_ref obtained from the interaction callback.
83
+ */
84
+ async continueGrant(continueUri, continueAccessToken, interactRef, privateKey, keyId) {
85
+ const body = JSON.stringify({ interact_ref: interactRef });
86
+ const contentDigest = computeContentDigestFromString(body);
87
+ const headers = new Map();
88
+ headers.set("content-type", "application/json");
89
+ headers.set("content-digest", contentDigest);
90
+ headers.set("content-length", Buffer.byteLength(body).toString());
91
+ headers.set("authorization", `GNAP ${continueAccessToken}`);
92
+ const { signature, signatureInput } = await createHttpSignature({
93
+ privateKey,
94
+ keyId,
95
+ method: "POST",
96
+ targetUri: continueUri,
97
+ headers,
98
+ hasBody: true,
99
+ });
100
+ const response = await fetch(continueUri, {
101
+ method: "POST",
102
+ headers: {
103
+ "Content-Type": "application/json",
104
+ "Content-Digest": contentDigest,
105
+ "Content-Length": Buffer.byteLength(body).toString(),
106
+ Authorization: `GNAP ${continueAccessToken}`,
107
+ Signature: signature,
108
+ "Signature-Input": signatureInput,
109
+ },
110
+ body,
111
+ });
112
+ const grantResponse = (await response.json());
113
+ if (grantResponse.error) {
114
+ throw new GnapGrantError(grantResponse.error.code, grantResponse.error.description);
115
+ }
116
+ if (grantResponse.access_token) {
117
+ return grantResponse.access_token;
118
+ }
119
+ throw new GnapGrantError("invalid_continue_response", `Continue response did not include access_token (HTTP ${response.status})`);
120
+ }
121
+ /**
122
+ * Rotate an access token (§6.1).
123
+ */
124
+ async rotateToken(manageUrl, existingToken, privateKey, keyId) {
125
+ const headers = new Map();
126
+ headers.set("authorization", `GNAP ${existingToken}`);
127
+ const { signature, signatureInput } = await createHttpSignature({
128
+ privateKey,
129
+ keyId,
130
+ method: "POST",
131
+ targetUri: manageUrl,
132
+ headers,
133
+ hasBody: false,
134
+ });
135
+ const response = await fetch(manageUrl, {
136
+ method: "POST",
137
+ headers: {
138
+ Authorization: `GNAP ${existingToken}`,
139
+ Signature: signature,
140
+ "Signature-Input": signatureInput,
141
+ },
142
+ });
143
+ const tokenResponse = (await response.json());
144
+ if (!tokenResponse.value) {
145
+ throw new GnapGrantError("invalid_rotation_response", `Token rotation failed (HTTP ${response.status})`);
146
+ }
147
+ return tokenResponse;
148
+ }
149
+ /**
150
+ * Revoke an access token (§6.2).
151
+ */
152
+ async revokeToken(manageUrl, existingToken, privateKey, keyId) {
153
+ const headers = new Map();
154
+ headers.set("authorization", `GNAP ${existingToken}`);
155
+ const { signature, signatureInput } = await createHttpSignature({
156
+ privateKey,
157
+ keyId,
158
+ method: "DELETE",
159
+ targetUri: manageUrl,
160
+ headers,
161
+ hasBody: false,
162
+ });
163
+ const response = await fetch(manageUrl, {
164
+ method: "DELETE",
165
+ headers: {
166
+ Authorization: `GNAP ${existingToken}`,
167
+ Signature: signature,
168
+ "Signature-Input": signatureInput,
169
+ },
170
+ });
171
+ if (!response.ok) {
172
+ throw new GnapGrantError("revocation_failed", `Token revocation failed (HTTP ${response.status})`);
173
+ }
174
+ }
175
+ }
176
+ //# sourceMappingURL=gnapAccessTokenProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gnapAccessTokenProvider.js","sourceRoot":"","sources":["../src/gnapAccessTokenProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * GNAP Authentication Provider for Kiota.
3
+ *
4
+ * Implements the Kiota AuthenticationProvider interface to authenticate
5
+ * requests using the GNAP protocol (RFC 9635) with HTTP Message Signatures
6
+ * (RFC 9421).
7
+ *
8
+ * This is the main entry point for the package. Usage:
9
+ *
10
+ * ```typescript
11
+ * import { GnapAuthenticationProvider } from "@flarcos/kiota-authentication-gnap";
12
+ *
13
+ * const authProvider = new GnapAuthenticationProvider({
14
+ * authServerUrl: "https://auth.wallet.example/.well-known/...",
15
+ * privateKey: fs.readFileSync("private-key.pem", "utf-8"),
16
+ * clientIdentifier: "https://wallet.example/alice",
17
+ * accessRights: [{ type: "outgoing-payment", actions: ["create", "read"] }],
18
+ * });
19
+ *
20
+ * const adapter = new FetchRequestAdapter(authProvider);
21
+ * const client = new OpenPaymentsClient(adapter);
22
+ * ```
23
+ */
24
+ import type { AuthenticationProvider } from "@microsoft/kiota-abstractions";
25
+ import type { RequestInformation } from "@microsoft/kiota-abstractions";
26
+ import type { GnapAuthenticationProviderOptions, GnapAccessToken } from "./types.js";
27
+ export declare class GnapAuthenticationProvider implements AuthenticationProvider {
28
+ private readonly authServerUrl;
29
+ private readonly clientIdentifier;
30
+ private readonly accessRights;
31
+ private readonly interact;
32
+ private readonly interactionHandler?;
33
+ private readonly tokenStore;
34
+ private readonly allowedHosts;
35
+ private readonly tokenProvider;
36
+ private keyPromise;
37
+ private readonly rawKey;
38
+ constructor(options: GnapAuthenticationProviderOptions);
39
+ /**
40
+ * Lazily initialize the key material.
41
+ */
42
+ private getKeyMaterial;
43
+ /**
44
+ * Authenticate a Kiota request by:
45
+ * 1. Obtaining/caching a GNAP access token
46
+ * 2. Setting the Authorization: GNAP header
47
+ * 3. Computing Content-Digest for request bodies
48
+ * 4. Signing the request with HTTP Message Signatures
49
+ */
50
+ authenticateRequest: (request: RequestInformation, additionalAuthenticationContext?: Record<string, unknown>) => Promise<void>;
51
+ /**
52
+ * Get an access token — either from cache or by negotiating a new grant.
53
+ */
54
+ private getAccessToken;
55
+ /**
56
+ * Handle interactive grants (redirect-based user consent).
57
+ */
58
+ private handleInteraction;
59
+ /**
60
+ * Rotate the current access token.
61
+ */
62
+ rotateToken(): Promise<GnapAccessToken>;
63
+ /**
64
+ * Revoke the current access token.
65
+ */
66
+ revokeToken(): Promise<void>;
67
+ }
68
+ //# sourceMappingURL=gnapAuthenticationProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gnapAuthenticationProvider.d.ts","sourceRoot":"","sources":["../src/gnapAuthenticationProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAexE,OAAO,KAAK,EACV,iCAAiC,EAIjC,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB,qBAAa,0BAA2B,YAAW,sBAAsB;IACvE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoD;IACjF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgD;IACzE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAiB;IAC5C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAc;IAC3C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0B;IAGxD,OAAO,CAAC,UAAU,CAID;IACjB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;gBAEhC,OAAO,EAAE,iCAAiC;IAYtD;;OAEG;YACW,cAAc;IAW5B;;;;;;OAMG;IACH,mBAAmB,GACjB,SAAS,kBAAkB,EAC3B,kCAAkC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KACxD,OAAO,CAAC,IAAI,CAAC,CA0Dd;IAEF;;OAEG;YACW,cAAc;IAuC5B;;OAEG;YACW,iBAAiB;IAuC/B;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,eAAe,CAAC;IA8B7C;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;CAmBnC"}