@opendatalabs/personal-server-ts-core 1.33.8 → 1.34.1
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 +5 -1
- package/dist/legacy-projection/index.d.ts.map +1 -1
- package/dist/legacy-projection/index.js +55 -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/read-client.d.ts.map +1 -1
- package/dist/mcp/read-client.js +10 -1
- package/dist/mcp/read-client.js.map +1 -1
- 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 +13 -10
- package/dist/storage/legacy-projection.d.ts.map +1 -1
- package/dist/storage/legacy-projection.js +269 -74
- 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,542 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persisted PDPP authorization state: declarations, grants, authorization
|
|
3
|
+
* codes, and the token/refresh-family graph.
|
|
4
|
+
*
|
|
5
|
+
* SQLite via better-sqlite3, matching `../storage/index/schema.ts`. The choice
|
|
6
|
+
* is not incidental: three requirements in §7/§9 are atomicity requirements,
|
|
7
|
+
* and a JSON file (the pattern `server/src/token-store.ts` uses for session
|
|
8
|
+
* tokens) cannot express them without a lock of its own.
|
|
9
|
+
*
|
|
10
|
+
* - §9 AS item 10: a `single_use` grant is consumed *atomically* with first
|
|
11
|
+
* client-token issuance.
|
|
12
|
+
* - §9 AS item 19: an authorization code is consumed *atomically* on first
|
|
13
|
+
* successful redemption; every later redemption fails.
|
|
14
|
+
* - §9 AS item 20: refresh reuse revokes the family and all linked tokens.
|
|
15
|
+
*
|
|
16
|
+
* Each is implemented as a conditional UPDATE inside a transaction, where the
|
|
17
|
+
* WHERE clause carries the precondition. `changes === 0` then means "someone
|
|
18
|
+
* else won the race", which is the only correct answer — checking state and
|
|
19
|
+
* then writing it would be exactly the TOCTOU bug these items exist to close.
|
|
20
|
+
*
|
|
21
|
+
* Tokens are stored as SHA-256 hashes, never plaintext, so a store dump does
|
|
22
|
+
* not yield usable credentials (§10 "Token security").
|
|
23
|
+
*/
|
|
24
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
25
|
+
import Database from "better-sqlite3";
|
|
26
|
+
/**
|
|
27
|
+
* Persisted-state contract version. §7 "Version layering" and §9 AS item 21
|
|
28
|
+
* require the reader to reject state it cannot validate against a supported
|
|
29
|
+
* contract, and forbid reconstructing missing facts from current config.
|
|
30
|
+
*/
|
|
31
|
+
export const PDPP_AUTH_STATE_VERSION = 1;
|
|
32
|
+
export class UnsupportedAuthStateError extends Error {
|
|
33
|
+
foundVersion;
|
|
34
|
+
supportedVersion;
|
|
35
|
+
constructor(foundVersion, supportedVersion = PDPP_AUTH_STATE_VERSION) {
|
|
36
|
+
super(`Persisted PDPP authorization state is version ${foundVersion}, which this build cannot validate ` +
|
|
37
|
+
`(supports ${supportedVersion}). Migrate from retained facts or require fresh consent; ` +
|
|
38
|
+
`authorization facts must not be reconstructed from current configuration.`);
|
|
39
|
+
this.foundVersion = foundVersion;
|
|
40
|
+
this.supportedVersion = supportedVersion;
|
|
41
|
+
this.name = "UnsupportedAuthStateError";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const SCHEMA_SQL = `
|
|
45
|
+
CREATE TABLE IF NOT EXISTS pdpp_state_meta (
|
|
46
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
47
|
+
version INTEGER NOT NULL
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
-- The exact retained declaration snapshot. Keyed by (source_id, version) so a
|
|
51
|
+
-- later revision is a new row and never overwrites the snapshot an issued
|
|
52
|
+
-- grant was resolved against (§9 AS item 16).
|
|
53
|
+
CREATE TABLE IF NOT EXISTS pdpp_declarations (
|
|
54
|
+
source_id TEXT NOT NULL,
|
|
55
|
+
version TEXT NOT NULL,
|
|
56
|
+
digest TEXT NOT NULL,
|
|
57
|
+
snapshot_json TEXT NOT NULL,
|
|
58
|
+
-- The exact retrieved bytes. snapshot_json is the parsed snapshot, so
|
|
59
|
+
-- re-digesting it does not reproduce digest. Without the original document
|
|
60
|
+
-- the retained digest is unverifiable from the store alone, which is the
|
|
61
|
+
-- gap this column closes. Nullable because rows retained before this
|
|
62
|
+
-- column existed have no document to recover; readers must treat NULL as
|
|
63
|
+
-- cannot-verify, never as verified.
|
|
64
|
+
document TEXT,
|
|
65
|
+
retrieved_at TEXT NOT NULL,
|
|
66
|
+
PRIMARY KEY (source_id, version)
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
CREATE TABLE IF NOT EXISTS pdpp_grants (
|
|
70
|
+
grant_id TEXT PRIMARY KEY,
|
|
71
|
+
subject_id TEXT NOT NULL,
|
|
72
|
+
client_id TEXT NOT NULL,
|
|
73
|
+
source_id TEXT NOT NULL,
|
|
74
|
+
declaration_version TEXT NOT NULL,
|
|
75
|
+
access_mode TEXT NOT NULL CHECK (access_mode IN ('single_use','continuous')),
|
|
76
|
+
grant_json TEXT NOT NULL,
|
|
77
|
+
issued_at TEXT NOT NULL,
|
|
78
|
+
expires_at TEXT,
|
|
79
|
+
revoked_at TEXT,
|
|
80
|
+
-- Set atomically with first client-token issuance for single_use grants.
|
|
81
|
+
consumed_at TEXT,
|
|
82
|
+
review_digest TEXT NOT NULL,
|
|
83
|
+
-- Consent evidence: the normalized bound claims, kept out of grant_json so
|
|
84
|
+
-- they cannot leak into introspection or RS enforcement (§6 client claims).
|
|
85
|
+
consent_evidence_json TEXT
|
|
86
|
+
);
|
|
87
|
+
CREATE INDEX IF NOT EXISTS idx_pdpp_grants_subject ON pdpp_grants (subject_id);
|
|
88
|
+
|
|
89
|
+
CREATE TABLE IF NOT EXISTS pdpp_auth_codes (
|
|
90
|
+
code_hash TEXT PRIMARY KEY,
|
|
91
|
+
grant_id TEXT NOT NULL,
|
|
92
|
+
client_id TEXT NOT NULL,
|
|
93
|
+
redirect_uri TEXT NOT NULL,
|
|
94
|
+
code_challenge TEXT,
|
|
95
|
+
code_challenge_method TEXT,
|
|
96
|
+
expires_at TEXT NOT NULL,
|
|
97
|
+
redeemed_at TEXT,
|
|
98
|
+
FOREIGN KEY (grant_id) REFERENCES pdpp_grants (grant_id)
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
-- One row per refresh-token family. Rotation supersedes a token within the
|
|
102
|
+
-- family; reuse of a superseded token kills the whole family (§9 AS item 20).
|
|
103
|
+
CREATE TABLE IF NOT EXISTS pdpp_refresh_families (
|
|
104
|
+
family_id TEXT PRIMARY KEY,
|
|
105
|
+
grant_id TEXT NOT NULL,
|
|
106
|
+
revoked_at TEXT,
|
|
107
|
+
FOREIGN KEY (grant_id) REFERENCES pdpp_grants (grant_id)
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
CREATE TABLE IF NOT EXISTS pdpp_refresh_tokens (
|
|
111
|
+
token_hash TEXT PRIMARY KEY,
|
|
112
|
+
family_id TEXT NOT NULL,
|
|
113
|
+
issued_at TEXT NOT NULL,
|
|
114
|
+
-- Non-null once rotated out. Presenting a superseded token is reuse.
|
|
115
|
+
superseded_at TEXT,
|
|
116
|
+
FOREIGN KEY (family_id) REFERENCES pdpp_refresh_families (family_id)
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
CREATE TABLE IF NOT EXISTS pdpp_access_tokens (
|
|
120
|
+
token_hash TEXT PRIMARY KEY,
|
|
121
|
+
grant_id TEXT,
|
|
122
|
+
subject_id TEXT NOT NULL,
|
|
123
|
+
client_id TEXT,
|
|
124
|
+
token_kind TEXT NOT NULL CHECK (token_kind IN ('owner','client')),
|
|
125
|
+
owner_source_id TEXT,
|
|
126
|
+
owner_instance_ids_json TEXT,
|
|
127
|
+
family_id TEXT,
|
|
128
|
+
issued_at TEXT NOT NULL,
|
|
129
|
+
expires_at TEXT,
|
|
130
|
+
revoked_at TEXT
|
|
131
|
+
);
|
|
132
|
+
CREATE INDEX IF NOT EXISTS idx_pdpp_access_grant ON pdpp_access_tokens (grant_id);
|
|
133
|
+
CREATE INDEX IF NOT EXISTS idx_pdpp_access_family ON pdpp_access_tokens (family_id);
|
|
134
|
+
`;
|
|
135
|
+
export function hashToken(token) {
|
|
136
|
+
return createHash("sha256").update(token, "utf8").digest("hex");
|
|
137
|
+
}
|
|
138
|
+
export function newOpaqueToken(prefix) {
|
|
139
|
+
return `${prefix}_${randomBytes(32).toString("hex")}`;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Open (and if needed create) the PDPP authorization store.
|
|
143
|
+
*
|
|
144
|
+
* Rejects a store whose persisted version this build cannot validate, before
|
|
145
|
+
* any caller reads authorization facts out of it. That is §9 AS item 21: the
|
|
146
|
+
* failure is loud and up front, not a silently-degraded read path.
|
|
147
|
+
*/
|
|
148
|
+
export function openPdppAuthStore(dbPath) {
|
|
149
|
+
const db = new Database(dbPath);
|
|
150
|
+
db.pragma("journal_mode = WAL");
|
|
151
|
+
// Transactions here express atomicity guarantees; a torn write would defeat
|
|
152
|
+
// exactly the single-use and code-consumption invariants they protect.
|
|
153
|
+
db.pragma("synchronous = FULL");
|
|
154
|
+
db.pragma("foreign_keys = ON");
|
|
155
|
+
db.exec(SCHEMA_SQL);
|
|
156
|
+
// Additive column migration. `CREATE TABLE IF NOT EXISTS` above does not
|
|
157
|
+
// alter an existing table, so a database created before `document` existed
|
|
158
|
+
// still lacks it. Adding a nullable column is backward compatible in both
|
|
159
|
+
// directions: an older build ignores it, and this build treats NULL as
|
|
160
|
+
// "not verifiable from the store" rather than as a failure. That is why
|
|
161
|
+
// this does NOT bump PDPP_AUTH_STATE_VERSION, which would throw
|
|
162
|
+
// UnsupportedAuthStateError and refuse to boot an existing deployment.
|
|
163
|
+
const declarationColumns = db
|
|
164
|
+
.prepare("PRAGMA table_info(pdpp_declarations)")
|
|
165
|
+
.all();
|
|
166
|
+
if (!declarationColumns.some((column) => column.name === "document")) {
|
|
167
|
+
db.exec("ALTER TABLE pdpp_declarations ADD COLUMN document TEXT");
|
|
168
|
+
}
|
|
169
|
+
const accessTokenColumns = db
|
|
170
|
+
.prepare("PRAGMA table_info(pdpp_access_tokens)")
|
|
171
|
+
.all();
|
|
172
|
+
if (!accessTokenColumns.some((column) => column.name === "owner_source_id")) {
|
|
173
|
+
db.exec("ALTER TABLE pdpp_access_tokens ADD COLUMN owner_source_id TEXT");
|
|
174
|
+
}
|
|
175
|
+
if (!accessTokenColumns.some((column) => column.name === "owner_instance_ids_json")) {
|
|
176
|
+
db.exec("ALTER TABLE pdpp_access_tokens ADD COLUMN owner_instance_ids_json TEXT");
|
|
177
|
+
}
|
|
178
|
+
const meta = db
|
|
179
|
+
.prepare("SELECT version FROM pdpp_state_meta WHERE id = 1")
|
|
180
|
+
.get();
|
|
181
|
+
if (meta === undefined) {
|
|
182
|
+
db.prepare("INSERT INTO pdpp_state_meta (id, version) VALUES (1, ?)").run(PDPP_AUTH_STATE_VERSION);
|
|
183
|
+
}
|
|
184
|
+
else if (meta.version !== PDPP_AUTH_STATE_VERSION) {
|
|
185
|
+
db.close();
|
|
186
|
+
throw new UnsupportedAuthStateError(meta.version);
|
|
187
|
+
}
|
|
188
|
+
return new PdppAuthStore(db);
|
|
189
|
+
}
|
|
190
|
+
export class PdppAuthStore {
|
|
191
|
+
db;
|
|
192
|
+
constructor(db) {
|
|
193
|
+
this.db = db;
|
|
194
|
+
}
|
|
195
|
+
close() {
|
|
196
|
+
this.db.close();
|
|
197
|
+
}
|
|
198
|
+
// -- declarations -------------------------------------------------------
|
|
199
|
+
/**
|
|
200
|
+
* Retain a declaration snapshot, and the exact bytes it was parsed from.
|
|
201
|
+
*
|
|
202
|
+
* Idempotent per (source_id, version): a re-retrieval of the same revision
|
|
203
|
+
* must not disturb the retained facts an issued grant points at, so the
|
|
204
|
+
* first snapshot wins and is never overwritten.
|
|
205
|
+
*
|
|
206
|
+
* The one exception is BACKFILLING `document`. A deployment that retained a
|
|
207
|
+
* version before this column existed has a NULL document, and a plain
|
|
208
|
+
* `DO NOTHING` would leave it NULL forever — the existing row would keep
|
|
209
|
+
* winning on every restart, so the digest would stay unverifiable until the
|
|
210
|
+
* declaration version happened to change. `COALESCE` fills it in once and
|
|
211
|
+
* then never changes it, so backfill happens exactly when it is missing and
|
|
212
|
+
* the retained bytes remain immutable thereafter.
|
|
213
|
+
*/
|
|
214
|
+
retainDeclaration(snapshot, document) {
|
|
215
|
+
this.db
|
|
216
|
+
.prepare(`INSERT INTO pdpp_declarations
|
|
217
|
+
(source_id, version, digest, snapshot_json, document, retrieved_at)
|
|
218
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
219
|
+
ON CONFLICT (source_id, version) DO UPDATE SET
|
|
220
|
+
document = COALESCE(pdpp_declarations.document, excluded.document)`)
|
|
221
|
+
.run(snapshot.source_id, snapshot.version, snapshot.digest, JSON.stringify(snapshot), document ?? null, new Date().toISOString());
|
|
222
|
+
}
|
|
223
|
+
getDeclaration(sourceId, version) {
|
|
224
|
+
const row = this.db
|
|
225
|
+
.prepare("SELECT snapshot_json FROM pdpp_declarations WHERE source_id = ? AND version = ?")
|
|
226
|
+
.get(sourceId, version);
|
|
227
|
+
return row ? JSON.parse(row.snapshot_json) : null;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* The exact retrieved bytes a retained declaration was parsed from.
|
|
231
|
+
*
|
|
232
|
+
* Returns null when this row predates the `document` column and has not been
|
|
233
|
+
* backfilled. A caller MUST treat null as "cannot verify from the store",
|
|
234
|
+
* never as "verified" — the whole point of this accessor is that re-digesting
|
|
235
|
+
* `snapshot_json` would silently produce a different value than the retained
|
|
236
|
+
* `digest`.
|
|
237
|
+
*/
|
|
238
|
+
getDeclarationDocument(sourceId, version) {
|
|
239
|
+
const row = this.db
|
|
240
|
+
.prepare("SELECT document FROM pdpp_declarations WHERE source_id = ? AND version = ?")
|
|
241
|
+
.get(sourceId, version);
|
|
242
|
+
return row?.document ?? null;
|
|
243
|
+
}
|
|
244
|
+
// -- grants -------------------------------------------------------------
|
|
245
|
+
insertGrant(input) {
|
|
246
|
+
const { grant } = input;
|
|
247
|
+
this.db
|
|
248
|
+
.prepare(`INSERT INTO pdpp_grants
|
|
249
|
+
(grant_id, subject_id, client_id, source_id, declaration_version,
|
|
250
|
+
access_mode, grant_json, issued_at, expires_at, review_digest,
|
|
251
|
+
consent_evidence_json)
|
|
252
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
253
|
+
.run(grant.grant_id, input.subjectId, grant.client.client_id, grant.source.id, grant.source_declaration.version, grant.access_mode, JSON.stringify(grant), grant.issued_at, grant.expires_at ?? null, input.reviewDigest, input.consentEvidence ? JSON.stringify(input.consentEvidence) : null);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Persist an issued grant and its authorization code as one durable unit.
|
|
257
|
+
*
|
|
258
|
+
* An owner approval produces exactly one grant and exactly one redeemable
|
|
259
|
+
* code; there is no meaningful state where one exists without the other.
|
|
260
|
+
* Without this, a failure between the two inserts (disk full, process
|
|
261
|
+
* kill) could leave a grant with no code ever issued for it — and, worse,
|
|
262
|
+
* strand the caller's in-memory approval state with nothing durable to
|
|
263
|
+
* show for it. Wrapping both in one transaction makes the local write
|
|
264
|
+
* atomic: it either lands completely or not at all, so a caller can retry
|
|
265
|
+
* the same approval decision safely on failure.
|
|
266
|
+
*/
|
|
267
|
+
insertGrantWithAuthCode(input) {
|
|
268
|
+
const tx = this.db.transaction(() => {
|
|
269
|
+
this.insertGrant({
|
|
270
|
+
grant: input.grant,
|
|
271
|
+
subjectId: input.subjectId,
|
|
272
|
+
reviewDigest: input.reviewDigest,
|
|
273
|
+
consentEvidence: input.consentEvidence,
|
|
274
|
+
});
|
|
275
|
+
this.insertAuthCode(input.code, input.authCode);
|
|
276
|
+
});
|
|
277
|
+
tx();
|
|
278
|
+
}
|
|
279
|
+
getGrant(grantId) {
|
|
280
|
+
const row = this.db
|
|
281
|
+
.prepare("SELECT * FROM pdpp_grants WHERE grant_id = ?")
|
|
282
|
+
.get(grantId);
|
|
283
|
+
if (!row)
|
|
284
|
+
return null;
|
|
285
|
+
return {
|
|
286
|
+
grant: JSON.parse(row.grant_json),
|
|
287
|
+
subjectId: row.subject_id,
|
|
288
|
+
clientId: row.client_id,
|
|
289
|
+
reviewDigest: row.review_digest,
|
|
290
|
+
issuedAt: row.issued_at,
|
|
291
|
+
expiresAt: row.expires_at,
|
|
292
|
+
revokedAt: row.revoked_at,
|
|
293
|
+
consumedAt: row.consumed_at,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
listGrantsForSubject(subjectId) {
|
|
297
|
+
const rows = this.db
|
|
298
|
+
.prepare("SELECT * FROM pdpp_grants WHERE subject_id = ? ORDER BY issued_at DESC")
|
|
299
|
+
.all(subjectId);
|
|
300
|
+
return rows.map((row) => ({
|
|
301
|
+
grant: JSON.parse(row.grant_json),
|
|
302
|
+
subjectId: row.subject_id,
|
|
303
|
+
clientId: row.client_id,
|
|
304
|
+
reviewDigest: row.review_digest,
|
|
305
|
+
issuedAt: row.issued_at,
|
|
306
|
+
expiresAt: row.expires_at,
|
|
307
|
+
revokedAt: row.revoked_at,
|
|
308
|
+
consumedAt: row.consumed_at,
|
|
309
|
+
}));
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Grant lifecycle (§9 AS item 8). Revoked beats expired: once an owner
|
|
313
|
+
* revokes, that is the fact worth reporting, and it does not become
|
|
314
|
+
* "expired" later just because the clock passed.
|
|
315
|
+
*/
|
|
316
|
+
grantStatus(stored, now = new Date()) {
|
|
317
|
+
if (stored.revokedAt)
|
|
318
|
+
return "revoked";
|
|
319
|
+
if (stored.expiresAt && Date.parse(stored.expiresAt) <= now.getTime()) {
|
|
320
|
+
return "expired";
|
|
321
|
+
}
|
|
322
|
+
return "active";
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Revoke a grant and everything linked to it, in one transaction.
|
|
326
|
+
*
|
|
327
|
+
* §2 of the delivery scope: the AS marks the grant and every linked token
|
|
328
|
+
* family inactive *immediately*. Doing this in one transaction means there
|
|
329
|
+
* is no window where the grant reads revoked but a linked access token still
|
|
330
|
+
* introspects active.
|
|
331
|
+
*
|
|
332
|
+
* Returns false if the grant does not exist or was already revoked, so a
|
|
333
|
+
* double revoke is not reported as a fresh one.
|
|
334
|
+
*/
|
|
335
|
+
revokeGrant(grantId, now = new Date()) {
|
|
336
|
+
const at = now.toISOString();
|
|
337
|
+
const tx = this.db.transaction(() => {
|
|
338
|
+
const result = this.db
|
|
339
|
+
.prepare("UPDATE pdpp_grants SET revoked_at = ? WHERE grant_id = ? AND revoked_at IS NULL")
|
|
340
|
+
.run(at, grantId);
|
|
341
|
+
if (result.changes === 0)
|
|
342
|
+
return false;
|
|
343
|
+
this.db
|
|
344
|
+
.prepare("UPDATE pdpp_access_tokens SET revoked_at = ? WHERE grant_id = ? AND revoked_at IS NULL")
|
|
345
|
+
.run(at, grantId);
|
|
346
|
+
this.db
|
|
347
|
+
.prepare("UPDATE pdpp_refresh_families SET revoked_at = ? WHERE grant_id = ? AND revoked_at IS NULL")
|
|
348
|
+
.run(at, grantId);
|
|
349
|
+
return true;
|
|
350
|
+
});
|
|
351
|
+
return tx();
|
|
352
|
+
}
|
|
353
|
+
// -- authorization codes ------------------------------------------------
|
|
354
|
+
insertAuthCode(code, record) {
|
|
355
|
+
this.db
|
|
356
|
+
.prepare(`INSERT INTO pdpp_auth_codes
|
|
357
|
+
(code_hash, grant_id, client_id, redirect_uri, code_challenge,
|
|
358
|
+
code_challenge_method, expires_at)
|
|
359
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`)
|
|
360
|
+
.run(hashToken(code), record.grantId, record.clientId, record.redirectUri, record.codeChallenge, record.codeChallengeMethod, record.expiresAt);
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Consume an authorization code atomically (§9 AS item 19).
|
|
364
|
+
*
|
|
365
|
+
* The `redeemed_at IS NULL` predicate lives in the UPDATE, so two concurrent
|
|
366
|
+
* redemptions of the same code cannot both see it unredeemed. Exactly one
|
|
367
|
+
* gets `changes === 1`; the loser gets null and the caller returns
|
|
368
|
+
* `invalid_grant`. An expired code fails the same way, and a replayed code
|
|
369
|
+
* never issues a second token.
|
|
370
|
+
*/
|
|
371
|
+
consumeAuthCode(code, now = new Date()) {
|
|
372
|
+
const hash = hashToken(code);
|
|
373
|
+
const at = now.toISOString();
|
|
374
|
+
const tx = this.db.transaction(() => {
|
|
375
|
+
const result = this.db
|
|
376
|
+
.prepare(`UPDATE pdpp_auth_codes SET redeemed_at = ?
|
|
377
|
+
WHERE code_hash = ? AND redeemed_at IS NULL AND expires_at > ?`)
|
|
378
|
+
.run(at, hash, at);
|
|
379
|
+
if (result.changes === 0)
|
|
380
|
+
return null;
|
|
381
|
+
const row = this.db
|
|
382
|
+
.prepare("SELECT * FROM pdpp_auth_codes WHERE code_hash = ?")
|
|
383
|
+
.get(hash);
|
|
384
|
+
return {
|
|
385
|
+
grantId: row.grant_id,
|
|
386
|
+
clientId: row.client_id,
|
|
387
|
+
redirectUri: row.redirect_uri,
|
|
388
|
+
codeChallenge: row.code_challenge,
|
|
389
|
+
codeChallengeMethod: row.code_challenge_method,
|
|
390
|
+
expiresAt: row.expires_at,
|
|
391
|
+
};
|
|
392
|
+
});
|
|
393
|
+
return tx();
|
|
394
|
+
}
|
|
395
|
+
// -- tokens -------------------------------------------------------------
|
|
396
|
+
/**
|
|
397
|
+
* Consume a `single_use` grant atomically with the first client access token
|
|
398
|
+
* (§9 AS item 10 / §7 access modes).
|
|
399
|
+
*
|
|
400
|
+
* Both the consumption and the token insert happen in one transaction with
|
|
401
|
+
* the precondition in the UPDATE's WHERE clause, so a second concurrent
|
|
402
|
+
* issuance attempt finds `changes === 0` and issues nothing. A `continuous`
|
|
403
|
+
* grant skips the consume step and may issue repeatedly.
|
|
404
|
+
*
|
|
405
|
+
* Returns false when the grant was already consumed — the caller maps that
|
|
406
|
+
* to `invalid_grant`. Note §7: the RS still honors tokens already issued
|
|
407
|
+
* against a consumed grant until they expire or are revoked; consumption
|
|
408
|
+
* blocks *new* token issuance, not use of the existing token.
|
|
409
|
+
*/
|
|
410
|
+
issueAccessToken(input) {
|
|
411
|
+
const now = input.now ?? new Date();
|
|
412
|
+
const at = now.toISOString();
|
|
413
|
+
const tx = this.db.transaction(() => {
|
|
414
|
+
if (input.consumeSingleUse) {
|
|
415
|
+
if (!input.grantId)
|
|
416
|
+
return false;
|
|
417
|
+
const consumed = this.db
|
|
418
|
+
.prepare(`UPDATE pdpp_grants SET consumed_at = ?
|
|
419
|
+
WHERE grant_id = ? AND consumed_at IS NULL
|
|
420
|
+
AND access_mode = 'single_use' AND revoked_at IS NULL`)
|
|
421
|
+
.run(at, input.grantId);
|
|
422
|
+
if (consumed.changes === 0)
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
this.db
|
|
426
|
+
.prepare(`INSERT INTO pdpp_access_tokens
|
|
427
|
+
(token_hash, grant_id, subject_id, client_id, token_kind, owner_source_id,
|
|
428
|
+
owner_instance_ids_json, family_id, issued_at, expires_at)
|
|
429
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
430
|
+
.run(hashToken(input.token), input.grantId, input.subjectId, input.clientId, input.tokenKind, input.ownerSourceId ?? null, input.ownerInstanceIds
|
|
431
|
+
? JSON.stringify(input.ownerInstanceIds)
|
|
432
|
+
: null, input.familyId ?? null, at, input.expiresAt);
|
|
433
|
+
return true;
|
|
434
|
+
});
|
|
435
|
+
return tx();
|
|
436
|
+
}
|
|
437
|
+
getAccessToken(token) {
|
|
438
|
+
const row = this.db
|
|
439
|
+
.prepare("SELECT * FROM pdpp_access_tokens WHERE token_hash = ?")
|
|
440
|
+
.get(hashToken(token));
|
|
441
|
+
if (!row)
|
|
442
|
+
return null;
|
|
443
|
+
return {
|
|
444
|
+
tokenHash: row.token_hash,
|
|
445
|
+
grantId: row.grant_id,
|
|
446
|
+
subjectId: row.subject_id,
|
|
447
|
+
clientId: row.client_id,
|
|
448
|
+
tokenKind: row.token_kind,
|
|
449
|
+
ownerSourceId: row.owner_source_id,
|
|
450
|
+
ownerInstanceIds: row.owner_instance_ids_json
|
|
451
|
+
? JSON.parse(row.owner_instance_ids_json)
|
|
452
|
+
: null,
|
|
453
|
+
familyId: row.family_id,
|
|
454
|
+
issuedAt: row.issued_at,
|
|
455
|
+
expiresAt: row.expires_at,
|
|
456
|
+
revokedAt: row.revoked_at,
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
// -- refresh families ---------------------------------------------------
|
|
460
|
+
createRefreshFamily(grantId) {
|
|
461
|
+
const familyId = `rfam_${randomBytes(16).toString("hex")}`;
|
|
462
|
+
this.db
|
|
463
|
+
.prepare("INSERT INTO pdpp_refresh_families (family_id, grant_id) VALUES (?, ?)")
|
|
464
|
+
.run(familyId, grantId);
|
|
465
|
+
return familyId;
|
|
466
|
+
}
|
|
467
|
+
insertRefreshToken(token, familyId, now = new Date()) {
|
|
468
|
+
this.db
|
|
469
|
+
.prepare("INSERT INTO pdpp_refresh_tokens (token_hash, family_id, issued_at) VALUES (?, ?, ?)")
|
|
470
|
+
.run(hashToken(token), familyId, now.toISOString());
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Rotate a refresh token, detecting reuse (§9 AS item 20).
|
|
474
|
+
*
|
|
475
|
+
* Three outcomes:
|
|
476
|
+
* - `{ ok: true }`: the presented token was current; it is superseded and
|
|
477
|
+
* the caller issues a replacement in the same family.
|
|
478
|
+
* - `{ ok: false, reason: "reuse_detected" }`: the token was already
|
|
479
|
+
* superseded. The family and every family-linked access token are
|
|
480
|
+
* revoked here, atomically, before returning. The caller returns
|
|
481
|
+
* `invalid_grant` and the client must obtain fresh authorization.
|
|
482
|
+
* - `{ ok: false, reason: "unknown" | "family_revoked" }`: nothing to
|
|
483
|
+
* rotate.
|
|
484
|
+
*
|
|
485
|
+
* Reuse revoking the family is the whole point: an attacker who replays a
|
|
486
|
+
* stolen refresh token and a legitimate client who replays after a dropped
|
|
487
|
+
* response are indistinguishable, so the safe response is to kill both and
|
|
488
|
+
* force a fresh grant-backed authorization.
|
|
489
|
+
*/
|
|
490
|
+
rotateRefreshToken(presented, now = new Date()) {
|
|
491
|
+
const hash = hashToken(presented);
|
|
492
|
+
const at = now.toISOString();
|
|
493
|
+
const tx = this.db.transaction(() => {
|
|
494
|
+
const row = this.db
|
|
495
|
+
.prepare(`SELECT t.token_hash, t.family_id, t.superseded_at,
|
|
496
|
+
f.revoked_at AS family_revoked_at, f.grant_id
|
|
497
|
+
FROM pdpp_refresh_tokens t
|
|
498
|
+
JOIN pdpp_refresh_families f ON f.family_id = t.family_id
|
|
499
|
+
WHERE t.token_hash = ?`)
|
|
500
|
+
.get(hash);
|
|
501
|
+
if (!row)
|
|
502
|
+
return { ok: false, reason: "unknown" };
|
|
503
|
+
if (row.family_revoked_at) {
|
|
504
|
+
return { ok: false, reason: "family_revoked" };
|
|
505
|
+
}
|
|
506
|
+
if (row.superseded_at) {
|
|
507
|
+
// Reuse. Burn the family and every access token issued under it.
|
|
508
|
+
this.db
|
|
509
|
+
.prepare("UPDATE pdpp_refresh_families SET revoked_at = ? WHERE family_id = ?")
|
|
510
|
+
.run(at, row.family_id);
|
|
511
|
+
this.db
|
|
512
|
+
.prepare(`UPDATE pdpp_access_tokens SET revoked_at = ?
|
|
513
|
+
WHERE family_id = ? AND revoked_at IS NULL`)
|
|
514
|
+
.run(at, row.family_id);
|
|
515
|
+
return { ok: false, reason: "reuse_detected" };
|
|
516
|
+
}
|
|
517
|
+
this.db
|
|
518
|
+
.prepare("UPDATE pdpp_refresh_tokens SET superseded_at = ? WHERE token_hash = ?")
|
|
519
|
+
.run(at, hash);
|
|
520
|
+
return {
|
|
521
|
+
ok: true,
|
|
522
|
+
familyId: row.family_id,
|
|
523
|
+
grantId: row.grant_id,
|
|
524
|
+
};
|
|
525
|
+
});
|
|
526
|
+
return tx();
|
|
527
|
+
}
|
|
528
|
+
/** Revoke one access token. Returns false if unknown or already revoked. */
|
|
529
|
+
revokeAccessToken(token, now = new Date()) {
|
|
530
|
+
const result = this.db
|
|
531
|
+
.prepare("UPDATE pdpp_access_tokens SET revoked_at = ? WHERE token_hash = ? AND revoked_at IS NULL")
|
|
532
|
+
.run(now.toISOString(), hashToken(token));
|
|
533
|
+
return result.changes > 0;
|
|
534
|
+
}
|
|
535
|
+
isFamilyRevoked(familyId) {
|
|
536
|
+
const row = this.db
|
|
537
|
+
.prepare("SELECT revoked_at FROM pdpp_refresh_families WHERE family_id = ?")
|
|
538
|
+
.get(familyId);
|
|
539
|
+
return row ? row.revoked_at !== null : true;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/pdpp/store.ts"],"names":[],"mappings":"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"}
|