@structured-id/ui-core 1.3.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.
- package/README.md +22 -10
- package/package.json +25 -43
- package/src/components/SidStatusBadge.vue +1 -5
- package/src/composables/useAccountTransport.test.ts +2 -1
- package/src/composables/useGrpc.test.ts +17 -14
- package/src/composables/useGrpc.ts +4 -2
- package/src/index.ts +2 -120
- package/src/profile/components/ConsentManager.vue +5 -1
- package/src/profile/components/ProfileEditor.vue +1 -1
- package/src/profile/components/SecuritySettings.vue +5 -1
- package/src/profile/components/SessionList.vue +2 -1
- package/src/profile/components/SidRegistrationForm.test.ts +173 -1
- package/src/profile/components/SidRegistrationForm.vue +88 -32
- package/src/profile/components/index.ts +1 -4
- package/src/profile/composables/index.ts +1 -0
- package/src/profile/composables/operationPlan.test.ts +155 -0
- package/src/profile/composables/operationPlan.ts +218 -0
- package/src/profile/composables/useAccountApi.ts +4 -4
- package/src/profile/composables/useAuth.test.ts +312 -31
- package/src/profile/composables/useAuth.ts +176 -30
- package/src/profile/composables/useAuthApi.opaque.test.ts +13 -6
- package/src/profile/composables/useAuthApi.ts +40 -18
- package/src/profile/composables/useCurrentPasswordRequirement.test.ts +196 -0
- package/src/profile/composables/useCurrentPasswordRequirement.ts +95 -0
- package/src/profile/composables/useEnrollmentApi.test.ts +7 -3
- package/src/profile/composables/useEnrollmentApi.ts +3 -5
- package/src/profile/composables/useIdentityApi.ts +4 -4
- package/src/profile/composables/useSecurityLevel.test.ts +1 -1
- package/src/profile/composables/useSecurityLevel.ts +1 -1
- package/src/quasar/SidPasswordCeremony.test.ts +253 -0
- package/src/quasar/SidPasswordCeremony.vue +456 -0
- package/src/quasar/index.ts +2 -0
- package/src/utils/rpcRefusal.test.ts +4 -4
- package/src/utils/rpcRefusal.ts +8 -4
- package/dist/browser-vp/index.cjs +0 -349
- package/dist/browser-vp/index.d.cts +0 -276
- package/dist/browser-vp/index.d.ts +0 -276
- package/dist/browser-vp/index.js +0 -349
- package/dist/chunk-C3Q766CY.cjs +0 -125
- package/dist/chunk-J5YXRXB5.js +0 -125
- package/dist/index.cjs +0 -23581
- package/dist/index.d.cts +0 -11539
- package/dist/index.d.ts +0 -11539
- package/dist/index.js +0 -23581
- package/dist/storage-4CB2JTLW.cjs +0 -22
- package/dist/storage-THRWREZP.js +0 -22
- package/src/browser-vp/crypto.ts +0 -253
- package/src/browser-vp/index.test.ts +0 -10
- package/src/browser-vp/index.ts +0 -116
- package/src/browser-vp/prf.ts +0 -179
- package/src/browser-vp/storage.test.ts +0 -9
- package/src/browser-vp/storage.ts +0 -163
- package/src/browser-vp/types.ts +0 -95
- package/src/browser-vp/vp.test.ts +0 -43
- package/src/browser-vp/vp.ts +0 -111
- package/src/generated/google/api/http.ts +0 -687
- package/src/generated/google/api/httpbody.ts +0 -159
- package/src/generated/google/protobuf/any.ts +0 -326
- package/src/generated/google/protobuf/descriptor.ts +0 -4865
- package/src/generated/google/protobuf/duration.ts +0 -230
- package/src/generated/google/protobuf/empty.ts +0 -94
- package/src/generated/google/protobuf/struct.ts +0 -484
- package/src/generated/google/protobuf/timestamp.ts +0 -287
- package/src/generated/google/rpc/error_details.ts +0 -1386
- package/src/generated/google/rpc/status.ts +0 -127
- package/src/generated/sid/v1/account/account.client.ts +0 -527
- package/src/generated/sid/v1/account/account.ts +0 -3261
- package/src/generated/sid/v1/admin/admin.client.ts +0 -139
- package/src/generated/sid/v1/admin/admin.ts +0 -781
- package/src/generated/sid/v1/admin/admin_roles.client.ts +0 -181
- package/src/generated/sid/v1/admin/admin_roles.ts +0 -1606
- package/src/generated/sid/v1/admin/analytics.client.ts +0 -106
- package/src/generated/sid/v1/admin/analytics.ts +0 -1017
- package/src/generated/sid/v1/admin/branding.client.ts +0 -166
- package/src/generated/sid/v1/admin/branding.ts +0 -1906
- package/src/generated/sid/v1/admin/enrollment.client.ts +0 -177
- package/src/generated/sid/v1/admin/enrollment.ts +0 -1766
- package/src/generated/sid/v1/admin/events.client.ts +0 -195
- package/src/generated/sid/v1/admin/events.ts +0 -1900
- package/src/generated/sid/v1/admin/flow.client.ts +0 -183
- package/src/generated/sid/v1/admin/flow.ts +0 -1552
- package/src/generated/sid/v1/admin/keys.client.ts +0 -107
- package/src/generated/sid/v1/admin/keys.ts +0 -931
- package/src/generated/sid/v1/admin/login_experience.client.ts +0 -160
- package/src/generated/sid/v1/admin/login_experience.ts +0 -1061
- package/src/generated/sid/v1/admin/migration.client.ts +0 -127
- package/src/generated/sid/v1/admin/migration.ts +0 -1948
- package/src/generated/sid/v1/admin/notifications.client.ts +0 -134
- package/src/generated/sid/v1/admin/notifications.ts +0 -1273
- package/src/generated/sid/v1/admin/operations.client.ts +0 -241
- package/src/generated/sid/v1/admin/operations.ts +0 -2633
- package/src/generated/sid/v1/admin/organizations.client.ts +0 -57
- package/src/generated/sid/v1/admin/organizations.ts +0 -332
- package/src/generated/sid/v1/admin/provisioning.client.ts +0 -232
- package/src/generated/sid/v1/admin/provisioning.ts +0 -1383
- package/src/generated/sid/v1/admin/scopes.client.ts +0 -171
- package/src/generated/sid/v1/admin/scopes.ts +0 -1324
- package/src/generated/sid/v1/admin/security.client.ts +0 -410
- package/src/generated/sid/v1/admin/security.ts +0 -4077
- package/src/generated/sid/v1/admin/sessions.client.ts +0 -114
- package/src/generated/sid/v1/admin/sessions.ts +0 -923
- package/src/generated/sid/v1/admin/settings.client.ts +0 -351
- package/src/generated/sid/v1/admin/settings.ts +0 -2458
- package/src/generated/sid/v1/admin/user_profile.client.ts +0 -57
- package/src/generated/sid/v1/admin/user_profile.ts +0 -730
- package/src/generated/sid/v1/admin/webhooks.client.ts +0 -269
- package/src/generated/sid/v1/admin/webhooks.ts +0 -2804
- package/src/generated/sid/v1/attestation/attestation.client.ts +0 -115
- package/src/generated/sid/v1/attestation/attestation.ts +0 -813
- package/src/generated/sid/v1/authn/auth.client.ts +0 -943
- package/src/generated/sid/v1/authn/auth.ts +0 -9719
- package/src/generated/sid/v1/authn/flows.client.ts +0 -413
- package/src/generated/sid/v1/authn/flows.ts +0 -3205
- package/src/generated/sid/v1/authn/issuer.client.ts +0 -489
- package/src/generated/sid/v1/authn/issuer.ts +0 -1600
- package/src/generated/sid/v1/authn/password_history.client.ts +0 -57
- package/src/generated/sid/v1/authn/password_history.ts +0 -578
- package/src/generated/sid/v1/authn/system_integration.client.ts +0 -73
- package/src/generated/sid/v1/authn/system_integration.ts +0 -218
- package/src/generated/sid/v1/authz/authz.client.ts +0 -459
- package/src/generated/sid/v1/authz/authz.ts +0 -6524
- package/src/generated/sid/v1/authz/forward_auth.client.ts +0 -97
- package/src/generated/sid/v1/authz/forward_auth.ts +0 -79
- package/src/generated/sid/v1/authz/governance.client.ts +0 -102
- package/src/generated/sid/v1/authz/governance.ts +0 -875
- package/src/generated/sid/v1/common/errors.ts +0 -710
- package/src/generated/sid/v1/common/pagination.ts +0 -152
- package/src/generated/sid/v1/common/transport.ts +0 -227
- package/src/generated/sid/v1/common/types.ts +0 -151
- package/src/generated/sid/v1/events/event_stream.client.ts +0 -67
- package/src/generated/sid/v1/events/event_stream.ts +0 -375
- package/src/generated/sid/v1/federation/upstream.client.ts +0 -142
- package/src/generated/sid/v1/federation/upstream.ts +0 -1736
- package/src/generated/sid/v1/identity/identity.client.ts +0 -467
- package/src/generated/sid/v1/identity/identity.ts +0 -5676
- package/src/generated/sid/v1/ids/ids.ts +0 -395
- package/src/generated/sid/v1/machine/machine_user.client.ts +0 -219
- package/src/generated/sid/v1/machine/machine_user.ts +0 -2364
- package/src/generated/sid/v1/machine/pat.client.ts +0 -130
- package/src/generated/sid/v1/machine/pat.ts +0 -1083
- package/src/generated/sid/v1/projects/projects.client.ts +0 -404
- package/src/generated/sid/v1/projects/projects.ts +0 -4446
- package/src/generated/sid/v1/scim/scim.client.ts +0 -520
- package/src/generated/sid/v1/scim/scim.ts +0 -3182
- package/src/generated/sid/v1/test/test.client.ts +0 -232
- package/src/generated/sid/v1/test/test.ts +0 -1136
- package/src/transport/adaptive.test.ts +0 -728
- package/src/transport/adaptive.ts +0 -308
- package/src/transport/framing.test.ts +0 -227
- package/src/transport/framing.ts +0 -98
- package/src/transport/webtransport.test.ts +0 -728
- 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
|
-
}
|
package/src/browser-vp/types.ts
DELETED
|
@@ -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
|
-
});
|
package/src/browser-vp/vp.ts
DELETED
|
@@ -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
|
-
}
|