@enbox/dwn-server 0.0.2 → 0.0.4
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 +3 -2
- package/README.md +115 -215
- package/dist/esm/src/admin/activity-log.d.ts +44 -0
- package/dist/esm/src/admin/activity-log.d.ts.map +1 -0
- package/dist/esm/src/admin/activity-log.js +85 -0
- package/dist/esm/src/admin/activity-log.js.map +1 -0
- package/dist/esm/src/admin/admin-api.d.ts +61 -0
- package/dist/esm/src/admin/admin-api.d.ts.map +1 -0
- package/dist/esm/src/admin/admin-api.js +1047 -0
- package/dist/esm/src/admin/admin-api.js.map +1 -0
- package/dist/esm/src/admin/admin-auth.d.ts +9 -0
- package/dist/esm/src/admin/admin-auth.d.ts.map +1 -0
- package/dist/esm/src/admin/admin-auth.js +45 -0
- package/dist/esm/src/admin/admin-auth.js.map +1 -0
- package/dist/esm/src/admin/admin-store.d.ts +111 -0
- package/dist/esm/src/admin/admin-store.d.ts.map +1 -0
- package/dist/esm/src/admin/admin-store.js +376 -0
- package/dist/esm/src/admin/admin-store.js.map +1 -0
- package/dist/esm/src/admin/audit-log.d.ts +94 -0
- package/dist/esm/src/admin/audit-log.d.ts.map +1 -0
- package/dist/esm/src/admin/audit-log.js +220 -0
- package/dist/esm/src/admin/audit-log.js.map +1 -0
- package/dist/esm/src/admin/index.d.ts +10 -0
- package/dist/esm/src/admin/index.d.ts.map +1 -0
- package/dist/esm/src/admin/index.js +7 -0
- package/dist/esm/src/admin/index.js.map +1 -0
- package/dist/esm/src/admin/types.d.ts +306 -0
- package/dist/esm/src/admin/types.d.ts.map +1 -0
- package/dist/esm/src/admin/types.js +2 -0
- package/dist/esm/src/admin/types.js.map +1 -0
- package/dist/esm/src/admin/webhook-manager.d.ts +55 -0
- package/dist/esm/src/admin/webhook-manager.d.ts.map +1 -0
- package/dist/esm/src/admin/webhook-manager.js +184 -0
- package/dist/esm/src/admin/webhook-manager.js.map +1 -0
- package/dist/esm/src/config.d.ts +124 -9
- package/dist/esm/src/config.d.ts.map +1 -1
- package/dist/esm/src/config.js +155 -13
- package/dist/esm/src/config.js.map +1 -1
- package/dist/esm/src/connection/connection-manager.d.ts +32 -9
- package/dist/esm/src/connection/connection-manager.d.ts.map +1 -1
- package/dist/esm/src/connection/connection-manager.js +38 -5
- package/dist/esm/src/connection/connection-manager.js.map +1 -1
- package/dist/esm/src/connection/flow-controller.d.ts +53 -0
- package/dist/esm/src/connection/flow-controller.d.ts.map +1 -0
- package/dist/esm/src/connection/flow-controller.js +101 -0
- package/dist/esm/src/connection/flow-controller.js.map +1 -0
- package/dist/esm/src/connection/socket-connection.d.ts +54 -18
- package/dist/esm/src/connection/socket-connection.d.ts.map +1 -1
- package/dist/esm/src/connection/socket-connection.js +102 -40
- package/dist/esm/src/connection/socket-connection.js.map +1 -1
- package/dist/esm/src/delivery-service.d.ts +43 -0
- package/dist/esm/src/delivery-service.d.ts.map +1 -0
- package/dist/esm/src/delivery-service.js +574 -0
- package/dist/esm/src/delivery-service.js.map +1 -0
- package/dist/esm/src/dwn-error.d.ts +10 -1
- package/dist/esm/src/dwn-error.d.ts.map +1 -1
- package/dist/esm/src/dwn-error.js +9 -0
- package/dist/esm/src/dwn-error.js.map +1 -1
- package/dist/esm/src/dwn-server.d.ts +13 -6
- package/dist/esm/src/dwn-server.d.ts.map +1 -1
- package/dist/esm/src/dwn-server.js +199 -24
- package/dist/esm/src/dwn-server.js.map +1 -1
- package/dist/esm/src/http-api.d.ts +28 -13
- package/dist/esm/src/http-api.d.ts.map +1 -1
- package/dist/esm/src/http-api.js +649 -374
- package/dist/esm/src/http-api.js.map +1 -1
- package/dist/esm/src/index.d.ts +6 -2
- package/dist/esm/src/index.d.ts.map +1 -1
- package/dist/esm/src/index.js +4 -1
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/json-rpc-api.js +2 -1
- package/dist/esm/src/json-rpc-api.js.map +1 -1
- package/dist/esm/src/json-rpc-handlers/dwn/process-message.d.ts.map +1 -1
- package/dist/esm/src/json-rpc-handlers/dwn/process-message.js +109 -7
- package/dist/esm/src/json-rpc-handlers/dwn/process-message.js.map +1 -1
- package/dist/esm/src/json-rpc-handlers/subscription/ack.d.ts +20 -0
- package/dist/esm/src/json-rpc-handlers/subscription/ack.d.ts.map +1 -0
- package/dist/esm/src/json-rpc-handlers/subscription/ack.js +41 -0
- package/dist/esm/src/json-rpc-handlers/subscription/ack.js.map +1 -0
- package/dist/esm/src/json-rpc-handlers/subscription/close.d.ts.map +1 -1
- package/dist/esm/src/json-rpc-handlers/subscription/close.js +1 -1
- package/dist/esm/src/json-rpc-handlers/subscription/close.js.map +1 -1
- package/dist/esm/src/json-rpc-handlers/subscription/index.d.ts +1 -0
- package/dist/esm/src/json-rpc-handlers/subscription/index.d.ts.map +1 -1
- package/dist/esm/src/json-rpc-handlers/subscription/index.js +1 -0
- package/dist/esm/src/json-rpc-handlers/subscription/index.js.map +1 -1
- package/dist/esm/src/lib/json-rpc-router.d.ts +25 -8
- package/dist/esm/src/lib/json-rpc-router.d.ts.map +1 -1
- package/dist/esm/src/lib/json-rpc-router.js.map +1 -1
- package/dist/esm/src/lib/sql-utils.d.ts +6 -0
- package/dist/esm/src/lib/sql-utils.d.ts.map +1 -0
- package/dist/esm/src/lib/sql-utils.js +8 -0
- package/dist/esm/src/lib/sql-utils.js.map +1 -0
- package/dist/esm/src/main.js +0 -6
- package/dist/esm/src/main.js.map +1 -1
- package/dist/esm/src/message-processed-hook.d.ts +35 -0
- package/dist/esm/src/message-processed-hook.d.ts.map +1 -0
- package/dist/esm/src/message-processed-hook.js +2 -0
- package/dist/esm/src/message-processed-hook.js.map +1 -0
- package/dist/esm/src/metrics.d.ts +14 -2
- package/dist/esm/src/metrics.d.ts.map +1 -1
- package/dist/esm/src/metrics.js +41 -1
- package/dist/esm/src/metrics.js.map +1 -1
- package/dist/esm/src/plugins/event-log-nats.d.ts +25 -0
- package/dist/esm/src/plugins/event-log-nats.d.ts.map +1 -0
- package/dist/esm/src/plugins/event-log-nats.js +379 -0
- package/dist/esm/src/plugins/event-log-nats.js.map +1 -0
- package/dist/esm/src/rate-limiter.d.ts +60 -0
- package/dist/esm/src/rate-limiter.d.ts.map +1 -0
- package/dist/esm/src/rate-limiter.js +116 -0
- package/dist/esm/src/rate-limiter.js.map +1 -0
- package/dist/esm/src/registration/jwt-provider-auth-plugin.d.ts +53 -0
- package/dist/esm/src/registration/jwt-provider-auth-plugin.d.ts.map +1 -0
- package/dist/esm/src/registration/jwt-provider-auth-plugin.js +90 -0
- package/dist/esm/src/registration/jwt-provider-auth-plugin.js.map +1 -0
- package/dist/esm/src/registration/open-auth-handler.d.ts +37 -0
- package/dist/esm/src/registration/open-auth-handler.d.ts.map +1 -0
- package/dist/esm/src/registration/open-auth-handler.js +214 -0
- package/dist/esm/src/registration/open-auth-handler.js.map +1 -0
- package/dist/esm/src/registration/proof-of-work-manager.d.ts +1 -1
- package/dist/esm/src/registration/proof-of-work-manager.d.ts.map +1 -1
- package/dist/esm/src/registration/proof-of-work-manager.js +3 -3
- package/dist/esm/src/registration/proof-of-work-manager.js.map +1 -1
- package/dist/esm/src/registration/provider-auth-plugin.d.ts +46 -0
- package/dist/esm/src/registration/provider-auth-plugin.d.ts.map +1 -0
- package/dist/esm/src/registration/provider-auth-plugin.js +29 -0
- package/dist/esm/src/registration/provider-auth-plugin.js.map +1 -0
- package/dist/esm/src/registration/registration-manager.d.ts +28 -5
- package/dist/esm/src/registration/registration-manager.d.ts.map +1 -1
- package/dist/esm/src/registration/registration-manager.js +83 -12
- package/dist/esm/src/registration/registration-manager.js.map +1 -1
- package/dist/esm/src/registration/registration-store.d.ts +83 -3
- package/dist/esm/src/registration/registration-store.d.ts.map +1 -1
- package/dist/esm/src/registration/registration-store.js +248 -11
- package/dist/esm/src/registration/registration-store.js.map +1 -1
- package/dist/esm/src/storage.d.ts +5 -5
- package/dist/esm/src/storage.d.ts.map +1 -1
- package/dist/esm/src/storage.js +105 -24
- package/dist/esm/src/storage.js.map +1 -1
- package/dist/esm/src/web5-connect/sql-ttl-cache.d.ts.map +1 -1
- package/dist/esm/src/web5-connect/sql-ttl-cache.js +11 -3
- package/dist/esm/src/web5-connect/sql-ttl-cache.js.map +1 -1
- package/dist/esm/src/web5-connect/web5-connect-server.d.ts.map +1 -1
- package/dist/esm/src/web5-connect/web5-connect-server.js +2 -2
- package/dist/esm/src/web5-connect/web5-connect-server.js.map +1 -1
- package/dist/esm/src/ws-api.d.ts +18 -4
- package/dist/esm/src/ws-api.d.ts.map +1 -1
- package/dist/esm/src/ws-api.js +12 -16
- package/dist/esm/src/ws-api.js.map +1 -1
- package/package.json +34 -53
- package/src/admin/activity-log.ts +100 -0
- package/src/admin/admin-api.ts +1308 -0
- package/src/admin/admin-auth.ts +56 -0
- package/src/admin/admin-store.ts +515 -0
- package/src/admin/audit-log.ts +327 -0
- package/src/admin/index.ts +34 -0
- package/src/admin/types.ts +352 -0
- package/src/admin/webhook-manager.ts +245 -0
- package/src/config.ts +190 -22
- package/src/connection/connection-manager.ts +67 -17
- package/src/connection/flow-controller.ts +117 -0
- package/src/connection/socket-connection.ts +144 -67
- package/src/delivery-service.ts +740 -0
- package/src/dwn-error.ts +11 -2
- package/src/dwn-server.ts +254 -39
- package/src/http-api.ts +736 -392
- package/src/index.ts +13 -2
- package/src/json-rpc-api.ts +2 -1
- package/src/json-rpc-handlers/dwn/process-message.ts +149 -15
- package/src/json-rpc-handlers/subscription/ack.ts +63 -0
- package/src/json-rpc-handlers/subscription/close.ts +5 -9
- package/src/json-rpc-handlers/subscription/index.ts +1 -0
- package/src/lib/json-rpc-router.ts +26 -11
- package/src/lib/sql-utils.ts +7 -0
- package/src/main.ts +0 -8
- package/src/message-processed-hook.ts +33 -0
- package/src/metrics.ts +57 -8
- package/src/plugins/event-log-nats.ts +466 -0
- package/src/process-handlers.ts +5 -5
- package/src/rate-limiter.ts +143 -0
- package/src/registration/jwt-provider-auth-plugin.ts +119 -0
- package/src/registration/open-auth-handler.ts +263 -0
- package/src/registration/proof-of-work-manager.ts +11 -10
- package/src/registration/provider-auth-plugin.ts +84 -0
- package/src/registration/registration-manager.ts +129 -31
- package/src/registration/registration-store.ts +332 -22
- package/src/storage.ts +136 -40
- package/src/web5-connect/sql-ttl-cache.ts +12 -5
- package/src/web5-connect/web5-connect-server.ts +9 -8
- package/src/ws-api.ts +39 -26
- package/dist/cjs/index.js +0 -6811
- package/dist/cjs/package.json +0 -1
- package/dist/esm/src/json-rpc-socket.d.ts +0 -39
- package/dist/esm/src/json-rpc-socket.d.ts.map +0 -1
- package/dist/esm/src/json-rpc-socket.js +0 -125
- package/dist/esm/src/json-rpc-socket.js.map +0 -1
- package/dist/esm/src/lib/http-server-shutdown-handler.d.ts +0 -10
- package/dist/esm/src/lib/http-server-shutdown-handler.d.ts.map +0 -1
- package/dist/esm/src/lib/http-server-shutdown-handler.js +0 -65
- package/dist/esm/src/lib/http-server-shutdown-handler.js.map +0 -1
- package/dist/esm/src/lib/json-rpc.d.ts +0 -54
- package/dist/esm/src/lib/json-rpc.d.ts.map +0 -1
- package/dist/esm/src/lib/json-rpc.js +0 -60
- package/dist/esm/src/lib/json-rpc.js.map +0 -1
- package/dist/esm/src/registration/proof-of-work-types.d.ts +0 -8
- package/dist/esm/src/registration/proof-of-work-types.d.ts.map +0 -1
- package/dist/esm/src/registration/proof-of-work-types.js +0 -2
- package/dist/esm/src/registration/proof-of-work-types.js.map +0 -1
- package/dist/esm/src/registration/registration-types.d.ts +0 -18
- package/dist/esm/src/registration/registration-types.d.ts.map +0 -1
- package/dist/esm/src/registration/registration-types.js +0 -2
- package/dist/esm/src/registration/registration-types.js.map +0 -1
- package/dist/esm/tests/common-scenario-validator.d.ts +0 -11
- package/dist/esm/tests/common-scenario-validator.d.ts.map +0 -1
- package/dist/esm/tests/common-scenario-validator.js +0 -114
- package/dist/esm/tests/common-scenario-validator.js.map +0 -1
- package/dist/esm/tests/connection/connection-manager.spec.d.ts +0 -2
- package/dist/esm/tests/connection/connection-manager.spec.d.ts.map +0 -1
- package/dist/esm/tests/connection/connection-manager.spec.js +0 -47
- package/dist/esm/tests/connection/connection-manager.spec.js.map +0 -1
- package/dist/esm/tests/connection/socket-connection.spec.d.ts +0 -2
- package/dist/esm/tests/connection/socket-connection.spec.d.ts.map +0 -1
- package/dist/esm/tests/connection/socket-connection.spec.js +0 -125
- package/dist/esm/tests/connection/socket-connection.spec.js.map +0 -1
- package/dist/esm/tests/cors/http-api.browser.d.ts +0 -2
- package/dist/esm/tests/cors/http-api.browser.d.ts.map +0 -1
- package/dist/esm/tests/cors/http-api.browser.js +0 -60
- package/dist/esm/tests/cors/http-api.browser.js.map +0 -1
- package/dist/esm/tests/cors/ping.browser.d.ts +0 -2
- package/dist/esm/tests/cors/ping.browser.d.ts.map +0 -1
- package/dist/esm/tests/cors/ping.browser.js +0 -7
- package/dist/esm/tests/cors/ping.browser.js.map +0 -1
- package/dist/esm/tests/dwn-process-message.spec.d.ts +0 -2
- package/dist/esm/tests/dwn-process-message.spec.d.ts.map +0 -1
- package/dist/esm/tests/dwn-process-message.spec.js +0 -172
- package/dist/esm/tests/dwn-process-message.spec.js.map +0 -1
- package/dist/esm/tests/dwn-server.spec.d.ts +0 -2
- package/dist/esm/tests/dwn-server.spec.d.ts.map +0 -1
- package/dist/esm/tests/dwn-server.spec.js +0 -49
- package/dist/esm/tests/dwn-server.spec.js.map +0 -1
- package/dist/esm/tests/http-api.spec.d.ts +0 -2
- package/dist/esm/tests/http-api.spec.d.ts.map +0 -1
- package/dist/esm/tests/http-api.spec.js +0 -775
- package/dist/esm/tests/http-api.spec.js.map +0 -1
- package/dist/esm/tests/json-rpc-socket.spec.d.ts +0 -2
- package/dist/esm/tests/json-rpc-socket.spec.d.ts.map +0 -1
- package/dist/esm/tests/json-rpc-socket.spec.js +0 -225
- package/dist/esm/tests/json-rpc-socket.spec.js.map +0 -1
- package/dist/esm/tests/plugins/data-store-sqlite.d.ts +0 -17
- package/dist/esm/tests/plugins/data-store-sqlite.d.ts.map +0 -1
- package/dist/esm/tests/plugins/data-store-sqlite.js +0 -23
- package/dist/esm/tests/plugins/data-store-sqlite.js.map +0 -1
- package/dist/esm/tests/plugins/event-log-sqlite.d.ts +0 -17
- package/dist/esm/tests/plugins/event-log-sqlite.d.ts.map +0 -1
- package/dist/esm/tests/plugins/event-log-sqlite.js +0 -23
- package/dist/esm/tests/plugins/event-log-sqlite.js.map +0 -1
- package/dist/esm/tests/plugins/event-stream-in-memory.d.ts +0 -17
- package/dist/esm/tests/plugins/event-stream-in-memory.d.ts.map +0 -1
- package/dist/esm/tests/plugins/event-stream-in-memory.js +0 -21
- package/dist/esm/tests/plugins/event-stream-in-memory.js.map +0 -1
- package/dist/esm/tests/plugins/message-store-sqlite.d.ts +0 -17
- package/dist/esm/tests/plugins/message-store-sqlite.d.ts.map +0 -1
- package/dist/esm/tests/plugins/message-store-sqlite.js +0 -23
- package/dist/esm/tests/plugins/message-store-sqlite.js.map +0 -1
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.d.ts +0 -17
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.d.ts.map +0 -1
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.js +0 -23
- package/dist/esm/tests/plugins/resumable-task-store-sqlite.js.map +0 -1
- package/dist/esm/tests/process-handler.spec.d.ts +0 -2
- package/dist/esm/tests/process-handler.spec.d.ts.map +0 -1
- package/dist/esm/tests/process-handler.spec.js +0 -60
- package/dist/esm/tests/process-handler.spec.js.map +0 -1
- package/dist/esm/tests/registration/proof-of-work-manager.spec.d.ts +0 -2
- package/dist/esm/tests/registration/proof-of-work-manager.spec.d.ts.map +0 -1
- package/dist/esm/tests/registration/proof-of-work-manager.spec.js +0 -157
- package/dist/esm/tests/registration/proof-of-work-manager.spec.js.map +0 -1
- package/dist/esm/tests/rpc-subscribe-close.spec.d.ts +0 -2
- package/dist/esm/tests/rpc-subscribe-close.spec.d.ts.map +0 -1
- package/dist/esm/tests/rpc-subscribe-close.spec.js +0 -81
- package/dist/esm/tests/rpc-subscribe-close.spec.js.map +0 -1
- package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.d.ts +0 -2
- package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.d.ts.map +0 -1
- package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.js +0 -73
- package/dist/esm/tests/scenarios/dynamic-plugin-loading.spec.js.map +0 -1
- package/dist/esm/tests/scenarios/registration.spec.d.ts +0 -2
- package/dist/esm/tests/scenarios/registration.spec.d.ts.map +0 -1
- package/dist/esm/tests/scenarios/registration.spec.js +0 -507
- package/dist/esm/tests/scenarios/registration.spec.js.map +0 -1
- package/dist/esm/tests/scenarios/web5-connect.spec.d.ts +0 -2
- package/dist/esm/tests/scenarios/web5-connect.spec.d.ts.map +0 -1
- package/dist/esm/tests/scenarios/web5-connect.spec.js +0 -137
- package/dist/esm/tests/scenarios/web5-connect.spec.js.map +0 -1
- package/dist/esm/tests/test-dwn.d.ts +0 -7
- package/dist/esm/tests/test-dwn.d.ts.map +0 -1
- package/dist/esm/tests/test-dwn.js +0 -34
- package/dist/esm/tests/test-dwn.js.map +0 -1
- package/dist/esm/tests/utils.d.ts +0 -46
- package/dist/esm/tests/utils.d.ts.map +0 -1
- package/dist/esm/tests/utils.js +0 -116
- package/dist/esm/tests/utils.js.map +0 -1
- package/dist/esm/tests/ws-api.spec.d.ts +0 -2
- package/dist/esm/tests/ws-api.spec.d.ts.map +0 -1
- package/dist/esm/tests/ws-api.spec.js +0 -327
- package/dist/esm/tests/ws-api.spec.js.map +0 -1
- package/src/json-rpc-socket.ts +0 -155
- package/src/lib/http-server-shutdown-handler.ts +0 -79
- package/src/lib/json-rpc.ts +0 -126
- package/src/registration/proof-of-work-types.ts +0 -7
- package/src/registration/registration-types.ts +0 -18
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { ProviderAuthPlugin, ProviderAuthValidationResult } from './provider-auth-plugin.js';
|
|
2
|
+
|
|
3
|
+
import * as jose from 'jose';
|
|
4
|
+
import log from 'loglevel';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Options for creating a {@link JwtProviderAuthPlugin}.
|
|
8
|
+
* Exactly one of `secret` or `jwksUrl` must be provided.
|
|
9
|
+
*/
|
|
10
|
+
export type JwtProviderAuthPluginOptions = {
|
|
11
|
+
/** HMAC shared secret for HS256/HS384/HS512 tokens. */
|
|
12
|
+
secret? : string;
|
|
13
|
+
/** JWKS endpoint URL for RS256/ES256/EdDSA tokens. */
|
|
14
|
+
jwksUrl? : string;
|
|
15
|
+
/** Expected JWT `iss` claim. If set, tokens without a matching issuer are rejected. */
|
|
16
|
+
issuer? : string;
|
|
17
|
+
/** Expected JWT `aud` claim. If set, tokens without a matching audience are rejected. */
|
|
18
|
+
audience? : string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Built-in {@link ProviderAuthPlugin} that validates JWT registration tokens.
|
|
23
|
+
*
|
|
24
|
+
* Supports two modes:
|
|
25
|
+
* - **HMAC secret** (`DWN_PROVIDER_AUTH_JWT_SECRET`): symmetric HS256 verification.
|
|
26
|
+
* - **JWKS URL** (`DWN_PROVIDER_AUTH_JWT_JWKS_URL`): asymmetric verification via
|
|
27
|
+
* a remote JWKS endpoint (RS256, ES256, EdDSA, etc.). Keys are cached and
|
|
28
|
+
* rotated automatically by `jose`.
|
|
29
|
+
*
|
|
30
|
+
* The JWT payload may include:
|
|
31
|
+
* - `sub` — mapped to `accountId` in the validation result.
|
|
32
|
+
* - `metadata` — arbitrary provider-defined object stored with the tenant.
|
|
33
|
+
*
|
|
34
|
+
* This plugin ships with `@enbox/dwn-server` and requires no external code.
|
|
35
|
+
* Providers who need custom validation logic can implement their own
|
|
36
|
+
* {@link ProviderAuthPlugin} instead.
|
|
37
|
+
*/
|
|
38
|
+
export class JwtProviderAuthPlugin implements ProviderAuthPlugin {
|
|
39
|
+
#getKey: Uint8Array | jose.JWTVerifyGetKey;
|
|
40
|
+
#issuer: string | undefined;
|
|
41
|
+
#audience: string | undefined;
|
|
42
|
+
|
|
43
|
+
private constructor(
|
|
44
|
+
getKey: Uint8Array | jose.JWTVerifyGetKey,
|
|
45
|
+
issuer?: string,
|
|
46
|
+
audience?: string,
|
|
47
|
+
) {
|
|
48
|
+
this.#getKey = getKey;
|
|
49
|
+
this.#issuer = issuer;
|
|
50
|
+
this.#audience = audience;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Create a {@link JwtProviderAuthPlugin} from options.
|
|
55
|
+
*
|
|
56
|
+
* @param options - Must include exactly one of `secret` or `jwksUrl`.
|
|
57
|
+
* @throws If neither `secret` nor `jwksUrl` is provided.
|
|
58
|
+
*/
|
|
59
|
+
public static async create(options: JwtProviderAuthPluginOptions): Promise<JwtProviderAuthPlugin> {
|
|
60
|
+
if (!options.secret && !options.jwksUrl) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
'JwtProviderAuthPlugin: exactly one of `secret` or `jwksUrl` must be provided.',
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let getKey: any;
|
|
67
|
+
if (options.secret) {
|
|
68
|
+
getKey = new TextEncoder().encode(options.secret);
|
|
69
|
+
} else {
|
|
70
|
+
getKey = jose.createRemoteJWKSet(new URL(options.jwksUrl!));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return new JwtProviderAuthPlugin(getKey, options.issuer, options.audience);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Create a {@link JwtProviderAuthPlugin} from environment variables.
|
|
78
|
+
*
|
|
79
|
+
* Reads `DWN_PROVIDER_AUTH_JWT_SECRET`, `DWN_PROVIDER_AUTH_JWT_JWKS_URL`,
|
|
80
|
+
* and optionally `DWN_PROVIDER_AUTH_JWT_ISSUER` / `DWN_PROVIDER_AUTH_JWT_AUDIENCE`.
|
|
81
|
+
*/
|
|
82
|
+
public static async fromEnv(): Promise<JwtProviderAuthPlugin> {
|
|
83
|
+
return JwtProviderAuthPlugin.create({
|
|
84
|
+
secret : process.env.DWN_PROVIDER_AUTH_JWT_SECRET,
|
|
85
|
+
jwksUrl : process.env.DWN_PROVIDER_AUTH_JWT_JWKS_URL,
|
|
86
|
+
issuer : process.env.DWN_PROVIDER_AUTH_JWT_ISSUER,
|
|
87
|
+
audience : process.env.DWN_PROVIDER_AUTH_JWT_AUDIENCE,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** @inheritdoc */
|
|
92
|
+
public async validateRegistrationToken(token: string): Promise<ProviderAuthValidationResult> {
|
|
93
|
+
try {
|
|
94
|
+
const verifyOptions: jose.JWTVerifyOptions = {};
|
|
95
|
+
if (this.#issuer !== undefined) {
|
|
96
|
+
verifyOptions.issuer = this.#issuer;
|
|
97
|
+
}
|
|
98
|
+
if (this.#audience !== undefined) {
|
|
99
|
+
verifyOptions.audience = this.#audience;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const { payload } = await jose.jwtVerify(token, this.#getKey as any, verifyOptions);
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
isValid : true,
|
|
106
|
+
accountId : typeof payload.sub === 'string' ? payload.sub : undefined,
|
|
107
|
+
metadata : typeof payload.metadata === 'object' && payload.metadata !== null
|
|
108
|
+
? payload.metadata as Record<string, unknown>
|
|
109
|
+
: undefined,
|
|
110
|
+
};
|
|
111
|
+
} catch (error) {
|
|
112
|
+
log.debug('JWT validation failed:', (error as Error).message);
|
|
113
|
+
return {
|
|
114
|
+
isValid : false,
|
|
115
|
+
detail : (error as Error).message,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import * as jose from 'jose';
|
|
2
|
+
import log from 'loglevel';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Built-in "open auth" handler for DWN servers that want registration
|
|
6
|
+
* without any user authentication (open-to-all).
|
|
7
|
+
*
|
|
8
|
+
* Implements the provider-auth-v0 authorize / token / refresh endpoints
|
|
9
|
+
* locally on the DWN server. The authorize endpoint immediately returns
|
|
10
|
+
* an authorization code (no user interaction), the token endpoint exchanges
|
|
11
|
+
* it for a JWT registration token, and the refresh endpoint issues a new token.
|
|
12
|
+
*
|
|
13
|
+
* This is the simplest possible auth backend — suitable for free/public DWN
|
|
14
|
+
* providers. Providers who want real authentication (passkey, OIDC, etc.)
|
|
15
|
+
* host their own auth service and point `providerAuth.authorizeUrl` /
|
|
16
|
+
* `tokenUrl` to it instead.
|
|
17
|
+
*
|
|
18
|
+
* The JWTs issued here are verified by {@link JwtProviderAuthPlugin} using
|
|
19
|
+
* the same shared secret (`DWN_PROVIDER_AUTH_JWT_SECRET`).
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Maximum number of pending authorization codes held in memory.
|
|
23
|
+
* When the limit is reached, new authorize requests are rejected with 503.
|
|
24
|
+
*/
|
|
25
|
+
const MAX_PENDING_CODES = 10_000;
|
|
26
|
+
|
|
27
|
+
/** Interval (ms) at which expired authorization codes are purged. */
|
|
28
|
+
const CLEANUP_INTERVAL_MS = 60_000;
|
|
29
|
+
|
|
30
|
+
export class OpenAuthHandler {
|
|
31
|
+
#secret: Uint8Array;
|
|
32
|
+
#issuer: string;
|
|
33
|
+
/** Pending authorization codes. Maps code → { redirectUri, expiresAt }. */
|
|
34
|
+
#pendingCodes: Map<string, { redirectUri: string; expiresAt: number }>;
|
|
35
|
+
/** Registration token TTL in seconds. Default: 1 year. */
|
|
36
|
+
#tokenTtlSeconds: number;
|
|
37
|
+
/** Periodic cleanup timer for expired codes. */
|
|
38
|
+
#cleanupTimer: ReturnType<typeof setInterval>;
|
|
39
|
+
|
|
40
|
+
private constructor(secret: Uint8Array, issuer: string, tokenTtlSeconds: number) {
|
|
41
|
+
this.#secret = secret;
|
|
42
|
+
this.#issuer = issuer;
|
|
43
|
+
this.#pendingCodes = new Map();
|
|
44
|
+
this.#tokenTtlSeconds = tokenTtlSeconds;
|
|
45
|
+
|
|
46
|
+
// Periodically purge expired codes so memory does not grow unbounded
|
|
47
|
+
// even when no new authorize requests arrive.
|
|
48
|
+
this.#cleanupTimer = setInterval(() => this.#cleanExpiredCodes(), CLEANUP_INTERVAL_MS);
|
|
49
|
+
// Allow the process to exit even if the timer is still running.
|
|
50
|
+
if (this.#cleanupTimer.unref) {
|
|
51
|
+
this.#cleanupTimer.unref();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create an {@link OpenAuthHandler}.
|
|
57
|
+
*
|
|
58
|
+
* @param secret - HMAC shared secret (same as `DWN_PROVIDER_AUTH_JWT_SECRET`).
|
|
59
|
+
* @param issuer - JWT issuer claim (typically the DWN server's base URL).
|
|
60
|
+
* @param tokenTtlSeconds - Registration token lifetime in seconds. Default: 1 year.
|
|
61
|
+
*/
|
|
62
|
+
public static create(secret: string, issuer: string, tokenTtlSeconds = 365 * 24 * 60 * 60): OpenAuthHandler {
|
|
63
|
+
return new OpenAuthHandler(
|
|
64
|
+
new TextEncoder().encode(secret),
|
|
65
|
+
issuer,
|
|
66
|
+
tokenTtlSeconds,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Handle `GET /provider-auth/authorize`.
|
|
72
|
+
*
|
|
73
|
+
* In open-auth mode this immediately generates a code and redirects back
|
|
74
|
+
* to the `redirect_uri` with `code` and `state` query parameters.
|
|
75
|
+
* No user interaction is required.
|
|
76
|
+
*/
|
|
77
|
+
public handleAuthorize(url: URL): Response {
|
|
78
|
+
const redirectUri = url.searchParams.get('redirect_uri');
|
|
79
|
+
const state = url.searchParams.get('state');
|
|
80
|
+
|
|
81
|
+
if (!redirectUri) {
|
|
82
|
+
return Response.json(
|
|
83
|
+
{ error: 'missing redirect_uri parameter' },
|
|
84
|
+
{ status: 400 },
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Reject new codes when the map is at capacity to prevent memory exhaustion.
|
|
89
|
+
if (this.#pendingCodes.size >= MAX_PENDING_CODES) {
|
|
90
|
+
log.warn(`OpenAuthHandler: pending codes map is full (${MAX_PENDING_CODES}), rejecting authorize request`);
|
|
91
|
+
return Response.json(
|
|
92
|
+
{ error: 'server is busy, try again later' },
|
|
93
|
+
{ status: 503 },
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Generate a random authorization code.
|
|
98
|
+
const code = crypto.randomUUID();
|
|
99
|
+
|
|
100
|
+
// Store the code with a 10-minute expiry.
|
|
101
|
+
this.#pendingCodes.set(code, {
|
|
102
|
+
redirectUri,
|
|
103
|
+
expiresAt: Date.now() + 10 * 60 * 1000,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Periodically clean up expired codes (simple inline sweep).
|
|
107
|
+
this.#cleanExpiredCodes();
|
|
108
|
+
|
|
109
|
+
// Build redirect URL.
|
|
110
|
+
const redirect = new URL(redirectUri);
|
|
111
|
+
redirect.searchParams.set('code', code);
|
|
112
|
+
if (state) {
|
|
113
|
+
redirect.searchParams.set('state', state);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
log.debug(`OpenAuthHandler: issued code ${code.slice(0, 8)}… for redirect to ${redirectUri}`);
|
|
117
|
+
|
|
118
|
+
return Response.json({
|
|
119
|
+
code,
|
|
120
|
+
state : state ?? undefined,
|
|
121
|
+
redirectUri : redirect.toString(),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Handle `POST /provider-auth/token`.
|
|
127
|
+
*
|
|
128
|
+
* Exchanges an authorization code for a JWT registration token.
|
|
129
|
+
* Request body: `{ code: string, redirectUri: string }`.
|
|
130
|
+
*/
|
|
131
|
+
public async handleToken(req: Request): Promise<Response> {
|
|
132
|
+
let body: { code?: string; redirectUri?: string };
|
|
133
|
+
try {
|
|
134
|
+
body = await req.json() as { code?: string; redirectUri?: string };
|
|
135
|
+
} catch {
|
|
136
|
+
return Response.json({ error: 'invalid JSON body' }, { status: 400 });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const { code, redirectUri } = body;
|
|
140
|
+
if (!code || !redirectUri) {
|
|
141
|
+
return Response.json(
|
|
142
|
+
{ error: 'missing code or redirectUri' },
|
|
143
|
+
{ status: 400 },
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Validate the authorization code.
|
|
148
|
+
const pending = this.#pendingCodes.get(code);
|
|
149
|
+
if (!pending) {
|
|
150
|
+
return Response.json({ error: 'invalid or expired code' }, { status: 400 });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (pending.redirectUri !== redirectUri) {
|
|
154
|
+
return Response.json({ error: 'redirect_uri mismatch' }, { status: 400 });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (Date.now() > pending.expiresAt) {
|
|
158
|
+
this.#pendingCodes.delete(code);
|
|
159
|
+
return Response.json({ error: 'code expired' }, { status: 400 });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Consume the code (one-time use).
|
|
163
|
+
this.#pendingCodes.delete(code);
|
|
164
|
+
|
|
165
|
+
// Generate an account ID for this session (open auth = anonymous accounts).
|
|
166
|
+
const accountId = crypto.randomUUID();
|
|
167
|
+
|
|
168
|
+
// Issue JWT registration token.
|
|
169
|
+
const registrationToken = await this.#signToken(accountId, this.#tokenTtlSeconds);
|
|
170
|
+
|
|
171
|
+
// Issue refresh token with longer TTL (2x).
|
|
172
|
+
const refreshToken = await this.#signToken(accountId, this.#tokenTtlSeconds * 2, 'refresh');
|
|
173
|
+
|
|
174
|
+
log.debug(`OpenAuthHandler: exchanged code for account ${accountId.slice(0, 8)}…`);
|
|
175
|
+
|
|
176
|
+
return Response.json({
|
|
177
|
+
registrationToken,
|
|
178
|
+
refreshToken,
|
|
179
|
+
expiresIn : this.#tokenTtlSeconds,
|
|
180
|
+
tokenType : 'bearer',
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Handle `POST /provider-auth/refresh`.
|
|
186
|
+
*
|
|
187
|
+
* Exchanges a refresh token for a new registration token.
|
|
188
|
+
* Request body: `{ refreshToken: string }`.
|
|
189
|
+
*/
|
|
190
|
+
public async handleRefresh(req: Request): Promise<Response> {
|
|
191
|
+
let body: { refreshToken?: string };
|
|
192
|
+
try {
|
|
193
|
+
body = await req.json() as { refreshToken?: string };
|
|
194
|
+
} catch {
|
|
195
|
+
return Response.json({ error: 'invalid JSON body' }, { status: 400 });
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (!body.refreshToken) {
|
|
199
|
+
return Response.json({ error: 'missing refreshToken' }, { status: 400 });
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Verify the refresh token.
|
|
203
|
+
let payload: jose.JWTPayload;
|
|
204
|
+
try {
|
|
205
|
+
const result = await jose.jwtVerify(body.refreshToken, this.#secret, {
|
|
206
|
+
issuer: this.#issuer,
|
|
207
|
+
});
|
|
208
|
+
payload = result.payload;
|
|
209
|
+
} catch (error) {
|
|
210
|
+
return Response.json(
|
|
211
|
+
{ error: `invalid refresh token: ${(error as Error).message}` },
|
|
212
|
+
{ status: 400 },
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (payload.purpose !== 'refresh') {
|
|
217
|
+
return Response.json({ error: 'token is not a refresh token' }, { status: 400 });
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const accountId = payload.sub ?? crypto.randomUUID();
|
|
221
|
+
|
|
222
|
+
// Issue new tokens.
|
|
223
|
+
const registrationToken = await this.#signToken(accountId, this.#tokenTtlSeconds);
|
|
224
|
+
const refreshToken = await this.#signToken(accountId, this.#tokenTtlSeconds * 2, 'refresh');
|
|
225
|
+
|
|
226
|
+
log.debug(`OpenAuthHandler: refreshed token for account ${accountId.slice(0, 8)}…`);
|
|
227
|
+
|
|
228
|
+
return Response.json({
|
|
229
|
+
registrationToken,
|
|
230
|
+
refreshToken,
|
|
231
|
+
expiresIn : this.#tokenTtlSeconds,
|
|
232
|
+
tokenType : 'bearer',
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Sign a JWT with the shared secret. */
|
|
237
|
+
async #signToken(accountId: string, ttlSeconds: number, purpose = 'registration'): Promise<string> {
|
|
238
|
+
return new jose.SignJWT({ purpose })
|
|
239
|
+
.setProtectedHeader({ alg: 'HS256' })
|
|
240
|
+
.setIssuer(this.#issuer)
|
|
241
|
+
.setAudience(this.#issuer)
|
|
242
|
+
.setSubject(accountId)
|
|
243
|
+
.setIssuedAt()
|
|
244
|
+
.setExpirationTime(`${ttlSeconds}s`)
|
|
245
|
+
.sign(this.#secret);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Stops the periodic cleanup timer and clears all pending codes. */
|
|
249
|
+
public destroy(): void {
|
|
250
|
+
clearInterval(this.#cleanupTimer);
|
|
251
|
+
this.#pendingCodes.clear();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Remove expired authorization codes from the pending map. */
|
|
255
|
+
#cleanExpiredCodes(): void {
|
|
256
|
+
const now = Date.now();
|
|
257
|
+
for (const [code, data] of this.#pendingCodes) {
|
|
258
|
+
if (now > data.expiresAt) {
|
|
259
|
+
this.#pendingCodes.delete(code);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { ProofOfWork } from
|
|
1
|
+
import type { ProofOfWorkChallengeModel } from '@enbox/dwn-clients';
|
|
2
|
+
|
|
3
|
+
import { ProofOfWork } from './proof-of-work.js';
|
|
4
|
+
import { DwnServerError, DwnServerErrorCode } from '../dwn-error.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Manages proof-of-work challenge difficulty and lifecycle based on solve rate.
|
|
@@ -18,7 +19,7 @@ export class ProofOfWorkManager {
|
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
// There is opportunity to improve implementation here.
|
|
21
|
-
// TODO: https://github.com/
|
|
22
|
+
// TODO: https://github.com/enboxorg/enbox/issues/101
|
|
22
23
|
private proofOfWorkOfLastMinute: Map<string, number> = new Map(); // proofOfWorkId -> timestamp of proof-of-work
|
|
23
24
|
|
|
24
25
|
// Seed to generate the challenge nonce from, this allows all DWN instances in a cluster to generate the same challenge.
|
|
@@ -122,8 +123,8 @@ export class ProofOfWorkManager {
|
|
|
122
123
|
|
|
123
124
|
public getProofOfWorkChallenge(): ProofOfWorkChallengeModel {
|
|
124
125
|
return {
|
|
125
|
-
challengeNonce: this.challengeNonces.currentChallengeNonce,
|
|
126
|
-
maximumAllowedHashValue: ProofOfWorkManager.bigIntToHexString(this.currentMaximumAllowedHashValue),
|
|
126
|
+
challengeNonce : this.challengeNonces.currentChallengeNonce,
|
|
127
|
+
maximumAllowedHashValue : ProofOfWorkManager.bigIntToHexString(this.currentMaximumAllowedHashValue),
|
|
127
128
|
};
|
|
128
129
|
}
|
|
129
130
|
|
|
@@ -228,7 +229,7 @@ export class ProofOfWorkManager {
|
|
|
228
229
|
/**
|
|
229
230
|
* Refreshes the difficulty by changing the max hash value.
|
|
230
231
|
* The higher the number, the easier. Scale 1 (hardest) to 2^256 (easiest), represented in HEX.
|
|
231
|
-
*
|
|
232
|
+
*
|
|
232
233
|
* If solve rate rate is higher than expected, the difficulty will increase rapidly.
|
|
233
234
|
* If solve rate is lower than expected, the difficulty will decrease gradually.
|
|
234
235
|
* The difficulty will never be lower than the initial difficulty.
|
|
@@ -247,16 +248,16 @@ export class ProofOfWorkManager {
|
|
|
247
248
|
// and harder difficulty is represented by a smaller max allowed hash value.
|
|
248
249
|
if (latestSolveCountPerMinute > this.desiredSolveCountPerMinute) {
|
|
249
250
|
// if solve rate is higher than desired, make difficulty harder by making the max allowed hash value smaller
|
|
250
|
-
|
|
251
|
+
|
|
251
252
|
const currentSolveRateInFractionOfDesiredSolveRate = latestSolveCountPerMinute / this.desiredSolveCountPerMinute;
|
|
252
253
|
const newMaximumAllowedHashValueAsBigIntPriorToMultiplierAdjustment
|
|
253
|
-
= (this.currentMaximumAllowedHashValueAsBigInt * BigInt(scaleFactor)) /
|
|
254
|
+
= (this.currentMaximumAllowedHashValueAsBigInt * BigInt(scaleFactor)) /
|
|
254
255
|
(BigInt(Math.floor(currentSolveRateInFractionOfDesiredSolveRate * this.difficultyIncreaseMultiplier * scaleFactor)));
|
|
255
256
|
|
|
256
257
|
const hashValueDecreaseAmountPriorToEvaluationFrequencyAdjustment
|
|
257
258
|
= (this.currentMaximumAllowedHashValueAsBigInt - newMaximumAllowedHashValueAsBigIntPriorToMultiplierAdjustment) *
|
|
258
259
|
(BigInt(Math.floor(this.difficultyIncreaseMultiplier * scaleFactor)) / BigInt(scaleFactor));
|
|
259
|
-
|
|
260
|
+
|
|
260
261
|
// Adjustment based on the reevaluation frequency to provide more-or-less consistent behavior regardless of the reevaluation frequency.
|
|
261
262
|
const hashValueDecreaseAmount = hashValueDecreaseAmountPriorToEvaluationFrequencyAdjustment / BigInt(difficultyEvaluationsPerMinute);
|
|
262
263
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { DwnServerError, DwnServerErrorCode } from '../dwn-error.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interface for validating provider auth registration tokens.
|
|
5
|
+
* DWN server operators implement this to integrate with their auth service.
|
|
6
|
+
*
|
|
7
|
+
* The token is intentionally opaque — it could be a JWT, blind RSA token,
|
|
8
|
+
* ecash token, or any other format. The plugin is responsible for all
|
|
9
|
+
* validation logic.
|
|
10
|
+
*/
|
|
11
|
+
export interface ProviderAuthPlugin {
|
|
12
|
+
/**
|
|
13
|
+
* Validate a registration token presented during DID registration.
|
|
14
|
+
*
|
|
15
|
+
* @param token - The opaque registration token from the client
|
|
16
|
+
* @returns Validation result with optional account metadata
|
|
17
|
+
*/
|
|
18
|
+
validateRegistrationToken(token: string): Promise<ProviderAuthValidationResult>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Result of validating a provider auth registration token.
|
|
23
|
+
*/
|
|
24
|
+
export type ProviderAuthValidationResult = {
|
|
25
|
+
/** Whether the token is valid and the registration should proceed. */
|
|
26
|
+
isValid : boolean;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Optional account identifier that this DID registration is associated with.
|
|
30
|
+
* Omit for privacy-preserving providers where DID-to-account correlation
|
|
31
|
+
* should not be stored.
|
|
32
|
+
*/
|
|
33
|
+
accountId? : string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Optional provider-defined metadata to store with the tenant registration.
|
|
37
|
+
* Can include plan details, quotas, or any other provider-specific data.
|
|
38
|
+
* Stored as JSON in the registeredTenants table.
|
|
39
|
+
*/
|
|
40
|
+
metadata? : Record<string, unknown>;
|
|
41
|
+
|
|
42
|
+
/** Error detail message when isValid is false. */
|
|
43
|
+
detail? : string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Load a ProviderAuthPlugin from a file path.
|
|
48
|
+
* The module must export a default class or object that implements ProviderAuthPlugin.
|
|
49
|
+
*
|
|
50
|
+
* Follows the same pattern as the existing PluginLoader.
|
|
51
|
+
*/
|
|
52
|
+
export async function loadProviderAuthPlugin(pluginPath: string): Promise<ProviderAuthPlugin> {
|
|
53
|
+
let module: any;
|
|
54
|
+
try {
|
|
55
|
+
module = await import(pluginPath);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
throw new DwnServerError(
|
|
58
|
+
DwnServerErrorCode.ProviderAuthPluginLoadFailed,
|
|
59
|
+
`Failed to load provider auth plugin at ${pluginPath}: ${(error as Error).message}`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const plugin = module.default;
|
|
64
|
+
if (plugin === undefined) {
|
|
65
|
+
throw new DwnServerError(
|
|
66
|
+
DwnServerErrorCode.ProviderAuthPluginLoadFailed,
|
|
67
|
+
`Provider auth plugin at ${pluginPath} does not have a default export.`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Support both class (instantiate) and plain object exports.
|
|
72
|
+
const instance: ProviderAuthPlugin = typeof plugin === 'function'
|
|
73
|
+
? new plugin() as ProviderAuthPlugin
|
|
74
|
+
: plugin as ProviderAuthPlugin;
|
|
75
|
+
|
|
76
|
+
if (typeof instance.validateRegistrationToken !== 'function') {
|
|
77
|
+
throw new DwnServerError(
|
|
78
|
+
DwnServerErrorCode.ProviderAuthPluginLoadFailed,
|
|
79
|
+
`Provider auth plugin at ${pluginPath} does not implement validateRegistrationToken().`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return instance;
|
|
84
|
+
}
|