@opendatalabs/personal-server-ts-core 1.33.7 → 1.34.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/dist/additions/budget.d.ts +28 -0
- package/dist/additions/budget.d.ts.map +1 -0
- package/dist/additions/budget.js +119 -0
- package/dist/additions/budget.js.map +1 -0
- package/dist/additions/durable.d.ts +64 -0
- package/dist/additions/durable.d.ts.map +1 -0
- package/dist/additions/durable.js +591 -0
- package/dist/additions/durable.js.map +1 -0
- package/dist/additions/first-added.d.ts +32 -48
- package/dist/additions/first-added.d.ts.map +1 -1
- package/dist/additions/first-added.js +68 -123
- package/dist/additions/first-added.js.map +1 -1
- package/dist/additions/index.d.ts +3 -0
- package/dist/additions/index.d.ts.map +1 -1
- package/dist/additions/index.js +3 -0
- package/dist/additions/index.js.map +1 -1
- package/dist/additions/ledger-store.js +1 -1
- package/dist/additions/record-rules.d.ts.map +1 -1
- package/dist/additions/record-rules.js +7 -0
- package/dist/additions/record-rules.js.map +1 -1
- package/dist/additions/summary.d.ts +88 -1
- package/dist/additions/summary.d.ts.map +1 -1
- package/dist/additions/summary.js +173 -8
- package/dist/additions/summary.js.map +1 -1
- package/dist/additions/version-inventory.d.ts +80 -0
- package/dist/additions/version-inventory.d.ts.map +1 -0
- package/dist/additions/version-inventory.js +232 -0
- package/dist/additions/version-inventory.js.map +1 -0
- package/dist/api/index.d.ts +70 -2
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +506 -79
- package/dist/api/index.js.map +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/request.d.ts +6 -0
- package/dist/auth/request.d.ts.map +1 -1
- package/dist/auth/request.js +66 -4
- package/dist/auth/request.js.map +1 -1
- package/dist/contracts/data.d.ts +54 -8
- package/dist/contracts/data.d.ts.map +1 -1
- package/dist/contracts/data.js +178 -35
- package/dist/contracts/data.js.map +1 -1
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/derivatives/registration.d.ts +0 -5
- package/dist/derivatives/registration.d.ts.map +1 -1
- package/dist/derivatives/registration.js +2 -1
- package/dist/derivatives/registration.js.map +1 -1
- package/dist/errors/pdpp-catalog.d.ts +36 -0
- package/dist/errors/pdpp-catalog.d.ts.map +1 -0
- package/dist/errors/pdpp-catalog.js +86 -0
- package/dist/errors/pdpp-catalog.js.map +1 -0
- package/dist/grants/index.d.ts +2 -0
- package/dist/grants/index.d.ts.map +1 -1
- package/dist/grants/index.js +4 -0
- package/dist/grants/index.js.map +1 -1
- package/dist/grants/pdpp-binding-store.d.ts +89 -0
- package/dist/grants/pdpp-binding-store.d.ts.map +1 -0
- package/dist/grants/pdpp-binding-store.js +112 -0
- package/dist/grants/pdpp-binding-store.js.map +1 -0
- package/dist/grants/pdpp-binding.d.ts +169 -0
- package/dist/grants/pdpp-binding.d.ts.map +1 -0
- package/dist/grants/pdpp-binding.js +236 -0
- package/dist/grants/pdpp-binding.js.map +1 -0
- package/dist/legacy-projection/bindings.d.ts +28 -1
- package/dist/legacy-projection/bindings.d.ts.map +1 -1
- package/dist/legacy-projection/bindings.js +622 -189
- package/dist/legacy-projection/bindings.js.map +1 -1
- package/dist/legacy-projection/claude-source-record.d.ts +22 -0
- package/dist/legacy-projection/claude-source-record.d.ts.map +1 -0
- package/dist/legacy-projection/claude-source-record.js +360 -0
- package/dist/legacy-projection/claude-source-record.js.map +1 -0
- package/dist/legacy-projection/declared-text.d.ts +41 -0
- package/dist/legacy-projection/declared-text.d.ts.map +1 -0
- package/dist/legacy-projection/declared-text.js +199 -0
- package/dist/legacy-projection/declared-text.js.map +1 -0
- package/dist/legacy-projection/github-browser-bindings.d.ts.map +1 -1
- package/dist/legacy-projection/github-browser-bindings.js +89 -5
- package/dist/legacy-projection/github-browser-bindings.js.map +1 -1
- package/dist/legacy-projection/github-legacy-fields.d.ts +6 -0
- package/dist/legacy-projection/github-legacy-fields.d.ts.map +1 -0
- package/dist/legacy-projection/github-legacy-fields.js +202 -0
- package/dist/legacy-projection/github-legacy-fields.js.map +1 -0
- package/dist/legacy-projection/index.d.ts +3 -1
- package/dist/legacy-projection/index.d.ts.map +1 -1
- package/dist/legacy-projection/index.js +49 -1
- package/dist/legacy-projection/index.js.map +1 -1
- package/dist/legacy-projection/oura-browser-bindings.d.ts.map +1 -1
- package/dist/legacy-projection/oura-browser-bindings.js +43 -41
- package/dist/legacy-projection/oura-browser-bindings.js.map +1 -1
- package/dist/legacy-projection/types.d.ts +38 -2
- package/dist/legacy-projection/types.d.ts.map +1 -1
- package/dist/mcp/connection-api.d.ts.map +1 -1
- package/dist/mcp/connection-api.js +4 -0
- package/dist/mcp/connection-api.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/oauth-redirect.d.ts +14 -0
- package/dist/mcp/oauth-redirect.d.ts.map +1 -0
- package/dist/mcp/oauth-redirect.js +51 -0
- package/dist/mcp/oauth-redirect.js.map +1 -0
- package/dist/mcp/resources.d.ts.map +1 -1
- package/dist/mcp/resources.js +3 -0
- package/dist/mcp/resources.js.map +1 -1
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +11 -2
- package/dist/mcp/tools.js.map +1 -1
- package/dist/pdpp/approval.d.ts +271 -0
- package/dist/pdpp/approval.d.ts.map +1 -0
- package/dist/pdpp/approval.js +467 -0
- package/dist/pdpp/approval.js.map +1 -0
- package/dist/pdpp/client-identity.d.ts +88 -0
- package/dist/pdpp/client-identity.d.ts.map +1 -0
- package/dist/pdpp/client-identity.js +196 -0
- package/dist/pdpp/client-identity.js.map +1 -0
- package/dist/pdpp/client-metadata.d.ts +95 -0
- package/dist/pdpp/client-metadata.d.ts.map +1 -0
- package/dist/pdpp/client-metadata.js +96 -0
- package/dist/pdpp/client-metadata.js.map +1 -0
- package/dist/pdpp/commitments.d.ts +75 -0
- package/dist/pdpp/commitments.d.ts.map +1 -0
- package/dist/pdpp/commitments.js +113 -0
- package/dist/pdpp/commitments.js.map +1 -0
- package/dist/pdpp/declaration.d.ts +118 -0
- package/dist/pdpp/declaration.d.ts.map +1 -0
- package/dist/pdpp/declaration.js +836 -0
- package/dist/pdpp/declaration.js.map +1 -0
- package/dist/pdpp/index.d.ts +37 -0
- package/dist/pdpp/index.d.ts.map +1 -0
- package/dist/pdpp/index.js +23 -0
- package/dist/pdpp/index.js.map +1 -0
- package/dist/pdpp/issuance.d.ts +126 -0
- package/dist/pdpp/issuance.d.ts.map +1 -0
- package/dist/pdpp/issuance.js +180 -0
- package/dist/pdpp/issuance.js.map +1 -0
- package/dist/pdpp/pkce.d.ts +53 -0
- package/dist/pdpp/pkce.d.ts.map +1 -0
- package/dist/pdpp/pkce.js +109 -0
- package/dist/pdpp/pkce.js.map +1 -0
- package/dist/pdpp/redirect.d.ts +60 -0
- package/dist/pdpp/redirect.d.ts.map +1 -0
- package/dist/pdpp/redirect.js +125 -0
- package/dist/pdpp/redirect.js.map +1 -0
- package/dist/pdpp/resolve.d.ts +109 -0
- package/dist/pdpp/resolve.d.ts.map +1 -0
- package/dist/pdpp/resolve.js +393 -0
- package/dist/pdpp/resolve.js.map +1 -0
- package/dist/pdpp/review.d.ts +280 -0
- package/dist/pdpp/review.d.ts.map +1 -0
- package/dist/pdpp/review.js +205 -0
- package/dist/pdpp/review.js.map +1 -0
- package/dist/pdpp/selection.d.ts +50 -0
- package/dist/pdpp/selection.d.ts.map +1 -0
- package/dist/pdpp/selection.js +442 -0
- package/dist/pdpp/selection.js.map +1 -0
- package/dist/pdpp/store.d.ts +228 -0
- package/dist/pdpp/store.d.ts.map +1 -0
- package/dist/pdpp/store.js +542 -0
- package/dist/pdpp/store.js.map +1 -0
- package/dist/pdpp/tokens.d.ts +181 -0
- package/dist/pdpp/tokens.d.ts.map +1 -0
- package/dist/pdpp/tokens.js +419 -0
- package/dist/pdpp/tokens.js.map +1 -0
- package/dist/pdpp/types.d.ts +441 -0
- package/dist/pdpp/types.d.ts.map +1 -0
- package/dist/pdpp/types.js +61 -0
- package/dist/pdpp/types.js.map +1 -0
- package/dist/pdpp-version.d.ts +28 -0
- package/dist/pdpp-version.d.ts.map +1 -0
- package/dist/pdpp-version.js +28 -0
- package/dist/pdpp-version.js.map +1 -0
- package/dist/policy/data-read.d.ts +24 -0
- package/dist/policy/data-read.d.ts.map +1 -1
- package/dist/policy/data-read.js +60 -3
- package/dist/policy/data-read.js.map +1 -1
- package/dist/policy/data-write.d.ts.map +1 -1
- package/dist/policy/data-write.js +3 -0
- package/dist/policy/data-write.js.map +1 -1
- package/dist/policy/index.d.ts +1 -0
- package/dist/policy/index.d.ts.map +1 -1
- package/dist/policy/index.js +1 -0
- package/dist/policy/index.js.map +1 -1
- package/dist/policy/reserved-scopes.d.ts +3 -0
- package/dist/policy/reserved-scopes.d.ts.map +1 -0
- package/dist/policy/reserved-scopes.js +5 -0
- package/dist/policy/reserved-scopes.js.map +1 -0
- package/dist/ports/index.d.ts +63 -0
- package/dist/ports/index.d.ts.map +1 -1
- package/dist/ports/index.js.map +1 -1
- package/dist/ports/pdpp-auth.d.ts +129 -0
- package/dist/ports/pdpp-auth.d.ts.map +1 -0
- package/dist/ports/pdpp-auth.js +60 -0
- package/dist/ports/pdpp-auth.js.map +1 -0
- package/dist/ports/pdpp-auth.test-utils.d.ts +9 -0
- package/dist/ports/pdpp-auth.test-utils.d.ts.map +1 -0
- package/dist/ports/pdpp-auth.test-utils.js +18 -0
- package/dist/ports/pdpp-auth.test-utils.js.map +1 -0
- package/dist/schemas/server-config.d.ts +44 -0
- package/dist/schemas/server-config.d.ts.map +1 -1
- package/dist/schemas/server-config.js +91 -0
- package/dist/schemas/server-config.js.map +1 -1
- package/dist/signing/request-signer.d.ts +2 -0
- package/dist/signing/request-signer.d.ts.map +1 -1
- package/dist/signing/request-signer.js +1 -0
- package/dist/signing/request-signer.js.map +1 -1
- package/dist/storage/adapters/interface.d.ts +3 -0
- package/dist/storage/adapters/interface.d.ts.map +1 -1
- package/dist/storage/adapters/vana-audience.d.ts +12 -0
- package/dist/storage/adapters/vana-audience.d.ts.map +1 -0
- package/dist/storage/adapters/vana-audience.js +72 -0
- package/dist/storage/adapters/vana-audience.js.map +1 -0
- package/dist/storage/adapters/vana.d.ts +2 -0
- package/dist/storage/adapters/vana.d.ts.map +1 -1
- package/dist/storage/adapters/vana.js +53 -20
- package/dist/storage/adapters/vana.js.map +1 -1
- package/dist/storage/canonical-legacy-projection.d.ts +262 -0
- package/dist/storage/canonical-legacy-projection.d.ts.map +1 -0
- package/dist/storage/canonical-legacy-projection.js +761 -0
- package/dist/storage/canonical-legacy-projection.js.map +1 -0
- package/dist/storage/index/manager.d.ts +29 -2
- package/dist/storage/index/manager.d.ts.map +1 -1
- package/dist/storage/index/schema.d.ts +1 -1
- package/dist/storage/index/schema.js +1 -1
- package/dist/storage/index/types.d.ts +7 -1
- package/dist/storage/index/types.d.ts.map +1 -1
- package/dist/storage/legacy-projection.d.ts +4 -3
- package/dist/storage/legacy-projection.d.ts.map +1 -1
- package/dist/storage/legacy-projection.js +145 -42
- package/dist/storage/legacy-projection.js.map +1 -1
- package/dist/storage/legacy-scope-coverage-exceptions.d.ts +8 -0
- package/dist/storage/legacy-scope-coverage-exceptions.d.ts.map +1 -0
- package/dist/storage/legacy-scope-coverage-exceptions.js +10 -0
- package/dist/storage/legacy-scope-coverage-exceptions.js.map +1 -0
- package/dist/storage/legacy-scope-coverage.d.ts +53 -0
- package/dist/storage/legacy-scope-coverage.d.ts.map +1 -0
- package/dist/storage/legacy-scope-coverage.js +403 -0
- package/dist/storage/legacy-scope-coverage.js.map +1 -0
- package/dist/storage/legacy-scope-entity-keys.d.ts +4 -0
- package/dist/storage/legacy-scope-entity-keys.d.ts.map +1 -0
- package/dist/storage/legacy-scope-entity-keys.js +92 -0
- package/dist/storage/legacy-scope-entity-keys.js.map +1 -0
- package/dist/storage/legacy-scope-view.d.ts +154 -0
- package/dist/storage/legacy-scope-view.d.ts.map +1 -0
- package/dist/storage/legacy-scope-view.js +735 -0
- package/dist/storage/legacy-scope-view.js.map +1 -0
- package/dist/storage/pdpp-records/completion.d.ts +91 -0
- package/dist/storage/pdpp-records/completion.d.ts.map +1 -0
- package/dist/storage/pdpp-records/completion.js +170 -0
- package/dist/storage/pdpp-records/completion.js.map +1 -0
- package/dist/storage/pdpp-records/cursor.d.ts +41 -0
- package/dist/storage/pdpp-records/cursor.d.ts.map +1 -0
- package/dist/storage/pdpp-records/cursor.js +49 -0
- package/dist/storage/pdpp-records/cursor.js.map +1 -0
- package/dist/storage/pdpp-records/declaration-witness.d.ts +58 -0
- package/dist/storage/pdpp-records/declaration-witness.d.ts.map +1 -0
- package/dist/storage/pdpp-records/declaration-witness.js +230 -0
- package/dist/storage/pdpp-records/declaration-witness.js.map +1 -0
- package/dist/storage/pdpp-records/enforcement.d.ts +52 -0
- package/dist/storage/pdpp-records/enforcement.d.ts.map +1 -0
- package/dist/storage/pdpp-records/enforcement.js +116 -0
- package/dist/storage/pdpp-records/enforcement.js.map +1 -0
- package/dist/storage/pdpp-records/index.d.ts +5 -0
- package/dist/storage/pdpp-records/index.d.ts.map +1 -0
- package/dist/storage/pdpp-records/index.js +5 -0
- package/dist/storage/pdpp-records/index.js.map +1 -0
- package/dist/storage/pdpp-records/ingest-plan.d.ts +39 -0
- package/dist/storage/pdpp-records/ingest-plan.d.ts.map +1 -0
- package/dist/storage/pdpp-records/ingest-plan.js +103 -0
- package/dist/storage/pdpp-records/ingest-plan.js.map +1 -0
- package/dist/storage/pdpp-records/interface.d.ts +104 -0
- package/dist/storage/pdpp-records/interface.d.ts.map +1 -0
- package/dist/storage/pdpp-records/interface.js +2 -0
- package/dist/storage/pdpp-records/interface.js.map +1 -0
- package/dist/storage/pdpp-records/memory-store.d.ts +9 -0
- package/dist/storage/pdpp-records/memory-store.d.ts.map +1 -0
- package/dist/storage/pdpp-records/memory-store.js +466 -0
- package/dist/storage/pdpp-records/memory-store.js.map +1 -0
- package/dist/storage/pdpp-records/portable.d.ts +9 -0
- package/dist/storage/pdpp-records/portable.d.ts.map +1 -0
- package/dist/storage/pdpp-records/portable.js +8 -0
- package/dist/storage/pdpp-records/portable.js.map +1 -0
- package/dist/storage/pdpp-records/read-port.d.ts +44 -0
- package/dist/storage/pdpp-records/read-port.d.ts.map +1 -0
- package/dist/storage/pdpp-records/read-port.js +22 -0
- package/dist/storage/pdpp-records/read-port.js.map +1 -0
- package/dist/storage/pdpp-records/record-key.d.ts +52 -0
- package/dist/storage/pdpp-records/record-key.d.ts.map +1 -0
- package/dist/storage/pdpp-records/record-key.js +97 -0
- package/dist/storage/pdpp-records/record-key.js.map +1 -0
- package/dist/storage/pdpp-records/schema.d.ts +41 -0
- package/dist/storage/pdpp-records/schema.d.ts.map +1 -0
- package/dist/storage/pdpp-records/schema.js +153 -0
- package/dist/storage/pdpp-records/schema.js.map +1 -0
- package/dist/storage/pdpp-records/stream-declaration.d.ts +88 -0
- package/dist/storage/pdpp-records/stream-declaration.d.ts.map +1 -0
- package/dist/storage/pdpp-records/stream-declaration.js +57 -0
- package/dist/storage/pdpp-records/stream-declaration.js.map +1 -0
- package/dist/storage/pdpp-records/types.d.ts +138 -0
- package/dist/storage/pdpp-records/types.d.ts.map +1 -0
- package/dist/storage/pdpp-records/types.js +38 -0
- package/dist/storage/pdpp-records/types.js.map +1 -0
- package/dist/sync/data-point-feed.d.ts +3 -0
- package/dist/sync/data-point-feed.d.ts.map +1 -1
- package/dist/sync/data-point-feed.js +17 -1
- package/dist/sync/data-point-feed.js.map +1 -1
- package/dist/sync/delete-data-port.d.ts +2 -0
- package/dist/sync/delete-data-port.d.ts.map +1 -1
- package/dist/sync/delete-data-port.js +1 -1
- package/dist/sync/delete-data-port.js.map +1 -1
- package/dist/sync/engine/sync-manager.d.ts.map +1 -1
- package/dist/sync/engine/sync-manager.js +5 -2
- package/dist/sync/engine/sync-manager.js.map +1 -1
- package/dist/sync/index.d.ts +3 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +4 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/pdpp-import.d.ts +192 -0
- package/dist/sync/pdpp-import.d.ts.map +1 -0
- package/dist/sync/pdpp-import.js +441 -0
- package/dist/sync/pdpp-import.js.map +1 -0
- package/dist/sync/scope-deletions.d.ts +10 -0
- package/dist/sync/scope-deletions.d.ts.map +1 -1
- package/dist/sync/scope-deletions.js +40 -7
- package/dist/sync/scope-deletions.js.map +1 -1
- package/dist/sync/workers/download.d.ts +14 -0
- package/dist/sync/workers/download.d.ts.map +1 -1
- package/dist/sync/workers/download.js +149 -13
- package/dist/sync/workers/download.js.map +1 -1
- package/dist/sync/workers/upload.d.ts +2 -0
- package/dist/sync/workers/upload.d.ts.map +1 -1
- package/dist/sync/workers/upload.js +7 -0
- package/dist/sync/workers/upload.js.map +1 -1
- package/dist/test-utils/memory-storage.d.ts +1 -1
- package/dist/test-utils/memory-storage.d.ts.map +1 -1
- package/dist/test-utils/memory-storage.js +11 -5
- package/dist/test-utils/memory-storage.js.map +1 -1
- package/package.json +54 -4
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolving a URL-hosted client identity, under a bounded fetch policy.
|
|
3
|
+
*
|
|
4
|
+
* §6 says a conforming AS MUST NOT reject a valid client ID metadata document
|
|
5
|
+
* solely because the client is not preregistered, and MUST accept a valid
|
|
6
|
+
* URL-hosted client identity unless local policy denies authorization. Before
|
|
7
|
+
* this, the AS validated `redirect_uri` only against a config-supplied
|
|
8
|
+
* registration, so an unregistered-but-valid client was refused for the one
|
|
9
|
+
* reason the spec names as insufficient.
|
|
10
|
+
*
|
|
11
|
+
* ── Why this is not just "fetch the client_id" ──────────────────────────────
|
|
12
|
+
*
|
|
13
|
+
* The AS is being handed a URL by an untrusted caller and asked to fetch it.
|
|
14
|
+
* That is a server-side request forgery primitive: without bounds, a client_id
|
|
15
|
+
* of `http://169.254.169.254/...` turns the AS into a proxy for whatever the
|
|
16
|
+
* deployment can reach. So this reuses `checkDeclarationUrl`, the same guard
|
|
17
|
+
* the declaration retrieval path already applies — https-only, no private or
|
|
18
|
+
* loopback literals, and the deployment's host allowlist unless it has
|
|
19
|
+
* explicitly opted into any HTTPS host.
|
|
20
|
+
*
|
|
21
|
+
* The response is size-capped and redirect-bounded for the same reason: an
|
|
22
|
+
* unbounded body is a memory exhaustion vector, and unbounded redirects defeat
|
|
23
|
+
* the host check by hopping somewhere it would have refused.
|
|
24
|
+
*
|
|
25
|
+
* ── Registration still wins ────────────────────────────────────────────────
|
|
26
|
+
*
|
|
27
|
+
* A locally registered client is resolved from config without any fetch at
|
|
28
|
+
* all. This path exists only for clients the deployment has NOT registered,
|
|
29
|
+
* and a deployment that wants registration-only behavior gets it by leaving
|
|
30
|
+
* the fetcher unset — in which case nothing here runs and the previous
|
|
31
|
+
* behavior is preserved exactly.
|
|
32
|
+
*/
|
|
33
|
+
import { checkDeclarationUrl, } from "./declaration.js";
|
|
34
|
+
import { verifyClientIdDocument, } from "./client-metadata.js";
|
|
35
|
+
/** Bodies larger than this are refused rather than buffered. */
|
|
36
|
+
export const MAX_CLIENT_DOCUMENT_BYTES = 64 * 1024;
|
|
37
|
+
/**
|
|
38
|
+
* Resolve a `client_id` URL into a redirect policy the AS can enforce.
|
|
39
|
+
*
|
|
40
|
+
* Returns the same `RegisteredRedirectPolicy` shape a config registration
|
|
41
|
+
* produces, so the caller's redirect validation is unchanged — a URL-hosted
|
|
42
|
+
* client is subject to exactly the same exact-match redirect rule as a
|
|
43
|
+
* registered one, rather than a looser path.
|
|
44
|
+
*/
|
|
45
|
+
export async function resolveUrlHostedClientIdentity(options) {
|
|
46
|
+
const { clientId, fetcher, policy } = options;
|
|
47
|
+
// A non-URL client_id is not a URL-hosted identity. Not an error here —
|
|
48
|
+
// the caller falls back to registration-only behavior.
|
|
49
|
+
let parsed;
|
|
50
|
+
try {
|
|
51
|
+
parsed = new URL(clientId);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
failure: {
|
|
57
|
+
code: "untrusted_client_url",
|
|
58
|
+
message: "client_id is not a URL-hosted identity",
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// The SSRF gate, before any network call.
|
|
63
|
+
const urlProblem = checkDeclarationUrl(parsed.toString(), policy);
|
|
64
|
+
if (urlProblem) {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
failure: {
|
|
68
|
+
code: "untrusted_client_url",
|
|
69
|
+
message: urlProblem.message,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
let retrieved;
|
|
74
|
+
try {
|
|
75
|
+
retrieved = await fetcher(parsed.toString());
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
return {
|
|
79
|
+
ok: false,
|
|
80
|
+
failure: {
|
|
81
|
+
code: "fetch_failed",
|
|
82
|
+
message: err instanceof Error ? err.message : String(err),
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (!retrieved || retrieved.status !== 200) {
|
|
87
|
+
return {
|
|
88
|
+
ok: false,
|
|
89
|
+
failure: {
|
|
90
|
+
code: "fetch_failed",
|
|
91
|
+
message: `client_id document returned ${retrieved?.status ?? "no response"}`,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (retrieved.body.length > MAX_CLIENT_DOCUMENT_BYTES) {
|
|
96
|
+
return {
|
|
97
|
+
ok: false,
|
|
98
|
+
failure: {
|
|
99
|
+
code: "too_large",
|
|
100
|
+
message: `client_id document exceeds ${MAX_CLIENT_DOCUMENT_BYTES} bytes`,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// A redirect may not escape the gate: re-check where we actually landed.
|
|
105
|
+
const finalProblem = checkDeclarationUrl(retrieved.finalUrl, policy);
|
|
106
|
+
if (finalProblem) {
|
|
107
|
+
return {
|
|
108
|
+
ok: false,
|
|
109
|
+
failure: {
|
|
110
|
+
code: "untrusted_client_url",
|
|
111
|
+
message: `client_id document redirected to an untrusted location: ${finalProblem.message}`,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
let parsedJson;
|
|
116
|
+
try {
|
|
117
|
+
parsedJson = JSON.parse(retrieved.body);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return {
|
|
121
|
+
ok: false,
|
|
122
|
+
failure: {
|
|
123
|
+
code: "invalid_document",
|
|
124
|
+
message: "client_id document is not valid JSON",
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
// A JSON document need not be an object — `null`, an array, or a bare
|
|
129
|
+
// primitive all parse without error, and none of them can carry the
|
|
130
|
+
// fields below.
|
|
131
|
+
if (typeof parsedJson !== "object" || parsedJson === null) {
|
|
132
|
+
return {
|
|
133
|
+
ok: false,
|
|
134
|
+
failure: {
|
|
135
|
+
code: "invalid_document",
|
|
136
|
+
message: "client_id document is not a JSON object",
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const document = parsedJson;
|
|
141
|
+
if (!Array.isArray(document.redirect_uris)) {
|
|
142
|
+
return {
|
|
143
|
+
ok: false,
|
|
144
|
+
failure: {
|
|
145
|
+
code: "invalid_document",
|
|
146
|
+
message: "client_id document declares no redirect_uris",
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// A document that declares no usable name gets none, rather than an empty
|
|
151
|
+
// one. `resolveRequesterIdentity` falls back through inline metadata to the
|
|
152
|
+
// client_id itself, so an absent name must be absent -- a `{ name: "" }`
|
|
153
|
+
// would outrank the inline display and render a blank requester.
|
|
154
|
+
const documentName = typeof document.client_name === "string" && document.client_name.length > 0
|
|
155
|
+
? document.client_name
|
|
156
|
+
: undefined;
|
|
157
|
+
// The identity check: the document must assert the URL it came from.
|
|
158
|
+
// Without this, any host could publish a document claiming to be someone
|
|
159
|
+
// else's client_id.
|
|
160
|
+
const candidate = {
|
|
161
|
+
client_id: String(document.client_id ?? ""),
|
|
162
|
+
...(documentName && { display: { name: documentName } }),
|
|
163
|
+
https: true,
|
|
164
|
+
retrieved_from: retrieved.finalUrl,
|
|
165
|
+
};
|
|
166
|
+
const verified = verifyClientIdDocument(candidate);
|
|
167
|
+
if (!verified) {
|
|
168
|
+
return {
|
|
169
|
+
ok: false,
|
|
170
|
+
failure: {
|
|
171
|
+
code: "client_id_mismatch",
|
|
172
|
+
message: "client_id document does not assert the URL it was retrieved from",
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const redirectUris = document.redirect_uris.filter((uri) => typeof uri === "string" && uri.length > 0);
|
|
177
|
+
if (redirectUris.length === 0) {
|
|
178
|
+
return {
|
|
179
|
+
ok: false,
|
|
180
|
+
failure: {
|
|
181
|
+
code: "invalid_document",
|
|
182
|
+
message: "client_id document declares no usable redirect_uris",
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
ok: true,
|
|
188
|
+
policy: { client_id: clientId, redirect_uris: redirectUris },
|
|
189
|
+
verifiedDomain: verified.verifiedDomain,
|
|
190
|
+
// The same document that earned redirect admission, so the caller can
|
|
191
|
+
// resolve the displayed requester identity from it rather than from the
|
|
192
|
+
// client's inline assertion.
|
|
193
|
+
document: candidate,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=client-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-identity.js","sourceRoot":"","sources":["../../src/pdpp/client-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EACL,mBAAmB,GAEpB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,sBAAsB,GAEvB,MAAM,sBAAsB,CAAC;AAG9B,gEAAgE;AAChE,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,GAAG,IAAI,CAAC;AAkDnD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAClD,OAAqC;IAErC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;IAE9C,wEAAwE;IACxE,uDAAuD;IACvD,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,sBAAsB;gBAC5B,OAAO,EAAE,wCAAwC;aAClD;SACF,CAAC;IACJ,CAAC;IAED,0CAA0C;IAC1C,MAAM,UAAU,GAAG,mBAAmB,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC;IAClE,IAAI,UAAU,EAAE,CAAC;QACf,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,sBAAsB;gBAC5B,OAAO,EAAE,UAAU,CAAC,OAAO;aAC5B;SACF,CAAC;IACJ,CAAC;IAED,IAAI,SAAqD,CAAC;IAC1D,IAAI,CAAC;QACH,SAAS,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,cAAc;gBACpB,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aAC1D;SACF,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC3C,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,cAAc;gBACpB,OAAO,EAAE,+BAA+B,SAAS,EAAE,MAAM,IAAI,aAAa,EAAE;aAC7E;SACF,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,CAAC,IAAI,CAAC,MAAM,GAAG,yBAAyB,EAAE,CAAC;QACtD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,WAAW;gBACjB,OAAO,EAAE,8BAA8B,yBAAyB,QAAQ;aACzE;SACF,CAAC;IACJ,CAAC;IAED,yEAAyE;IACzE,MAAM,YAAY,GAAG,mBAAmB,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACrE,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,sBAAsB;gBAC5B,OAAO,EAAE,2DAA2D,YAAY,CAAC,OAAO,EAAE;aAC3F;SACF,CAAC;IACJ,CAAC;IAED,IAAI,UAAmB,CAAC;IACxB,IAAI,CAAC;QACH,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,sCAAsC;aAChD;SACF,CAAC;IACJ,CAAC;IAED,sEAAsE;IACtE,oEAAoE;IACpE,gBAAgB;IAChB,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QAC1D,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,yCAAyC;aACnD;SACF,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,UAIhB,CAAC;IAEF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QAC3C,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,8CAA8C;aACxD;SACF,CAAC;IACJ,CAAC;IAED,0EAA0E;IAC1E,4EAA4E;IAC5E,yEAAyE;IACzE,iEAAiE;IACjE,MAAM,YAAY,GAChB,OAAO,QAAQ,CAAC,WAAW,KAAK,QAAQ,IAAI,QAAQ,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC;QACzE,CAAC,CAAC,QAAQ,CAAC,WAAW;QACtB,CAAC,CAAC,SAAS,CAAC;IAEhB,qEAAqE;IACrE,yEAAyE;IACzE,oBAAoB;IACpB,MAAM,SAAS,GAA6B;QAC1C,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,SAAS,IAAI,EAAE,CAAC;QAC3C,GAAG,CAAC,YAAY,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,CAAC;QACxD,KAAK,EAAE,IAAI;QACX,cAAc,EAAE,SAAS,CAAC,QAAQ;KACnC,CAAC;IACF,MAAM,QAAQ,GAAG,sBAAsB,CAAC,SAAS,CAAC,CAAC;IACnD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,oBAAoB;gBAC1B,OAAO,EACL,kEAAkE;aACrE;SACF,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAChD,CAAC,GAAG,EAAiB,EAAE,CAAC,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAClE,CAAC;IACF,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO;YACL,EAAE,EAAE,KAAK;YACT,OAAO,EAAE;gBACP,IAAI,EAAE,kBAAkB;gBACxB,OAAO,EAAE,qDAAqD;aAC/D;SACF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE;QAC5D,cAAc,EAAE,QAAQ,CAAC,cAAc;QACvC,sEAAsE;QACtE,wEAAwE;QACxE,6BAA6B;QAC7B,QAAQ,EAAE,SAAS;KACpB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDPP Core §6 requester-identity resolution.
|
|
3
|
+
*
|
|
4
|
+
* Two things the spec insists on keeping apart, and which are easy to conflate:
|
|
5
|
+
*
|
|
6
|
+
* - **Verified domain control** means the AS retrieved this client's metadata
|
|
7
|
+
* from the client_id URL over HTTPS and the document identified the same
|
|
8
|
+
* client. It says the operator of that domain published this metadata. It
|
|
9
|
+
* says nothing about the client's conduct or data practices.
|
|
10
|
+
* - **App approval** means a local registration or trust-registry admission
|
|
11
|
+
* decision — someone actually vetted the client.
|
|
12
|
+
*
|
|
13
|
+
* §6 obligation 5 requires the first to be rendered as a *named domain*
|
|
14
|
+
* ("Verified domain: example.com"), never as an unqualified "Verified app".
|
|
15
|
+
* `RequesterIdentity` therefore carries them as two separate fields, so a
|
|
16
|
+
* consent surface cannot render one as the other by accident.
|
|
17
|
+
*/
|
|
18
|
+
import type { RequesterIdentity } from "./review.js";
|
|
19
|
+
import type { ClientDisplay } from "./types.js";
|
|
20
|
+
/** Metadata the deployment holds locally, or a trust registry supplied. */
|
|
21
|
+
export interface RegisteredClientMetadata {
|
|
22
|
+
client_id: string;
|
|
23
|
+
display: ClientDisplay;
|
|
24
|
+
/** True only when an admission decision was actually made. */
|
|
25
|
+
approved: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A client ID metadata document retrieved from the client_id URL.
|
|
29
|
+
*
|
|
30
|
+
* §6: the document is *valid* only when it was retrieved over HTTPS from the
|
|
31
|
+
* client_id URL itself and the `client_id` inside it is identical to that URL.
|
|
32
|
+
* That last check is what stops a document claiming to be a different client.
|
|
33
|
+
*/
|
|
34
|
+
export interface ClientIdMetadataDocument {
|
|
35
|
+
/** The URL the document was retrieved from. */
|
|
36
|
+
retrieved_from: string;
|
|
37
|
+
/** The client_id the document asserts. */
|
|
38
|
+
client_id: string;
|
|
39
|
+
/**
|
|
40
|
+
* Optional: a document may validly declare redirect_uris and no name. An
|
|
41
|
+
* absent name must stay absent rather than become `{ name: "" }`, which
|
|
42
|
+
* would outrank inline metadata in §6 precedence and render a blank
|
|
43
|
+
* requester on the consent surface.
|
|
44
|
+
*/
|
|
45
|
+
display?: ClientDisplay;
|
|
46
|
+
/** Whether retrieval used HTTPS. */
|
|
47
|
+
https: boolean;
|
|
48
|
+
}
|
|
49
|
+
export interface ResolveRequesterInput {
|
|
50
|
+
client_id: string;
|
|
51
|
+
/** Highest precedence: local registration or trust registry. */
|
|
52
|
+
registered?: RegisteredClientMetadata;
|
|
53
|
+
/** Validated binding metadata, when the binding supplied one. */
|
|
54
|
+
document?: ClientIdMetadataDocument;
|
|
55
|
+
/** Lowest precedence: the client's own inline assertion. */
|
|
56
|
+
inline?: ClientDisplay;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Validate a client ID metadata document per §6.
|
|
60
|
+
*
|
|
61
|
+
* Returns the verified domain when all three conditions hold, otherwise null.
|
|
62
|
+
* A null result is not a rejection of the client — §6's interoperability
|
|
63
|
+
* obligation forbids rejecting a valid document merely for being
|
|
64
|
+
* unpreregistered, and a document that fails validation just means we fall
|
|
65
|
+
* through to a lower-precedence metadata source.
|
|
66
|
+
*/
|
|
67
|
+
export declare function verifyClientIdDocument(doc: ClientIdMetadataDocument): {
|
|
68
|
+
verifiedDomain: string;
|
|
69
|
+
} | null;
|
|
70
|
+
/**
|
|
71
|
+
* Resolve requester identity under §6's precedence:
|
|
72
|
+
*
|
|
73
|
+
* local registration / trust registry
|
|
74
|
+
* > validated software statement (not supported in this build)
|
|
75
|
+
* > validated binding metadata (the client ID metadata document)
|
|
76
|
+
* > inline `client_display`
|
|
77
|
+
* > `client_id` fallback
|
|
78
|
+
*
|
|
79
|
+
* The display name always resolves to something: §6 obligation 2 requires the
|
|
80
|
+
* AS to display `client_id` when no name is available, so the consent surface
|
|
81
|
+
* is never left with nothing to render.
|
|
82
|
+
*/
|
|
83
|
+
export declare function resolveRequesterIdentity(input: ResolveRequesterInput): RequesterIdentity;
|
|
84
|
+
/**
|
|
85
|
+
* Whether a client-supplied remote logo may be rendered directly.
|
|
86
|
+
*
|
|
87
|
+
* §6 obligation 6: `logo_uri` is untrusted content. An unverified client's
|
|
88
|
+
* remote logo must not be fetched into the consent UI — the surface generates
|
|
89
|
+
* a monogram from the display name instead. Proxied-and-approved assets are a
|
|
90
|
+
* deployment concern the caller signals explicitly.
|
|
91
|
+
*/
|
|
92
|
+
export declare function mayRenderRemoteLogo(identity: RequesterIdentity, options?: {
|
|
93
|
+
assetProxiedAndApproved?: boolean;
|
|
94
|
+
}): boolean;
|
|
95
|
+
//# sourceMappingURL=client-metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-metadata.d.ts","sourceRoot":"","sources":["../../src/pdpp/client-metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,2EAA2E;AAC3E,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,aAAa,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,wBAAwB;IACvC,+CAA+C;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,oCAAoC;IACpC,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC,iEAAiE;IACjE,QAAQ,CAAC,EAAE,wBAAwB,CAAC;IACpC,4DAA4D;IAC5D,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,wBAAwB,GAC5B;IAAE,cAAc,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAsBnC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,qBAAqB,GAC3B,iBAAiB,CAoBnB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,GAAE;IAAE,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAAO,GAClD,OAAO,CAGT"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDPP Core §6 requester-identity resolution.
|
|
3
|
+
*
|
|
4
|
+
* Two things the spec insists on keeping apart, and which are easy to conflate:
|
|
5
|
+
*
|
|
6
|
+
* - **Verified domain control** means the AS retrieved this client's metadata
|
|
7
|
+
* from the client_id URL over HTTPS and the document identified the same
|
|
8
|
+
* client. It says the operator of that domain published this metadata. It
|
|
9
|
+
* says nothing about the client's conduct or data practices.
|
|
10
|
+
* - **App approval** means a local registration or trust-registry admission
|
|
11
|
+
* decision — someone actually vetted the client.
|
|
12
|
+
*
|
|
13
|
+
* §6 obligation 5 requires the first to be rendered as a *named domain*
|
|
14
|
+
* ("Verified domain: example.com"), never as an unqualified "Verified app".
|
|
15
|
+
* `RequesterIdentity` therefore carries them as two separate fields, so a
|
|
16
|
+
* consent surface cannot render one as the other by accident.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Validate a client ID metadata document per §6.
|
|
20
|
+
*
|
|
21
|
+
* Returns the verified domain when all three conditions hold, otherwise null.
|
|
22
|
+
* A null result is not a rejection of the client — §6's interoperability
|
|
23
|
+
* obligation forbids rejecting a valid document merely for being
|
|
24
|
+
* unpreregistered, and a document that fails validation just means we fall
|
|
25
|
+
* through to a lower-precedence metadata source.
|
|
26
|
+
*/
|
|
27
|
+
export function verifyClientIdDocument(doc) {
|
|
28
|
+
if (!doc.https)
|
|
29
|
+
return null;
|
|
30
|
+
let retrievedUrl;
|
|
31
|
+
try {
|
|
32
|
+
retrievedUrl = new URL(doc.retrieved_from);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (retrievedUrl.protocol !== "https:")
|
|
38
|
+
return null;
|
|
39
|
+
// The identity check: the document's client_id must be the URL it came from.
|
|
40
|
+
// Compared as URLs so a trailing-slash difference is not a false negative.
|
|
41
|
+
let assertedUrl;
|
|
42
|
+
try {
|
|
43
|
+
assertedUrl = new URL(doc.client_id);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
if (assertedUrl.toString() !== retrievedUrl.toString())
|
|
49
|
+
return null;
|
|
50
|
+
return { verifiedDomain: retrievedUrl.hostname };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Resolve requester identity under §6's precedence:
|
|
54
|
+
*
|
|
55
|
+
* local registration / trust registry
|
|
56
|
+
* > validated software statement (not supported in this build)
|
|
57
|
+
* > validated binding metadata (the client ID metadata document)
|
|
58
|
+
* > inline `client_display`
|
|
59
|
+
* > `client_id` fallback
|
|
60
|
+
*
|
|
61
|
+
* The display name always resolves to something: §6 obligation 2 requires the
|
|
62
|
+
* AS to display `client_id` when no name is available, so the consent surface
|
|
63
|
+
* is never left with nothing to render.
|
|
64
|
+
*/
|
|
65
|
+
export function resolveRequesterIdentity(input) {
|
|
66
|
+
const verification = input.document
|
|
67
|
+
? verifyClientIdDocument(input.document)
|
|
68
|
+
: null;
|
|
69
|
+
// Precedence order, highest first.
|
|
70
|
+
const display = input.registered?.display ??
|
|
71
|
+
(verification ? input.document?.display : undefined) ??
|
|
72
|
+
input.inline;
|
|
73
|
+
return {
|
|
74
|
+
client_id: input.client_id,
|
|
75
|
+
display_name: display?.name ?? input.client_id,
|
|
76
|
+
...(display && { display }),
|
|
77
|
+
// Named, not asserted as blanket verification.
|
|
78
|
+
...(verification && { verified_domain: verification.verifiedDomain }),
|
|
79
|
+
// Only a real admission decision counts. Domain control is not approval.
|
|
80
|
+
app_approved: input.registered?.approved ?? false,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Whether a client-supplied remote logo may be rendered directly.
|
|
85
|
+
*
|
|
86
|
+
* §6 obligation 6: `logo_uri` is untrusted content. An unverified client's
|
|
87
|
+
* remote logo must not be fetched into the consent UI — the surface generates
|
|
88
|
+
* a monogram from the display name instead. Proxied-and-approved assets are a
|
|
89
|
+
* deployment concern the caller signals explicitly.
|
|
90
|
+
*/
|
|
91
|
+
export function mayRenderRemoteLogo(identity, options = {}) {
|
|
92
|
+
if (options.assetProxiedAndApproved === true)
|
|
93
|
+
return true;
|
|
94
|
+
return identity.app_approved || identity.verified_domain !== undefined;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=client-metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-metadata.js","sourceRoot":"","sources":["../../src/pdpp/client-metadata.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA8CH;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CACpC,GAA6B;IAE7B,IAAI,CAAC,GAAG,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAE5B,IAAI,YAAiB,CAAC;IACtB,IAAI,CAAC;QACH,YAAY,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,YAAY,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAEpD,6EAA6E;IAC7E,2EAA2E;IAC3E,IAAI,WAAgB,CAAC;IACrB,IAAI,CAAC;QACH,WAAW,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,WAAW,CAAC,QAAQ,EAAE,KAAK,YAAY,CAAC,QAAQ,EAAE;QAAE,OAAO,IAAI,CAAC;IAEpE,OAAO,EAAE,cAAc,EAAE,YAAY,CAAC,QAAQ,EAAE,CAAC;AACnD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,wBAAwB,CACtC,KAA4B;IAE5B,MAAM,YAAY,GAAG,KAAK,CAAC,QAAQ;QACjC,CAAC,CAAC,sBAAsB,CAAC,KAAK,CAAC,QAAQ,CAAC;QACxC,CAAC,CAAC,IAAI,CAAC;IAET,mCAAmC;IACnC,MAAM,OAAO,GACX,KAAK,CAAC,UAAU,EAAE,OAAO;QACzB,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QACpD,KAAK,CAAC,MAAM,CAAC;IAEf,OAAO;QACL,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,YAAY,EAAE,OAAO,EAAE,IAAI,IAAI,KAAK,CAAC,SAAS;QAC9C,GAAG,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,CAAC;QAC3B,+CAA+C;QAC/C,GAAG,CAAC,YAAY,IAAI,EAAE,eAAe,EAAE,YAAY,CAAC,cAAc,EAAE,CAAC;QACrE,yEAAyE;QACzE,YAAY,EAAE,KAAK,CAAC,UAAU,EAAE,QAAQ,IAAI,KAAK;KAClD,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,QAA2B,EAC3B,UAAiD,EAAE;IAEnD,IAAI,OAAO,CAAC,uBAAuB,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC1D,OAAO,QAAQ,CAAC,YAAY,IAAI,QAAQ,CAAC,eAAe,KAAK,SAAS,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDPP v0.2 recipient commitments: resolving purpose and retention before the
|
|
3
|
+
* owner approves.
|
|
4
|
+
*
|
|
5
|
+
* The section this implements exists because of one asymmetry. An owner
|
|
6
|
+
* narrowing what they share is acting entirely within their own authority —
|
|
7
|
+
* nobody else has to agree to receive less. An owner *changing the terms of
|
|
8
|
+
* use* is proposing an obligation the recipient has to carry, and no amount of
|
|
9
|
+
* owner authority makes the recipient agree to it. v0.2 keeps those two apart
|
|
10
|
+
* deliberately: "The AS MUST NOT interpret narrower access as recipient
|
|
11
|
+
* acceptance of a new purpose or retention obligation."
|
|
12
|
+
*
|
|
13
|
+
* So commitments resolve from exactly two places — the client's own request,
|
|
14
|
+
* or standing terms the recipient authorized — and an owner condition covered
|
|
15
|
+
* by neither refuses issuance rather than being carried as though agreed.
|
|
16
|
+
*
|
|
17
|
+
* The important non-obvious case: a condition *favourable* to the owner is
|
|
18
|
+
* still a condition. An owner asking for a 7-day retention when the client
|
|
19
|
+
* requested 30 is asking for less, but it is still a term the recipient never
|
|
20
|
+
* accepted, and recording it as a grant constraint would claim an acceptance
|
|
21
|
+
* that does not exist. PDPP carries commitments; it cannot enforce the
|
|
22
|
+
* recipient's behavior after disclosure, which is exactly why the record of
|
|
23
|
+
* what they actually agreed to has to be honest.
|
|
24
|
+
*/
|
|
25
|
+
import type { OwnerConditions, RecipientTerms, Retention, SelectionRequest } from "./types.js";
|
|
26
|
+
/**
|
|
27
|
+
* Sentinel terms identity for commitments that came from the selection request
|
|
28
|
+
* itself.
|
|
29
|
+
*
|
|
30
|
+
* The request *is* recipient authority — the client wrote it — so this is a
|
|
31
|
+
* real answer rather than a missing one. Recording it explicitly, rather than
|
|
32
|
+
* leaving the evidence absent, is what makes "no terms were consulted"
|
|
33
|
+
* distinguishable from "the evidence was never written".
|
|
34
|
+
*/
|
|
35
|
+
export declare const REQUEST_TERMS_ID = "urn:pdpp:recipient-terms:selection-request";
|
|
36
|
+
/** The commitments that will be shown to the owner and written into the grant. */
|
|
37
|
+
export interface ResolvedCommitments {
|
|
38
|
+
purpose_code: string;
|
|
39
|
+
purpose_description?: string;
|
|
40
|
+
retention?: Retention;
|
|
41
|
+
/** Identity and version of the authority these came from. */
|
|
42
|
+
evidence: {
|
|
43
|
+
id: string;
|
|
44
|
+
version: string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export interface CommitmentsFailure {
|
|
48
|
+
code: "recipient_terms_unsupported";
|
|
49
|
+
message: string;
|
|
50
|
+
}
|
|
51
|
+
export type CommitmentsResult = {
|
|
52
|
+
ok: true;
|
|
53
|
+
commitments: ResolvedCommitments;
|
|
54
|
+
} | {
|
|
55
|
+
ok: false;
|
|
56
|
+
failure: CommitmentsFailure;
|
|
57
|
+
};
|
|
58
|
+
export interface ResolveCommitmentsInput {
|
|
59
|
+
request: SelectionRequest;
|
|
60
|
+
/** Conditions the owner attached to their approval. */
|
|
61
|
+
ownerConditions?: OwnerConditions;
|
|
62
|
+
/** Standing terms the recipient authorized, when the deployment tracks them. */
|
|
63
|
+
standingTerms?: RecipientTerms;
|
|
64
|
+
/** The declaration revision, used as the request's own evidence version. */
|
|
65
|
+
declarationVersion: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Resolve purpose and retention from the request or applicable standing terms.
|
|
69
|
+
*
|
|
70
|
+
* Runs before the owner approves, never after: v0.2 requires the owner to see
|
|
71
|
+
* the resolved commitments in the final review, so a commitment resolved at
|
|
72
|
+
* issuance time would be one the owner never saw.
|
|
73
|
+
*/
|
|
74
|
+
export declare function resolveCommitments(input: ResolveCommitmentsInput): CommitmentsResult;
|
|
75
|
+
//# sourceMappingURL=commitments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commitments.d.ts","sourceRoot":"","sources":["../../src/pdpp/commitments.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,SAAS,EACT,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,+CAA+C,CAAC;AAE7E,kFAAkF;AAClF,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,6DAA6D;IAC7D,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,6BAA6B,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,mBAAmB,CAAA;CAAE,GAC9C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,CAAC;AAM/C,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,gBAAgB,CAAC;IAC1B,uDAAuD;IACvD,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gFAAgF;IAChF,aAAa,CAAC,EAAE,cAAc,CAAC;IAC/B,4EAA4E;IAC5E,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,uBAAuB,GAC7B,iBAAiB,CAyEnB"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDPP v0.2 recipient commitments: resolving purpose and retention before the
|
|
3
|
+
* owner approves.
|
|
4
|
+
*
|
|
5
|
+
* The section this implements exists because of one asymmetry. An owner
|
|
6
|
+
* narrowing what they share is acting entirely within their own authority —
|
|
7
|
+
* nobody else has to agree to receive less. An owner *changing the terms of
|
|
8
|
+
* use* is proposing an obligation the recipient has to carry, and no amount of
|
|
9
|
+
* owner authority makes the recipient agree to it. v0.2 keeps those two apart
|
|
10
|
+
* deliberately: "The AS MUST NOT interpret narrower access as recipient
|
|
11
|
+
* acceptance of a new purpose or retention obligation."
|
|
12
|
+
*
|
|
13
|
+
* So commitments resolve from exactly two places — the client's own request,
|
|
14
|
+
* or standing terms the recipient authorized — and an owner condition covered
|
|
15
|
+
* by neither refuses issuance rather than being carried as though agreed.
|
|
16
|
+
*
|
|
17
|
+
* The important non-obvious case: a condition *favourable* to the owner is
|
|
18
|
+
* still a condition. An owner asking for a 7-day retention when the client
|
|
19
|
+
* requested 30 is asking for less, but it is still a term the recipient never
|
|
20
|
+
* accepted, and recording it as a grant constraint would claim an acceptance
|
|
21
|
+
* that does not exist. PDPP carries commitments; it cannot enforce the
|
|
22
|
+
* recipient's behavior after disclosure, which is exactly why the record of
|
|
23
|
+
* what they actually agreed to has to be honest.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Sentinel terms identity for commitments that came from the selection request
|
|
27
|
+
* itself.
|
|
28
|
+
*
|
|
29
|
+
* The request *is* recipient authority — the client wrote it — so this is a
|
|
30
|
+
* real answer rather than a missing one. Recording it explicitly, rather than
|
|
31
|
+
* leaving the evidence absent, is what makes "no terms were consulted"
|
|
32
|
+
* distinguishable from "the evidence was never written".
|
|
33
|
+
*/
|
|
34
|
+
export const REQUEST_TERMS_ID = "urn:pdpp:recipient-terms:selection-request";
|
|
35
|
+
function sameRetention(a, b) {
|
|
36
|
+
return a.max_duration === b.max_duration && a.on_expiry === b.on_expiry;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolve purpose and retention from the request or applicable standing terms.
|
|
40
|
+
*
|
|
41
|
+
* Runs before the owner approves, never after: v0.2 requires the owner to see
|
|
42
|
+
* the resolved commitments in the final review, so a commitment resolved at
|
|
43
|
+
* issuance time would be one the owner never saw.
|
|
44
|
+
*/
|
|
45
|
+
export function resolveCommitments(input) {
|
|
46
|
+
const { request, ownerConditions, standingTerms } = input;
|
|
47
|
+
// Absent any owner condition, the request's own terms stand and the client
|
|
48
|
+
// is self-evidently the authority for them.
|
|
49
|
+
let evidence = {
|
|
50
|
+
id: REQUEST_TERMS_ID,
|
|
51
|
+
version: input.declarationVersion,
|
|
52
|
+
};
|
|
53
|
+
let purposeCode = request.purpose_code;
|
|
54
|
+
let retention = request.retention;
|
|
55
|
+
// Error text is deliberately generic. v0.2: "Error descriptions MUST NOT
|
|
56
|
+
// reveal unapproved source, instance, record, or field existence." A
|
|
57
|
+
// rejection is a fine place to leak an inventory if the message names what
|
|
58
|
+
// the request touched, so this one names only the term class.
|
|
59
|
+
const refuse = (what) => ({
|
|
60
|
+
ok: false,
|
|
61
|
+
failure: {
|
|
62
|
+
code: "recipient_terms_unsupported",
|
|
63
|
+
message: `the requested ${what} is not covered by the recipient's applicable authority`,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
if (ownerConditions?.purpose_code !== undefined) {
|
|
67
|
+
const proposed = ownerConditions.purpose_code;
|
|
68
|
+
const covered = proposed === request.purpose_code ||
|
|
69
|
+
standingTerms?.accepted_purpose_codes?.includes(proposed) === true;
|
|
70
|
+
if (!covered)
|
|
71
|
+
return refuse("purpose");
|
|
72
|
+
purposeCode = proposed;
|
|
73
|
+
// A purpose description authored for the client's own purpose code does
|
|
74
|
+
// not describe a different one. Dropping it is the honest outcome; the
|
|
75
|
+
// surface falls back to the code or its registry entry.
|
|
76
|
+
if (proposed !== request.purpose_code) {
|
|
77
|
+
evidence = termsEvidence(standingTerms, evidence);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (ownerConditions?.retention !== undefined) {
|
|
81
|
+
const proposed = ownerConditions.retention;
|
|
82
|
+
const covered = (request.retention !== undefined &&
|
|
83
|
+
sameRetention(proposed, request.retention)) ||
|
|
84
|
+
standingTerms?.accepted_retention?.some((accepted) => sameRetention(proposed, accepted)) === true;
|
|
85
|
+
if (!covered)
|
|
86
|
+
return refuse("retention term");
|
|
87
|
+
retention = proposed;
|
|
88
|
+
if (request.retention === undefined ||
|
|
89
|
+
!sameRetention(proposed, request.retention)) {
|
|
90
|
+
evidence = termsEvidence(standingTerms, evidence);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const purposeDescription = purposeCode === request.purpose_code
|
|
94
|
+
? request.purpose_description
|
|
95
|
+
: undefined;
|
|
96
|
+
return {
|
|
97
|
+
ok: true,
|
|
98
|
+
commitments: {
|
|
99
|
+
purpose_code: purposeCode,
|
|
100
|
+
...(purposeDescription !== undefined && {
|
|
101
|
+
purpose_description: purposeDescription,
|
|
102
|
+
}),
|
|
103
|
+
...(retention !== undefined && { retention }),
|
|
104
|
+
evidence,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function termsEvidence(standingTerms, fallback) {
|
|
109
|
+
if (!standingTerms)
|
|
110
|
+
return fallback;
|
|
111
|
+
return { id: standingTerms.id, version: standingTerms.version };
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=commitments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commitments.js","sourceRoot":"","sources":["../../src/pdpp/commitments.ts"],"names":[],"mappings":"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"}
|