@structured-id/ui-core 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/README.md +22 -10
  2. package/package.json +25 -43
  3. package/src/components/SidStatusBadge.vue +1 -5
  4. package/src/composables/useAccountTransport.test.ts +2 -1
  5. package/src/composables/useGrpc.test.ts +17 -14
  6. package/src/composables/useGrpc.ts +4 -2
  7. package/src/index.ts +2 -120
  8. package/src/profile/components/ConsentManager.vue +5 -1
  9. package/src/profile/components/ProfileEditor.vue +1 -1
  10. package/src/profile/components/SecuritySettings.vue +5 -1
  11. package/src/profile/components/SessionList.vue +2 -1
  12. package/src/profile/components/SidRegistrationForm.test.ts +173 -1
  13. package/src/profile/components/SidRegistrationForm.vue +88 -32
  14. package/src/profile/components/index.ts +1 -4
  15. package/src/profile/composables/index.ts +1 -0
  16. package/src/profile/composables/operationPlan.test.ts +155 -0
  17. package/src/profile/composables/operationPlan.ts +218 -0
  18. package/src/profile/composables/useAccountApi.ts +4 -4
  19. package/src/profile/composables/useAuth.test.ts +312 -31
  20. package/src/profile/composables/useAuth.ts +176 -30
  21. package/src/profile/composables/useAuthApi.opaque.test.ts +13 -6
  22. package/src/profile/composables/useAuthApi.ts +40 -18
  23. package/src/profile/composables/useCurrentPasswordRequirement.test.ts +196 -0
  24. package/src/profile/composables/useCurrentPasswordRequirement.ts +95 -0
  25. package/src/profile/composables/useEnrollmentApi.test.ts +7 -3
  26. package/src/profile/composables/useEnrollmentApi.ts +3 -5
  27. package/src/profile/composables/useIdentityApi.ts +4 -4
  28. package/src/profile/composables/useSecurityLevel.test.ts +1 -1
  29. package/src/profile/composables/useSecurityLevel.ts +1 -1
  30. package/src/quasar/SidPasswordCeremony.test.ts +253 -0
  31. package/src/quasar/SidPasswordCeremony.vue +456 -0
  32. package/src/quasar/index.ts +2 -0
  33. package/src/utils/rpcRefusal.test.ts +4 -4
  34. package/src/utils/rpcRefusal.ts +8 -4
  35. package/dist/browser-vp/index.cjs +0 -349
  36. package/dist/browser-vp/index.d.cts +0 -276
  37. package/dist/browser-vp/index.d.ts +0 -276
  38. package/dist/browser-vp/index.js +0 -349
  39. package/dist/chunk-C3Q766CY.cjs +0 -125
  40. package/dist/chunk-J5YXRXB5.js +0 -125
  41. package/dist/index.cjs +0 -23581
  42. package/dist/index.d.cts +0 -11539
  43. package/dist/index.d.ts +0 -11539
  44. package/dist/index.js +0 -23581
  45. package/dist/storage-4CB2JTLW.cjs +0 -22
  46. package/dist/storage-THRWREZP.js +0 -22
  47. package/src/browser-vp/crypto.ts +0 -253
  48. package/src/browser-vp/index.test.ts +0 -10
  49. package/src/browser-vp/index.ts +0 -116
  50. package/src/browser-vp/prf.ts +0 -179
  51. package/src/browser-vp/storage.test.ts +0 -9
  52. package/src/browser-vp/storage.ts +0 -163
  53. package/src/browser-vp/types.ts +0 -95
  54. package/src/browser-vp/vp.test.ts +0 -43
  55. package/src/browser-vp/vp.ts +0 -111
  56. package/src/generated/google/api/http.ts +0 -687
  57. package/src/generated/google/api/httpbody.ts +0 -159
  58. package/src/generated/google/protobuf/any.ts +0 -326
  59. package/src/generated/google/protobuf/descriptor.ts +0 -4865
  60. package/src/generated/google/protobuf/duration.ts +0 -230
  61. package/src/generated/google/protobuf/empty.ts +0 -94
  62. package/src/generated/google/protobuf/struct.ts +0 -484
  63. package/src/generated/google/protobuf/timestamp.ts +0 -287
  64. package/src/generated/google/rpc/error_details.ts +0 -1386
  65. package/src/generated/google/rpc/status.ts +0 -127
  66. package/src/generated/sid/v1/account/account.client.ts +0 -527
  67. package/src/generated/sid/v1/account/account.ts +0 -3261
  68. package/src/generated/sid/v1/admin/admin.client.ts +0 -139
  69. package/src/generated/sid/v1/admin/admin.ts +0 -781
  70. package/src/generated/sid/v1/admin/admin_roles.client.ts +0 -181
  71. package/src/generated/sid/v1/admin/admin_roles.ts +0 -1606
  72. package/src/generated/sid/v1/admin/analytics.client.ts +0 -106
  73. package/src/generated/sid/v1/admin/analytics.ts +0 -1017
  74. package/src/generated/sid/v1/admin/branding.client.ts +0 -166
  75. package/src/generated/sid/v1/admin/branding.ts +0 -1906
  76. package/src/generated/sid/v1/admin/enrollment.client.ts +0 -177
  77. package/src/generated/sid/v1/admin/enrollment.ts +0 -1766
  78. package/src/generated/sid/v1/admin/events.client.ts +0 -195
  79. package/src/generated/sid/v1/admin/events.ts +0 -1900
  80. package/src/generated/sid/v1/admin/flow.client.ts +0 -183
  81. package/src/generated/sid/v1/admin/flow.ts +0 -1552
  82. package/src/generated/sid/v1/admin/keys.client.ts +0 -107
  83. package/src/generated/sid/v1/admin/keys.ts +0 -931
  84. package/src/generated/sid/v1/admin/login_experience.client.ts +0 -160
  85. package/src/generated/sid/v1/admin/login_experience.ts +0 -1061
  86. package/src/generated/sid/v1/admin/migration.client.ts +0 -127
  87. package/src/generated/sid/v1/admin/migration.ts +0 -1948
  88. package/src/generated/sid/v1/admin/notifications.client.ts +0 -134
  89. package/src/generated/sid/v1/admin/notifications.ts +0 -1273
  90. package/src/generated/sid/v1/admin/operations.client.ts +0 -241
  91. package/src/generated/sid/v1/admin/operations.ts +0 -2633
  92. package/src/generated/sid/v1/admin/organizations.client.ts +0 -57
  93. package/src/generated/sid/v1/admin/organizations.ts +0 -332
  94. package/src/generated/sid/v1/admin/provisioning.client.ts +0 -232
  95. package/src/generated/sid/v1/admin/provisioning.ts +0 -1383
  96. package/src/generated/sid/v1/admin/scopes.client.ts +0 -171
  97. package/src/generated/sid/v1/admin/scopes.ts +0 -1324
  98. package/src/generated/sid/v1/admin/security.client.ts +0 -410
  99. package/src/generated/sid/v1/admin/security.ts +0 -4077
  100. package/src/generated/sid/v1/admin/sessions.client.ts +0 -114
  101. package/src/generated/sid/v1/admin/sessions.ts +0 -923
  102. package/src/generated/sid/v1/admin/settings.client.ts +0 -351
  103. package/src/generated/sid/v1/admin/settings.ts +0 -2458
  104. package/src/generated/sid/v1/admin/user_profile.client.ts +0 -57
  105. package/src/generated/sid/v1/admin/user_profile.ts +0 -730
  106. package/src/generated/sid/v1/admin/webhooks.client.ts +0 -269
  107. package/src/generated/sid/v1/admin/webhooks.ts +0 -2804
  108. package/src/generated/sid/v1/attestation/attestation.client.ts +0 -115
  109. package/src/generated/sid/v1/attestation/attestation.ts +0 -813
  110. package/src/generated/sid/v1/authn/auth.client.ts +0 -943
  111. package/src/generated/sid/v1/authn/auth.ts +0 -9719
  112. package/src/generated/sid/v1/authn/flows.client.ts +0 -413
  113. package/src/generated/sid/v1/authn/flows.ts +0 -3205
  114. package/src/generated/sid/v1/authn/issuer.client.ts +0 -489
  115. package/src/generated/sid/v1/authn/issuer.ts +0 -1600
  116. package/src/generated/sid/v1/authn/password_history.client.ts +0 -57
  117. package/src/generated/sid/v1/authn/password_history.ts +0 -578
  118. package/src/generated/sid/v1/authn/system_integration.client.ts +0 -73
  119. package/src/generated/sid/v1/authn/system_integration.ts +0 -218
  120. package/src/generated/sid/v1/authz/authz.client.ts +0 -459
  121. package/src/generated/sid/v1/authz/authz.ts +0 -6524
  122. package/src/generated/sid/v1/authz/forward_auth.client.ts +0 -97
  123. package/src/generated/sid/v1/authz/forward_auth.ts +0 -79
  124. package/src/generated/sid/v1/authz/governance.client.ts +0 -102
  125. package/src/generated/sid/v1/authz/governance.ts +0 -875
  126. package/src/generated/sid/v1/common/errors.ts +0 -710
  127. package/src/generated/sid/v1/common/pagination.ts +0 -152
  128. package/src/generated/sid/v1/common/transport.ts +0 -227
  129. package/src/generated/sid/v1/common/types.ts +0 -151
  130. package/src/generated/sid/v1/events/event_stream.client.ts +0 -67
  131. package/src/generated/sid/v1/events/event_stream.ts +0 -375
  132. package/src/generated/sid/v1/federation/upstream.client.ts +0 -142
  133. package/src/generated/sid/v1/federation/upstream.ts +0 -1736
  134. package/src/generated/sid/v1/identity/identity.client.ts +0 -467
  135. package/src/generated/sid/v1/identity/identity.ts +0 -5676
  136. package/src/generated/sid/v1/ids/ids.ts +0 -395
  137. package/src/generated/sid/v1/machine/machine_user.client.ts +0 -219
  138. package/src/generated/sid/v1/machine/machine_user.ts +0 -2364
  139. package/src/generated/sid/v1/machine/pat.client.ts +0 -130
  140. package/src/generated/sid/v1/machine/pat.ts +0 -1083
  141. package/src/generated/sid/v1/projects/projects.client.ts +0 -404
  142. package/src/generated/sid/v1/projects/projects.ts +0 -4446
  143. package/src/generated/sid/v1/scim/scim.client.ts +0 -520
  144. package/src/generated/sid/v1/scim/scim.ts +0 -3182
  145. package/src/generated/sid/v1/test/test.client.ts +0 -232
  146. package/src/generated/sid/v1/test/test.ts +0 -1136
  147. package/src/transport/adaptive.test.ts +0 -728
  148. package/src/transport/adaptive.ts +0 -308
  149. package/src/transport/framing.test.ts +0 -227
  150. package/src/transport/framing.ts +0 -98
  151. package/src/transport/webtransport.test.ts +0 -728
  152. package/src/transport/webtransport.ts +0 -323
