@learncard/sss-key-manager 0.1.24 → 0.2.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/README.md +25 -95
- package/dist/api-client.d.ts +44 -7
- package/dist/api-client.d.ts.map +1 -1
- package/dist/atomic-operations.d.ts +10 -2
- package/dist/atomic-operations.d.ts.map +1 -1
- package/dist/email-relay-crypto.d.ts +49 -0
- package/dist/email-relay-crypto.d.ts.map +1 -0
- package/dist/escrow-attestation.d.ts +12 -0
- package/dist/escrow-attestation.d.ts.map +1 -0
- package/dist/escrow-crypto.d.ts +52 -0
- package/dist/escrow-crypto.d.ts.map +1 -0
- package/dist/escrow-nitro-attestation.d.ts +32 -0
- package/dist/escrow-nitro-attestation.d.ts.map +1 -0
- package/dist/escrow-pin.d.ts +22 -0
- package/dist/escrow-pin.d.ts.map +1 -0
- package/dist/index.d.ts +15 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/key-manager.d.ts +2 -0
- package/dist/key-manager.d.ts.map +1 -1
- package/dist/qr-login.d.ts.map +1 -1
- package/dist/recovery-phrase.d.ts +6 -0
- package/dist/recovery-phrase.d.ts.map +1 -1
- package/dist/sss-key-manager.cjs.development.js +5564 -353
- package/dist/sss-key-manager.cjs.development.js.map +4 -4
- package/dist/sss-key-manager.cjs.production.min.js +33 -33
- package/dist/sss-key-manager.cjs.production.min.js.map +4 -4
- package/dist/sss-key-manager.esm.js +3494 -354
- package/dist/sss-key-manager.esm.js.map +4 -4
- package/dist/sss-strategy.d.ts +74 -4
- package/dist/sss-strategy.d.ts.map +1 -1
- package/dist/storage.d.ts +1 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/types.d.ts +98 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -3
- package/src/__fixtures__/escrow-vectors-rust.json +82 -0
- package/src/__fixtures__/escrow-vectors.json +103 -0
- package/src/__fixtures__/nitro-attestation/bad-signature.cbor.b64 +1 -0
- package/src/__fixtures__/nitro-attestation/debug-zero-pcrs.cbor.b64 +1 -0
- package/src/__fixtures__/nitro-attestation/legacy-user-data.cbor.b64 +1 -0
- package/src/__fixtures__/nitro-attestation/manifest.json +60 -0
- package/src/__fixtures__/nitro-attestation/nonce-mismatch.cbor.b64 +1 -0
- package/src/__fixtures__/nitro-attestation/root.pem +12 -0
- package/src/__fixtures__/nitro-attestation/stale-timestamp.cbor.b64 +1 -0
- package/src/__fixtures__/nitro-attestation/tampered-previous-key-ids.cbor.b64 +1 -0
- package/src/__fixtures__/nitro-attestation/truncated-chain.cbor.b64 +1 -0
- package/src/__fixtures__/nitro-attestation/valid.cbor.b64 +1 -0
- package/src/__fixtures__/nitro-attestation/wrong-pcr0.cbor.b64 +1 -0
- package/src/__fixtures__/nitro-attestation/wrong-root.cbor.b64 +1 -0
- package/src/api-client.test.ts +122 -0
- package/src/api-client.ts +107 -116
- package/src/atomic-operations.test.ts +56 -16
- package/src/atomic-operations.ts +55 -19
- package/src/critical-paths.test.ts +51 -1
- package/src/email-relay-crypto.test.ts +87 -0
- package/src/email-relay-crypto.ts +293 -0
- package/src/escrow-attestation.test.ts +90 -0
- package/src/escrow-attestation.ts +125 -0
- package/src/escrow-crypto.test.ts +381 -0
- package/src/escrow-crypto.ts +319 -0
- package/src/escrow-nitro-attestation.test.ts +204 -0
- package/src/escrow-nitro-attestation.ts +286 -0
- package/src/escrow-pin.test.ts +119 -0
- package/src/escrow-pin.ts +83 -0
- package/src/escrow-vectors.test.ts +198 -0
- package/src/index.ts +70 -1
- package/src/key-manager.ts +4 -9
- package/src/qr-login.ts +22 -15
- package/src/recovery-phrase.ts +40 -0
- package/src/sss-session-writes.test.ts +109 -0
- package/src/sss-strategy.test.ts +2736 -111
- package/src/sss-strategy.ts +2226 -224
- package/src/storage.test.ts +66 -8
- package/src/storage.ts +41 -29
- package/src/types.ts +118 -6
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cases": {
|
|
3
|
+
"bad-signature": {
|
|
4
|
+
"accept": false,
|
|
5
|
+
"reason": "signature"
|
|
6
|
+
},
|
|
7
|
+
"debug-zero-pcrs": {
|
|
8
|
+
"accept": false,
|
|
9
|
+
"reason": "debug PCRs"
|
|
10
|
+
},
|
|
11
|
+
"legacy-user-data": {
|
|
12
|
+
"accept": true,
|
|
13
|
+
"previousKeyIds": []
|
|
14
|
+
},
|
|
15
|
+
"nonce-mismatch": {
|
|
16
|
+
"accept": false,
|
|
17
|
+
"nonceHex": "0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a",
|
|
18
|
+
"reason": "nonce"
|
|
19
|
+
},
|
|
20
|
+
"stale-timestamp": {
|
|
21
|
+
"accept": false,
|
|
22
|
+
"reason": "freshness"
|
|
23
|
+
},
|
|
24
|
+
"tampered-previous-key-ids": {
|
|
25
|
+
"accept": false,
|
|
26
|
+
"reason": "signature"
|
|
27
|
+
},
|
|
28
|
+
"truncated-chain": {
|
|
29
|
+
"accept": false,
|
|
30
|
+
"reason": "missing intermediate"
|
|
31
|
+
},
|
|
32
|
+
"valid": {
|
|
33
|
+
"accept": true
|
|
34
|
+
},
|
|
35
|
+
"wrong-pcr0": {
|
|
36
|
+
"accept": false,
|
|
37
|
+
"reason": "pcr"
|
|
38
|
+
},
|
|
39
|
+
"wrong-root": {
|
|
40
|
+
"accept": false,
|
|
41
|
+
"reason": "root pin"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"description": "Public fake CA, never trust in production. Freeze verifier clock to nowMs. Certificates valid 2020-2050. userDataBase64 was generated once by crypto::generate_escrow_key_pair; regeneration reuses it. keyId/previousKeyIds (P9.1) are fixture-only labels bound into the canonical-CBOR user_data wrapper, distinct from userDataBase64 (the escrow SPKI itself, still extracted identically from either user_data format).",
|
|
45
|
+
"keyId": "fixture-key",
|
|
46
|
+
"maxAgeMs": 300000,
|
|
47
|
+
"nonceHex": "0909090909090909090909090909090909090909090909090909090909090909",
|
|
48
|
+
"nowMs": 1790294401000,
|
|
49
|
+
"pcrs": {
|
|
50
|
+
"0": "010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101",
|
|
51
|
+
"1": "020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202",
|
|
52
|
+
"2": "030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303"
|
|
53
|
+
},
|
|
54
|
+
"previousKeyIds": ["fixture-previous-key"],
|
|
55
|
+
"publicKeyBase64": null,
|
|
56
|
+
"rootSha256": "d71ae13f792676894bf8549178ea45acc360b1935e628f678d9e2f3befae9dfe",
|
|
57
|
+
"testOnly": true,
|
|
58
|
+
"timestamp": 1790294400000,
|
|
59
|
+
"userDataBase64": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnX5GwyaaFpFYdl7m9ft+aF3MG6GanWFe7rvTqEGNoojfQtaaRXWNmYbC9affj2Twoy2RlWkOTGFETJQC5/f93w=="
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIBwjCCAUigAwIBAgIBATAKBggqhkjOPQQDAzApMScwJQYDVQQDDB5MZWFybkNh
|
|
3
|
+
cmQgVEVTVCBPTkxZIE5pdHJvIFJvb3QwIBcNMjAwMTAxMDAwMDAwWhgPMjA1MDAx
|
|
4
|
+
MDEwMDAwMDBaMCkxJzAlBgNVBAMMHkxlYXJuQ2FyZCBURVNUIE9OTFkgTml0cm8g
|
|
5
|
+
Um9vdDB2MBAGByqGSM49AgEGBSuBBAAiA2IABBck9uTlAFdttlkj3wkMkhPJrXNJ
|
|
6
|
+
ONf0FdJ5LLG8snfdW2PcZRQBGONRWfeZRqriSz+YkAODjFMLLnsBgWoBqER90QsP
|
|
7
|
+
a2AG4OjfOPIlmTHBqdjVRJ2zVK76DOKqU5xinaNCMEAwHQYDVR0OBBYEFH5W2DvY
|
|
8
|
+
+F7QW1n4UyVGh7YW3YnbMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEG
|
|
9
|
+
MAoGCCqGSM49BAMDA2gAMGUCMQC3icReCK/AVxSDJpCGMMq06g5DlbIYg1Tg9Ad+
|
|
10
|
+
cZvYD0yr0ZuomoKL4B8JI0GBGiUCMFkQ4nGjuxzQu9jc7gb3O2UEhxivT2I2/8/P
|
|
11
|
+
NwOH2sbZ/YY5Za+AsWQ2vgbabHJxYw==
|
|
12
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { SSSApiClient } from './api-client';
|
|
4
|
+
|
|
5
|
+
import type { AuthProvider } from './types';
|
|
6
|
+
|
|
7
|
+
const SENTINEL_TOKEN = 'SENTINEL_TOKEN_XYZ';
|
|
8
|
+
const DID_CHALLENGE_ERROR = 'This operation requires the DID-challenge flow; use createSSSStrategy';
|
|
9
|
+
|
|
10
|
+
const createMockAuthProvider = (): AuthProvider => ({
|
|
11
|
+
getIdToken: vi.fn().mockResolvedValue(SENTINEL_TOKEN),
|
|
12
|
+
getCurrentUser: vi.fn().mockResolvedValue({
|
|
13
|
+
id: 'user-123',
|
|
14
|
+
email: 'sentinel-user@example.com',
|
|
15
|
+
providerType: 'firebase' as const,
|
|
16
|
+
}),
|
|
17
|
+
getProviderType: vi.fn().mockReturnValue('firebase' as const),
|
|
18
|
+
signOut: vi.fn().mockResolvedValue(undefined),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('SSSApiClient', () => {
|
|
22
|
+
let client: SSSApiClient;
|
|
23
|
+
let calls: { url: string; init?: RequestInit }[];
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
calls = [];
|
|
27
|
+
|
|
28
|
+
vi.spyOn(globalThis, 'fetch').mockImplementation(async (url, init) => {
|
|
29
|
+
calls.push({ url: url.toString(), init });
|
|
30
|
+
return new Response(
|
|
31
|
+
JSON.stringify({
|
|
32
|
+
authShare: null,
|
|
33
|
+
primaryDid: null,
|
|
34
|
+
securityLevel: 'basic',
|
|
35
|
+
recoveryMethods: [],
|
|
36
|
+
keyProvider: 'sss',
|
|
37
|
+
shareVersion: 1,
|
|
38
|
+
sssActivationState: 'active',
|
|
39
|
+
recoverySessionToken: 'recovery-session-token',
|
|
40
|
+
rebindSessionToken: 'rebind-session-token',
|
|
41
|
+
recoveryMethodsRequireConfirmation: [],
|
|
42
|
+
}),
|
|
43
|
+
{ status: 200 }
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
client = new SSSApiClient({
|
|
48
|
+
serverUrl: 'http://test-server:5100/api',
|
|
49
|
+
authProvider: createMockAuthProvider(),
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterEach(() => {
|
|
54
|
+
vi.restoreAllMocks();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('uses X-Auth-Token rather than Authorization for provider-authenticated reads', async () => {
|
|
58
|
+
await client.getAuthShare();
|
|
59
|
+
|
|
60
|
+
const headers = calls[0]!.init?.headers as Record<string, string>;
|
|
61
|
+
expect(headers['X-Auth-Token']).toBe(SENTINEL_TOKEN);
|
|
62
|
+
expect(headers.Authorization).toBeUndefined();
|
|
63
|
+
expect(calls[0]!.url).not.toContain(SENTINEL_TOKEN);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('keeps recovery read credentials out of the URL', async () => {
|
|
67
|
+
await client.getRecoveryShare('passkey', 'credential-1');
|
|
68
|
+
|
|
69
|
+
const headers = calls[0]!.init?.headers as Record<string, string>;
|
|
70
|
+
expect(headers['X-Auth-Token']).toBe(SENTINEL_TOKEN);
|
|
71
|
+
expect(calls[0]!.url).not.toContain(SENTINEL_TOKEN);
|
|
72
|
+
expect(calls[0]!.url).not.toMatch(/[?&](token|share)=/i);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('retains the valid unauthenticated identity-recovery helpers', async () => {
|
|
76
|
+
await client.startIdentityRecovery('recovery@example.com');
|
|
77
|
+
await client.verifyIdentityRecovery('recovery@example.com', '123456');
|
|
78
|
+
await client.useIdentityRecoverySession('recovery-session-token', 'phrase');
|
|
79
|
+
|
|
80
|
+
expect(calls.map(call => call.url)).toEqual([
|
|
81
|
+
'http://test-server:5100/api/keys/recovery-session/start',
|
|
82
|
+
'http://test-server:5100/api/keys/recovery-session/verify',
|
|
83
|
+
'http://test-server:5100/api/keys/recovery-session/recover',
|
|
84
|
+
]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('retains identity rebind because it supplies both DID proof and the auth token', async () => {
|
|
88
|
+
await client.completeIdentityRecoveryRebind({
|
|
89
|
+
recoverySessionToken: 'rebind-session-token',
|
|
90
|
+
newAuthToken: SENTINEL_TOKEN,
|
|
91
|
+
providerType: 'firebase',
|
|
92
|
+
primaryDid: 'did:key:zTest',
|
|
93
|
+
authShare: { encryptedData: 'encrypted-share', encryptedDek: '', iv: '' },
|
|
94
|
+
didAuthVp: 'did-auth-vp',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const headers = calls[0]!.init?.headers as Record<string, string>;
|
|
98
|
+
expect(headers.Authorization).toBe('Bearer did-auth-vp');
|
|
99
|
+
expect(headers['X-Auth-Token']).toBe(SENTINEL_TOKEN);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it.each([
|
|
103
|
+
[
|
|
104
|
+
'storeAuthShare',
|
|
105
|
+
() =>
|
|
106
|
+
client.storeAuthShare({
|
|
107
|
+
authShare: { encryptedData: 'share', encryptedDek: '', iv: '' },
|
|
108
|
+
primaryDid: 'did:key:zTest',
|
|
109
|
+
}),
|
|
110
|
+
],
|
|
111
|
+
['addRecoveryMethod', () => client.addRecoveryMethod({ type: 'phrase' })],
|
|
112
|
+
['markMigrated', () => client.markMigrated()],
|
|
113
|
+
['activate', () => client.activate()],
|
|
114
|
+
['sendEmailBackupShare', () => client.sendEmailBackupShare('email-share')],
|
|
115
|
+
['addRecoveryEmail', () => client.addRecoveryEmail('recovery@example.com')],
|
|
116
|
+
['verifyRecoveryEmail', () => client.verifyRecoveryEmail('123456')],
|
|
117
|
+
['deleteUserKey', () => client.deleteUserKey()],
|
|
118
|
+
])('%s fails closed instead of calling a route without DID proof', async (_name, call) => {
|
|
119
|
+
await expect(call()).rejects.toThrow(DID_CHALLENGE_ERROR);
|
|
120
|
+
expect(calls).toHaveLength(0);
|
|
121
|
+
});
|
|
122
|
+
});
|
package/src/api-client.ts
CHANGED
|
@@ -5,12 +5,21 @@
|
|
|
5
5
|
import type {
|
|
6
6
|
AuthProvider,
|
|
7
7
|
AuthProviderType,
|
|
8
|
-
ContactMethod,
|
|
9
8
|
ServerEncryptedShare,
|
|
10
9
|
RecoveryMethodInfo,
|
|
11
10
|
EncryptedShare,
|
|
12
11
|
SecurityLevel,
|
|
12
|
+
SssActivationState,
|
|
13
|
+
IdentityRecoverySession,
|
|
13
14
|
} from './types';
|
|
15
|
+
import type { EmailRelayBranding } from './email-relay-crypto';
|
|
16
|
+
|
|
17
|
+
const DID_CHALLENGE_REQUIRED_ERROR =
|
|
18
|
+
'This operation requires the DID-challenge flow; use createSSSStrategy';
|
|
19
|
+
|
|
20
|
+
const throwDidChallengeRequired = (): never => {
|
|
21
|
+
throw new Error(DID_CHALLENGE_REQUIRED_ERROR);
|
|
22
|
+
};
|
|
14
23
|
|
|
15
24
|
export interface GetAuthShareResponse {
|
|
16
25
|
authShare: ServerEncryptedShare | null;
|
|
@@ -18,13 +27,16 @@ export interface GetAuthShareResponse {
|
|
|
18
27
|
securityLevel: SecurityLevel;
|
|
19
28
|
recoveryMethods: RecoveryMethodInfo[];
|
|
20
29
|
keyProvider: 'web3auth' | 'sss';
|
|
30
|
+
shareVersion: number;
|
|
21
31
|
maskedRecoveryEmail?: string | null;
|
|
32
|
+
sssActivationState: SssActivationState;
|
|
22
33
|
}
|
|
23
34
|
|
|
24
35
|
export interface StoreAuthShareInput {
|
|
25
36
|
authShare: ServerEncryptedShare;
|
|
26
37
|
primaryDid: string;
|
|
27
38
|
securityLevel?: SecurityLevel;
|
|
39
|
+
sssActivationState?: 'provisional';
|
|
28
40
|
}
|
|
29
41
|
|
|
30
42
|
export interface StoreRecoveryShareInput {
|
|
@@ -34,11 +46,23 @@ export interface StoreRecoveryShareInput {
|
|
|
34
46
|
shareVersion?: number;
|
|
35
47
|
}
|
|
36
48
|
|
|
49
|
+
export interface RecoverySessionMaterial {
|
|
50
|
+
authShare: ServerEncryptedShare;
|
|
51
|
+
encryptedShare?: EncryptedShare;
|
|
52
|
+
primaryDid: string;
|
|
53
|
+
shareVersion: number;
|
|
54
|
+
rebindSessionToken: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
37
57
|
export interface ApiClientConfig {
|
|
38
58
|
serverUrl: string;
|
|
39
59
|
authProvider: AuthProvider;
|
|
60
|
+
escrowRelayPublicKey?: string;
|
|
61
|
+
escrowRelayKeyId?: string;
|
|
62
|
+
emailBranding?: EmailRelayBranding;
|
|
40
63
|
}
|
|
41
64
|
|
|
65
|
+
/** @deprecated Use `createSSSStrategy`, which implements the hardened DID-challenge flow. */
|
|
42
66
|
export class SSSApiClient {
|
|
43
67
|
private serverUrl: string;
|
|
44
68
|
private authProvider: AuthProvider;
|
|
@@ -52,23 +76,10 @@ export class SSSApiClient {
|
|
|
52
76
|
const token = await this.authProvider.getIdToken();
|
|
53
77
|
return {
|
|
54
78
|
'Content-Type': 'application/json',
|
|
55
|
-
|
|
79
|
+
'X-Auth-Token': token,
|
|
56
80
|
};
|
|
57
81
|
}
|
|
58
82
|
|
|
59
|
-
private async getContactMethodFromUser(): Promise<ContactMethod | null> {
|
|
60
|
-
const user = await this.authProvider.getCurrentUser();
|
|
61
|
-
if (!user) return null;
|
|
62
|
-
|
|
63
|
-
if (user.email) {
|
|
64
|
-
return { type: 'email' as const, value: user.email.toLowerCase() };
|
|
65
|
-
}
|
|
66
|
-
if (user.phone) {
|
|
67
|
-
return { type: 'phone' as const, value: user.phone };
|
|
68
|
-
}
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
83
|
async getAuthShare(): Promise<GetAuthShareResponse | null> {
|
|
73
84
|
const headers = await this.getAuthHeaders();
|
|
74
85
|
const providerType = this.authProvider.getProviderType();
|
|
@@ -89,54 +100,28 @@ export class SSSApiClient {
|
|
|
89
100
|
return response.json();
|
|
90
101
|
}
|
|
91
102
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const response = await fetch(`${this.serverUrl}/keys/auth-share`, {
|
|
97
|
-
method: 'PUT',
|
|
98
|
-
headers,
|
|
99
|
-
body: JSON.stringify({
|
|
100
|
-
...input,
|
|
101
|
-
providerType,
|
|
102
|
-
}),
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
if (!response.ok) {
|
|
106
|
-
throw new Error(`Failed to store auth share: ${response.statusText}`);
|
|
107
|
-
}
|
|
103
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
104
|
+
async storeAuthShare(_input: StoreAuthShareInput): Promise<void> {
|
|
105
|
+
throwDidChallengeRequired();
|
|
108
106
|
}
|
|
109
107
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const response = await fetch(`${this.serverUrl}/keys/recovery`, {
|
|
115
|
-
method: 'POST',
|
|
116
|
-
headers,
|
|
117
|
-
body: JSON.stringify({
|
|
118
|
-
...input,
|
|
119
|
-
providerType,
|
|
120
|
-
}),
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
if (!response.ok) {
|
|
124
|
-
throw new Error(`Failed to add recovery method: ${response.statusText}`);
|
|
125
|
-
}
|
|
108
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
109
|
+
async addRecoveryMethod(_input: StoreRecoveryShareInput): Promise<void> {
|
|
110
|
+
throwDidChallengeRequired();
|
|
126
111
|
}
|
|
127
112
|
|
|
128
113
|
async getRecoveryShare(
|
|
129
114
|
type: 'passkey' | 'backup' | 'phrase' | 'email',
|
|
130
115
|
credentialId?: string
|
|
131
116
|
): Promise<{ encryptedShare?: EncryptedShare; shareVersion?: number } | null> {
|
|
117
|
+
// P0-4: authToken travels via the X-Auth-Token header — never as a
|
|
118
|
+
// query param, which would land in proxy/ALB access logs. A
|
|
119
|
+
// dedicated header (not Authorization) avoids colliding with
|
|
120
|
+
// clients that send a DID-Auth VP as Authorization for this route.
|
|
132
121
|
const token = await this.authProvider.getIdToken();
|
|
133
122
|
const providerType = this.authProvider.getProviderType();
|
|
134
123
|
|
|
135
|
-
const params = new URLSearchParams({
|
|
136
|
-
type,
|
|
137
|
-
providerType,
|
|
138
|
-
authToken: token,
|
|
139
|
-
});
|
|
124
|
+
const params = new URLSearchParams({ type, providerType });
|
|
140
125
|
|
|
141
126
|
if (credentialId) {
|
|
142
127
|
params.append('credentialId', credentialId);
|
|
@@ -144,9 +129,7 @@ export class SSSApiClient {
|
|
|
144
129
|
|
|
145
130
|
const response = await fetch(`${this.serverUrl}/keys/recovery?${params}`, {
|
|
146
131
|
method: 'GET',
|
|
147
|
-
headers: {
|
|
148
|
-
'Content-Type': 'application/json',
|
|
149
|
-
},
|
|
132
|
+
headers: { 'Content-Type': 'application/json', 'X-Auth-Token': token },
|
|
150
133
|
});
|
|
151
134
|
|
|
152
135
|
if (!response.ok) {
|
|
@@ -159,99 +142,107 @@ export class SSSApiClient {
|
|
|
159
142
|
return response.json();
|
|
160
143
|
}
|
|
161
144
|
|
|
162
|
-
async
|
|
163
|
-
const
|
|
164
|
-
const providerType = this.authProvider.getProviderType();
|
|
165
|
-
|
|
166
|
-
const response = await fetch(`${this.serverUrl}/keys/migrate`, {
|
|
145
|
+
async startIdentityRecovery(email: string): Promise<void> {
|
|
146
|
+
const response = await fetch(`${this.serverUrl}/keys/recovery-session/start`, {
|
|
167
147
|
method: 'POST',
|
|
168
|
-
headers,
|
|
169
|
-
body: JSON.stringify({
|
|
148
|
+
headers: { 'Content-Type': 'application/json' },
|
|
149
|
+
body: JSON.stringify({ email }),
|
|
170
150
|
});
|
|
171
151
|
|
|
172
152
|
if (!response.ok) {
|
|
173
|
-
throw new Error(`Failed to
|
|
153
|
+
throw new Error(`Failed to send recovery code: ${response.statusText}`);
|
|
174
154
|
}
|
|
175
155
|
}
|
|
176
156
|
|
|
177
|
-
async
|
|
178
|
-
const
|
|
179
|
-
const providerType = this.authProvider.getProviderType();
|
|
180
|
-
|
|
181
|
-
let email = overrideEmail;
|
|
182
|
-
|
|
183
|
-
if (!email) {
|
|
184
|
-
const user = await this.authProvider.getCurrentUser();
|
|
185
|
-
email = user?.email;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (!email) {
|
|
189
|
-
console.warn('Cannot send email backup share: no email address available');
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const response = await fetch(`${this.serverUrl}/keys/email-backup`, {
|
|
157
|
+
async verifyIdentityRecovery(email: string, code: string): Promise<IdentityRecoverySession> {
|
|
158
|
+
const response = await fetch(`${this.serverUrl}/keys/recovery-session/verify`, {
|
|
194
159
|
method: 'POST',
|
|
195
|
-
headers,
|
|
196
|
-
body: JSON.stringify({
|
|
197
|
-
providerType,
|
|
198
|
-
emailShare,
|
|
199
|
-
email,
|
|
200
|
-
}),
|
|
160
|
+
headers: { 'Content-Type': 'application/json' },
|
|
161
|
+
body: JSON.stringify({ email, code }),
|
|
201
162
|
});
|
|
202
163
|
|
|
203
164
|
if (!response.ok) {
|
|
204
|
-
|
|
205
|
-
console.warn(`Failed to send email backup share: ${response.statusText}`);
|
|
165
|
+
throw new Error(`Failed to verify recovery code: ${response.statusText}`);
|
|
206
166
|
}
|
|
207
|
-
}
|
|
208
167
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const providerType = this.authProvider.getProviderType();
|
|
168
|
+
return response.json();
|
|
169
|
+
}
|
|
212
170
|
|
|
213
|
-
|
|
171
|
+
async useIdentityRecoverySession(
|
|
172
|
+
recoverySessionToken: string,
|
|
173
|
+
type: StoreRecoveryShareInput['type'],
|
|
174
|
+
credentialId?: string
|
|
175
|
+
): Promise<RecoverySessionMaterial> {
|
|
176
|
+
const response = await fetch(`${this.serverUrl}/keys/recovery-session/recover`, {
|
|
214
177
|
method: 'POST',
|
|
215
178
|
headers: { 'Content-Type': 'application/json' },
|
|
216
|
-
body: JSON.stringify({
|
|
179
|
+
body: JSON.stringify({ recoverySessionToken, type, credentialId }),
|
|
217
180
|
});
|
|
218
181
|
|
|
219
182
|
if (!response.ok) {
|
|
220
|
-
|
|
221
|
-
throw new Error(data?.message || `Failed to add recovery email: ${response.statusText}`);
|
|
183
|
+
throw new Error(`Failed to retrieve recovery data: ${response.statusText}`);
|
|
222
184
|
}
|
|
223
|
-
}
|
|
224
185
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const providerType = this.authProvider.getProviderType();
|
|
186
|
+
return response.json();
|
|
187
|
+
}
|
|
228
188
|
|
|
229
|
-
|
|
189
|
+
async completeIdentityRecoveryRebind(input: {
|
|
190
|
+
recoverySessionToken: string;
|
|
191
|
+
newAuthToken: string;
|
|
192
|
+
providerType: AuthProviderType;
|
|
193
|
+
primaryDid: string;
|
|
194
|
+
authShare: ServerEncryptedShare;
|
|
195
|
+
didAuthVp: string;
|
|
196
|
+
}): Promise<{ shareVersion: number; recoveryMethodsRequireConfirmation: string[] }> {
|
|
197
|
+
const response = await fetch(`${this.serverUrl}/keys/recovery-session/rebind`, {
|
|
230
198
|
method: 'POST',
|
|
231
|
-
headers: {
|
|
232
|
-
|
|
199
|
+
headers: {
|
|
200
|
+
'Content-Type': 'application/json',
|
|
201
|
+
Authorization: `Bearer ${input.didAuthVp}`,
|
|
202
|
+
'X-Auth-Token': input.newAuthToken,
|
|
203
|
+
},
|
|
204
|
+
body: JSON.stringify({
|
|
205
|
+
recoverySessionToken: input.recoverySessionToken,
|
|
206
|
+
providerType: input.providerType,
|
|
207
|
+
primaryDid: input.primaryDid,
|
|
208
|
+
authShare: input.authShare,
|
|
209
|
+
}),
|
|
233
210
|
});
|
|
234
211
|
|
|
235
212
|
if (!response.ok) {
|
|
236
|
-
|
|
237
|
-
throw new Error(data?.message || `Failed to verify recovery email: ${response.statusText}`);
|
|
213
|
+
throw new Error(`Failed to bind the new sign-in: ${response.statusText}`);
|
|
238
214
|
}
|
|
239
215
|
|
|
240
216
|
return response.json();
|
|
241
217
|
}
|
|
242
218
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
219
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
220
|
+
async markMigrated(): Promise<void> {
|
|
221
|
+
throwDidChallengeRequired();
|
|
222
|
+
}
|
|
246
223
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
});
|
|
224
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
225
|
+
async activate(): Promise<void> {
|
|
226
|
+
throwDidChallengeRequired();
|
|
227
|
+
}
|
|
252
228
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
229
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
230
|
+
async sendEmailBackupShare(_emailShare: string, _overrideEmail?: string): Promise<void> {
|
|
231
|
+
throwDidChallengeRequired();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
235
|
+
async addRecoveryEmail(_email: string): Promise<void> {
|
|
236
|
+
throwDidChallengeRequired();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
240
|
+
async verifyRecoveryEmail(_code: string): Promise<{ maskedEmail: string }> {
|
|
241
|
+
return throwDidChallengeRequired();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/** @deprecated Requires DID-challenge authorization. Use `createSSSStrategy`. */
|
|
245
|
+
async deleteUserKey(): Promise<void> {
|
|
246
|
+
throwDidChallengeRequired();
|
|
256
247
|
}
|
|
257
248
|
}
|