@@ -1,163 +0,0 @@
1
- /**
2
- * IndexedDB storage for Browser VP.
3
- *
4
- * Database: sid-browser-vp
5
- * ├── bindings — per-service binding entries (key: service_sector)
6
- * └── profile-state — profile state + encrypted blob (key: profile_id)
7
- */
8
-
9
- import type { ProfileState, BindingEntry } from "./types";
10
-
11
- const DB_NAME = "sid-browser-vp";
12
- const DB_VERSION = 1;
13
- const STORE_BINDINGS = "bindings";
14
- const STORE_PROFILE = "profile-state";
15
-
16
- /**
17
- * Open (or create) the IndexedDB database.
18
- */
19
- function openDb(): Promise<IDBDatabase> {
20
- return new Promise((resolve, reject) => {
21
- const request = indexedDB.open(DB_NAME, DB_VERSION);
22
-
23
- request.onupgradeneeded = () => {
24
- const db = request.result;
25
- if (!db.objectStoreNames.contains(STORE_BINDINGS)) {
26
- db.createObjectStore(STORE_BINDINGS, { keyPath: "serviceSector" });
27
- }
28
- if (!db.objectStoreNames.contains(STORE_PROFILE)) {
29
- db.createObjectStore(STORE_PROFILE, { keyPath: "profileId" });
30
- }
31
- };
32
-
33
- request.onsuccess = () => resolve(request.result);
34
- request.onerror = () => reject(request.error);
35
- });
36
- }
37
-
38
- // ── Profile State ──
39
-
40
- /** Save or update profile state. */
41
- export async function saveProfileState(
42
- profileId: string,
43
- state: ProfileState,
44
- ): Promise<void> {
45
- const db = await openDb();
46
- return new Promise((resolve, reject) => {
47
- const tx = db.transaction(STORE_PROFILE, "readwrite");
48
- tx.objectStore(STORE_PROFILE).put({ profileId, ...state });
49
- tx.oncomplete = () => resolve();
50
- tx.onerror = () => reject(tx.error);
51
- });
52
- }
53
-
54
- /** Load profile state by ID. Returns null if not found. */
55
- export async function loadProfileState(
56
- profileId: string,
57
- ): Promise<ProfileState | null> {
58
- const db = await openDb();
59
- return new Promise((resolve, reject) => {
60
- const tx = db.transaction(STORE_PROFILE, "readonly");
61
- const request = tx.objectStore(STORE_PROFILE).get(profileId);
62
- request.onsuccess = () => {
63
- const result = request.result;
64
- if (!result) {
65
- resolve(null);
66
- return;
67
- }
68
- // Strip the profileId key we added for IndexedDB
69
- const { profileId: _profileId, ...state } = result;
70
- resolve(state as ProfileState);
71
- };
72
- request.onerror = () => reject(request.error);
73
- });
74
- }
75
-
76
- /** Delete profile state. */
77
- export async function deleteProfileState(profileId: string): Promise<void> {
78
- const db = await openDb();
79
- return new Promise((resolve, reject) => {
80
- const tx = db.transaction(STORE_PROFILE, "readwrite");
81
- tx.objectStore(STORE_PROFILE).delete(profileId);
82
- tx.oncomplete = () => resolve();
83
- tx.onerror = () => reject(tx.error);
84
- });
85
- }
86
-
87
- // ── Bindings ──
88
-
89
- /** Save or update a binding entry. */
90
- export async function saveBinding(
91
- serviceSector: string,
92
- entry: BindingEntry,
93
- ): Promise<void> {
94
- const db = await openDb();
95
- return new Promise((resolve, reject) => {
96
- const tx = db.transaction(STORE_BINDINGS, "readwrite");
97
- tx.objectStore(STORE_BINDINGS).put({ serviceSector, ...entry });
98
- tx.oncomplete = () => resolve();
99
- tx.onerror = () => reject(tx.error);
100
- });
101
- }
102
-
103
- /** Load a binding by service sector. */
104
- export async function loadBinding(
105
- serviceSector: string,
106
- ): Promise<BindingEntry | null> {
107
- const db = await openDb();
108
- return new Promise((resolve, reject) => {
109
- const tx = db.transaction(STORE_BINDINGS, "readonly");
110
- const request = tx.objectStore(STORE_BINDINGS).get(serviceSector);
111
- request.onsuccess = () => {
112
- const result = request.result;
113
- if (!result) {
114
- resolve(null);
115
- return;
116
- }
117
- const { serviceSector: _serviceSector, ...entry } = result;
118
- resolve(entry as BindingEntry);
119
- };
120
- request.onerror = () => reject(request.error);
121
- });
122
- }
123
-
124
- /** List all stored bindings. */
125
- export async function listBindings(): Promise<
126
- Array<{ serviceSector: string } & BindingEntry>
127
- > {
128
- const db = await openDb();
129
- return new Promise((resolve, reject) => {
130
- const tx = db.transaction(STORE_BINDINGS, "readonly");
131
- const request = tx.objectStore(STORE_BINDINGS).getAll();
132
- request.onsuccess = () => resolve(request.result ?? []);
133
- request.onerror = () => reject(request.error);
134
- });
135
- }
136
-
137
- /** Delete a binding. */
138
- export async function deleteBinding(serviceSector: string): Promise<void> {
139
- const db = await openDb();
140
- return new Promise((resolve, reject) => {
141
- const tx = db.transaction(STORE_BINDINGS, "readwrite");
142
- tx.objectStore(STORE_BINDINGS).delete(serviceSector);
143
- tx.oncomplete = () => resolve();
144
- tx.onerror = () => reject(tx.error);
145
- });
146
- }
147
-
148
- /** Clear all data (profile state + bindings). */
149
- export async function clearAll(): Promise<void> {
150
- const db = await openDb();
151
- return new Promise((resolve, reject) => {
152
- const tx = db.transaction([STORE_PROFILE, STORE_BINDINGS], "readwrite");
153
- tx.objectStore(STORE_PROFILE).clear();
154
- tx.objectStore(STORE_BINDINGS).clear();
155
- tx.oncomplete = () => resolve();
156
- tx.onerror = () => reject(tx.error);
157
- });
158
- }
159
-
160
- /** Check if IndexedDB is available in this environment. */
161
- export function isIndexedDbAvailable(): boolean {
162
- return typeof indexedDB !== "undefined";
163
- }
@@ -1,95 +0,0 @@
1
- /**
2
- * Browser VP types.
3
- */
4
-
5
- /** VP strategy — automatically selected based on browser capabilities. */
6
- export type VpStrategy = "prf" | "device-key";
7
-
8
- /** Profile state stored in IndexedDB. */
9
- export interface ProfileState {
10
- /** Which strategy this profile uses. */
11
- strategy: VpStrategy;
12
-
13
- /** Encrypted profile blob (< 1MB typical). */
14
- encryptedBlob: ArrayBuffer;
15
-
16
- /** Optimistic sync version with SID server. */
17
- blobVersion: number;
18
-
19
- // Strategy A (PRF) fields
20
- /** PRF salt identifier (for versioning). */
21
- prfSalt?: string;
22
- /** SHA-256 of master public key (authenticator change detection). */
23
- masterPublicKeyHash?: string;
24
-
25
- // Strategy B (device-key) fields
26
- /** Non-extractable CryptoKey reference (browser-managed). */
27
- cryptoKeyRef?: CryptoKey;
28
-
29
- /** Last sync timestamp with SID server. */
30
- lastSyncedAt: Date;
31
- /** SID device ID for this browser instance. */
32
- deviceId: string;
33
- }
34
-
35
- /** Per-service binding stored in IndexedDB. */
36
- export interface BindingEntry {
37
- /** Opaque BindingId (pairwise per-service). */
38
- bindingId: string;
39
- /** HD derivation index for this binding. */
40
- bindingIndex: number;
41
- /** Per-binding certificate chain (DER-encoded). */
42
- certChain: ArrayBuffer;
43
- /** Certificate expiry date. */
44
- certExpiry: Date;
45
- /** Last time a VP was created for this binding. */
46
- lastUsedAt: Date;
47
- }
48
-
49
- /** Key material derived from PRF or generated via WebCrypto. */
50
- export interface DerivedKeys {
51
- /** ECDSA P-256 signing key (non-extractable). */
52
- signingKey: CryptoKeyPair;
53
- /** AES-256-GCM data encryption key. */
54
- dataKey: CryptoKey;
55
- }
56
-
57
- /** Verifiable Presentation payload. */
58
- export interface VerifiablePresentation {
59
- /** The service sector this VP is bound to. */
60
- serviceSector: string;
61
- /** Binding ID (pairwise). */
62
- bindingId: string;
63
- /** Signed VP payload (CBOR or JSON). */
64
- payload: ArrayBuffer;
65
- /** Signature over payload. */
66
- signature: ArrayBuffer;
67
- /** Certificate chain for verification. */
68
- certChain: ArrayBuffer;
69
- /** Nonce from relying party (replay protection). */
70
- nonce: string;
71
- /** Audience (relying party origin). */
72
- audience: string;
73
- }
74
-
75
- /** Options for VP creation request from a relying party. */
76
- export interface VpRequest {
77
- /** Nonce for replay protection. */
78
- nonce: string;
79
- /** Requested claims (e.g., ["email", "name"]). */
80
- requestedClaims: string[];
81
- /** Audience (relying party origin). */
82
- audience: string;
83
- /** Service sector identifier. */
84
- serviceSector: string;
85
- }
86
-
87
- /** Result of browser VP initialization. */
88
- export interface BrowserVpContext {
89
- /** Active strategy. */
90
- strategy: VpStrategy;
91
- /** Profile ID. */
92
- profileId: string;
93
- /** Whether VP creation is available (keys + blob + cert ready). */
94
- ready: boolean;
95
- }
@@ -1,43 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { needsRenewal } from "./vp";
3
- import type { BindingEntry } from "./types";
4
-
5
- function makeBinding(certExpiry: Date): BindingEntry {
6
- return {
7
- bindingId: "bind_test",
8
- bindingIndex: 0,
9
- certChain: new ArrayBuffer(0),
10
- certExpiry,
11
- lastUsedAt: new Date(),
12
- };
13
- }
14
-
15
- describe("needsRenewal", () => {
16
- it("returns true when cert expires in less than 7 days", () => {
17
- const sixDays = Date.now() + 6 * 24 * 60 * 60 * 1000;
18
- expect(needsRenewal(makeBinding(new Date(sixDays)))).toBe(true);
19
- });
20
-
21
- it("returns true when cert is already expired", () => {
22
- const yesterday = Date.now() - 24 * 60 * 60 * 1000;
23
- expect(needsRenewal(makeBinding(new Date(yesterday)))).toBe(true);
24
- });
25
-
26
- it("returns false when cert expires in more than 7 days", () => {
27
- const eightDays = Date.now() + 8 * 24 * 60 * 60 * 1000;
28
- expect(needsRenewal(makeBinding(new Date(eightDays)))).toBe(false);
29
- });
30
-
31
- it("returns true at exactly 7 days boundary", () => {
32
- // At exactly 7 days, difference < 7 days in ms because of execution time
33
- // At exactly 7 days, could be true or false depending on execution timing.
34
- // We test just under 7 days to be deterministic.
35
- const justUnder = Date.now() + 7 * 24 * 60 * 60 * 1000 - 1000;
36
- expect(needsRenewal(makeBinding(new Date(justUnder)))).toBe(true);
37
- });
38
-
39
- it("returns false for far-future expiry", () => {
40
- const oneYear = Date.now() + 365 * 24 * 60 * 60 * 1000;
41
- expect(needsRenewal(makeBinding(new Date(oneYear)))).toBe(false);
42
- });
43
- });
@@ -1,111 +0,0 @@
1
- /**
2
- * Verifiable Presentation creation.
3
- *
4
- * Builds and signs VPs using cached keys and profile blob.
5
- * VP creation is fully offline (~50ms with biometric cache).
6
- */
7
-
8
- import type { VpRequest, VerifiablePresentation, BindingEntry } from "./types";
9
- import { sign } from "./crypto";
10
- import { loadBinding, saveBinding } from "./storage";
11
-
12
- const encoder = new TextEncoder();
13
-
14
- /**
15
- * Create a Verifiable Presentation for a service.
16
- *
17
- * @param request - VP request from the relying party.
18
- * @param signingKey - ECDSA P-256 private key (master or per-binding).
19
- * @param profileBlob - Decrypted profile blob.
20
- * @returns Signed VP, or null if binding not found.
21
- */
22
- export async function createVp(
23
- request: VpRequest,
24
- signingKey: CryptoKey,
25
- profileBlob: ArrayBuffer,
26
- ): Promise<VerifiablePresentation | null> {
27
- // Load binding for this service sector
28
- const binding = await loadBinding(request.serviceSector);
29
- if (!binding) {
30
- return null;
31
- }
32
-
33
- // Check certificate expiry
34
- if (binding.certExpiry < new Date()) {
35
- // Certificate expired — VP creation blocked
36
- // Caller should trigger online renewal
37
- return null;
38
- }
39
-
40
- // Build VP payload
41
- const payload = buildPayload(request, binding, profileBlob);
42
-
43
- // Sign with binding key
44
- const signature = await sign(payload, signingKey);
45
-
46
- // Update last used timestamp
47
- await saveBinding(request.serviceSector, {
48
- ...binding,
49
- lastUsedAt: new Date(),
50
- });
51
-
52
- return {
53
- serviceSector: request.serviceSector,
54
- bindingId: binding.bindingId,
55
- payload,
56
- signature,
57
- certChain: binding.certChain,
58
- nonce: request.nonce,
59
- audience: request.audience,
60
- };
61
- }
62
-
63
- /**
64
- * Build the VP payload to be signed.
65
- *
66
- * The payload contains:
67
- * - nonce (replay protection)
68
- * - audience (relying party)
69
- * - bindingId (pairwise identifier)
70
- * - requested claims (selective disclosure)
71
- * - timestamp
72
- */
73
- function buildPayload(
74
- request: VpRequest,
75
- binding: BindingEntry,
76
- _profileBlob: ArrayBuffer,
77
- ): ArrayBuffer {
78
- // Simplified payload structure.
79
- // Production: SD-JWT or Merkle-based selective disclosure
80
- // from the profile blob.
81
- const payload = {
82
- nonce: request.nonce,
83
- aud: request.audience,
84
- sub: binding.bindingId,
85
- iat: Math.floor(Date.now() / 1000),
86
- claims: request.requestedClaims,
87
- };
88
-
89
- return encoder.encode(JSON.stringify(payload)).buffer as ArrayBuffer;
90
- }
91
-
92
- /**
93
- * Check if a binding's certificate needs renewal.
94
- *
95
- * Renewal is attempted 7 days before expiry (background).
96
- */
97
- export function needsRenewal(binding: BindingEntry): boolean {
98
- const sevenDays = 7 * 24 * 60 * 60 * 1000;
99
- return binding.certExpiry.getTime() - Date.now() < sevenDays;
100
- }
101
-
102
- /**
103
- * List all bindings that need certificate renewal.
104
- */
105
- export async function getExpiringSoon(): Promise<
106
- Array<{ serviceSector: string } & BindingEntry>
107
- > {
108
- const { listBindings } = await import("./storage");
109
- const all = await listBindings();
110
- return all.filter((b) => needsRenewal(b));
111
- }