@learncard/lca-api-service 1.3.1 → 1.4.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/.env.example +26 -2
- package/AGENTS.md +59 -44
- package/CHANGELOG.md +57 -0
- package/compose.env +5 -0
- package/dist/accesslayer/signing-authority/index.d.ts +8 -2
- package/dist/app.d.ts +297 -8
- package/dist/cache/challenges.d.ts +2 -0
- package/dist/cache/recoverySessions.d.ts +42 -0
- package/dist/config/environment.d.ts +152 -2
- package/dist/config/environment.test.d.ts +1 -0
- package/dist/helpers/learnCard.signingAuthority.test.d.ts +1 -0
- package/dist/helpers/rateLimit.helpers.d.ts +4 -0
- package/dist/helpers/seedEncryption.helpers.d.ts +23 -0
- package/dist/helpers/seedEncryption.helpers.test.d.ts +1 -0
- package/dist/index.js +2 -2
- package/dist/migrations/signingAuthoritySeeds.d.ts +13 -0
- package/dist/migrations/signingAuthoritySeedsCli.test.d.ts +1 -0
- package/dist/models/EscrowHold.d.ts +75 -0
- package/dist/models/SigningAuthority.d.ts +7 -1
- package/dist/models/UserKey.d.ts +316 -3
- package/dist/models/UserKey.escrowPin.test.d.ts +1 -0
- package/dist/models/index.d.ts +1 -0
- package/dist/routes/escrow.d.ts +150 -0
- package/dist/routes/firebase.test.d.ts +1 -0
- package/dist/routes/index.d.ts +6 -0
- package/dist/routes/keys.d.ts +181 -7
- package/dist/routes/qr-login.d.ts +1 -1
- package/dist/services/escrow-enclave/index.d.ts +9 -0
- package/dist/services/escrow-enclave/notifications.d.ts +9 -0
- package/dist/services/escrow-enclave/softwareEnclave.d.ts +17 -0
- package/dist/services/escrow-enclave/softwareEnclave.test.d.ts +1 -0
- package/dist/services/escrow-enclave/types.d.ts +67 -0
- package/dist/types/seed-encryption.d.ts +31 -0
- package/dist/types/seed-migration.d.ts +49 -0
- package/lambda.ts +8 -3
- package/package.json +17 -11
- package/scripts/check-seed-encryption-infrastructure.mjs +123 -0
- package/scripts/check-signing-authority-redis.mjs +93 -0
- package/scripts/migrate-signing-authority-seeds-local.sh +51 -0
- package/scripts/migrate-signing-authority-seeds.mjs +105 -0
- package/seedMigrationLambda.test.ts +145 -0
- package/seedMigrationLambda.ts +50 -0
- package/serverless-local.yml +3 -0
- package/serverless.escrow-env.cjs +20 -0
- package/serverless.yml +133 -0
- package/src/accesslayer/signing-authority/create.ts +11 -7
- package/src/app.ts +2 -0
- package/src/cache/challenges.ts +12 -2
- package/src/cache/in-memory-lru.ts +1 -0
- package/src/cache/recoverySessions.ts +252 -0
- package/src/config/environment.test.ts +116 -0
- package/src/config/environment.ts +97 -0
- package/src/docker-entry.ts +4 -0
- package/src/helpers/learnCard.helpers.test.ts +11 -1
- package/src/helpers/learnCard.helpers.ts +57 -45
- package/src/helpers/learnCard.signingAuthority.test.ts +194 -0
- package/src/helpers/rateLimit.helpers.ts +112 -0
- package/src/helpers/seedEncryption.helpers.test.ts +427 -0
- package/src/helpers/seedEncryption.helpers.ts +372 -0
- package/src/helpers/signingAuthority.helpers.ts +4 -1
- package/src/migrations/signingAuthoritySeeds.ts +430 -0
- package/src/migrations/signingAuthoritySeedsCli.test.ts +70 -0
- package/src/models/EscrowHold.ts +225 -0
- package/src/models/SigningAuthority.ts +14 -3
- package/src/models/UserKey.escrowPin.test.ts +49 -0
- package/src/models/UserKey.ts +1024 -40
- package/src/models/index.ts +1 -0
- package/src/models/pruneOrphanedRecoveryMethods.ts +3 -1
- package/src/routes/credentials.ts +8 -0
- package/src/routes/escrow.ts +826 -0
- package/src/routes/firebase.test.ts +94 -0
- package/src/routes/firebase.ts +99 -56
- package/src/routes/index.ts +98 -5
- package/src/routes/keys.ts +1017 -174
- package/src/routes/qr-login.ts +51 -53
- package/src/services/delivery/adapters/postmark.adapter.ts +5 -0
- package/src/services/escrow-enclave/README.md +51 -0
- package/src/services/escrow-enclave/index.ts +36 -0
- package/src/services/escrow-enclave/notifications.ts +14 -0
- package/src/services/escrow-enclave/softwareEnclave.test.ts +249 -0
- package/src/services/escrow-enclave/softwareEnclave.ts +168 -0
- package/src/services/escrow-enclave/types.ts +76 -0
- package/src/types/seed-encryption.ts +51 -0
- package/src/types/seed-migration.ts +61 -0
- package/test/credentials.spec.ts +2 -0
- package/test/keys-escrow.spec.ts +1389 -0
- package/test/keys-identity-rebind.spec.ts +456 -0
- package/test/keys-lifecycle-invariants.spec.ts +785 -0
- package/test/keys-recovery-confirmation.spec.ts +586 -0
- package/test/keys-recovery-rate-limit.spec.ts +123 -0
- package/test/keys-secret-leakage.spec.ts +265 -0
- package/test/models/escrow.spec.ts +210 -0
- package/test/signing-authority-http.spec.ts +161 -0
- package/test/signing-authority-seed-migration.spec.ts +414 -0
- package/test/signing-authority.spec.ts +40 -0
- package/vitest.config.ts +1 -1
- package/vitest.integration.config.ts +5 -0
- package/dist/cache/signing-authority.d.ts +0 -6
- package/src/cache/signing-authority.ts +0 -37
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
export declare const escrowRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
2
|
+
ctx: import("@routes").Context;
|
|
3
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
4
|
+
errorShape: import("@trpc/server").TRPCDefaultErrorShape;
|
|
5
|
+
transformer: false;
|
|
6
|
+
}, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
|
|
7
|
+
getAttestation: import("@trpc/server").TRPCQueryProcedure<{
|
|
8
|
+
input: Record<string, never>;
|
|
9
|
+
output: {
|
|
10
|
+
attestation: {
|
|
11
|
+
mode: "software" | "nitro";
|
|
12
|
+
keyId: string;
|
|
13
|
+
publicKey: string;
|
|
14
|
+
measurements: {
|
|
15
|
+
imageSha384?: string | undefined;
|
|
16
|
+
pcr0?: string | undefined;
|
|
17
|
+
pcr1?: string | undefined;
|
|
18
|
+
pcr2?: string | undefined;
|
|
19
|
+
};
|
|
20
|
+
document: string;
|
|
21
|
+
issuedAt: string;
|
|
22
|
+
};
|
|
23
|
+
holdDurationMs: number;
|
|
24
|
+
};
|
|
25
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
26
|
+
}>;
|
|
27
|
+
enroll: import("@trpc/server").TRPCMutationProcedure<{
|
|
28
|
+
input: {
|
|
29
|
+
authToken: string;
|
|
30
|
+
providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
|
|
31
|
+
envelope: {
|
|
32
|
+
version: 1;
|
|
33
|
+
algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
|
|
34
|
+
keyId: string;
|
|
35
|
+
ephemeralPublicKey: string;
|
|
36
|
+
salt: string;
|
|
37
|
+
iv: string;
|
|
38
|
+
ciphertext: string;
|
|
39
|
+
};
|
|
40
|
+
shareVersion: number;
|
|
41
|
+
enclaveKeyId: string;
|
|
42
|
+
pinSalt?: string | undefined;
|
|
43
|
+
clearPin?: boolean | undefined;
|
|
44
|
+
};
|
|
45
|
+
output: {
|
|
46
|
+
success: true;
|
|
47
|
+
shareVersion: number;
|
|
48
|
+
};
|
|
49
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
50
|
+
}>;
|
|
51
|
+
remove: import("@trpc/server").TRPCMutationProcedure<{
|
|
52
|
+
input: {
|
|
53
|
+
authToken: string;
|
|
54
|
+
providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
|
|
55
|
+
optOut: boolean;
|
|
56
|
+
};
|
|
57
|
+
output: {
|
|
58
|
+
success: true;
|
|
59
|
+
};
|
|
60
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
61
|
+
}>;
|
|
62
|
+
optIn: import("@trpc/server").TRPCMutationProcedure<{
|
|
63
|
+
input: {
|
|
64
|
+
authToken: string;
|
|
65
|
+
providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
|
|
66
|
+
};
|
|
67
|
+
output: {
|
|
68
|
+
success: true;
|
|
69
|
+
};
|
|
70
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
71
|
+
}>;
|
|
72
|
+
startRecovery: import("@trpc/server").TRPCMutationProcedure<{
|
|
73
|
+
input: {
|
|
74
|
+
clientEphemeralPublicKey: string;
|
|
75
|
+
releasePolicy?: "pin" | "hold" | undefined;
|
|
76
|
+
restart?: boolean | undefined;
|
|
77
|
+
authToken?: string | undefined;
|
|
78
|
+
providerType?: "firebase" | "supertokens" | "keycloak" | "oidc" | undefined;
|
|
79
|
+
recoverySessionToken?: string | undefined;
|
|
80
|
+
};
|
|
81
|
+
output: {
|
|
82
|
+
requestedAt: string;
|
|
83
|
+
releaseAfter: string;
|
|
84
|
+
releasePolicy: "pin" | "hold";
|
|
85
|
+
holdId: string;
|
|
86
|
+
status: "pending";
|
|
87
|
+
resumeToken: string | null;
|
|
88
|
+
pinSalt?: string | undefined;
|
|
89
|
+
};
|
|
90
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
91
|
+
}>;
|
|
92
|
+
getStatus: import("@trpc/server").TRPCQueryProcedure<{
|
|
93
|
+
input: {
|
|
94
|
+
holdId?: string | undefined;
|
|
95
|
+
resumeToken?: string | undefined;
|
|
96
|
+
authToken?: string | undefined;
|
|
97
|
+
providerType?: "firebase" | "supertokens" | "keycloak" | "oidc" | undefined;
|
|
98
|
+
};
|
|
99
|
+
output: {
|
|
100
|
+
hold: {
|
|
101
|
+
holdId: string;
|
|
102
|
+
status: "pending" | "cancelled" | "completed" | "expired";
|
|
103
|
+
requestedAt: string;
|
|
104
|
+
releaseAfter: string;
|
|
105
|
+
releasePolicy: "pin" | "hold";
|
|
106
|
+
cancelledAt?: string | undefined;
|
|
107
|
+
completedAt?: string | undefined;
|
|
108
|
+
} | null;
|
|
109
|
+
};
|
|
110
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
111
|
+
}>;
|
|
112
|
+
cancelRecovery: import("@trpc/server").TRPCMutationProcedure<{
|
|
113
|
+
input: {
|
|
114
|
+
authToken: string;
|
|
115
|
+
providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
|
|
116
|
+
};
|
|
117
|
+
output: {
|
|
118
|
+
success: true;
|
|
119
|
+
cancelled: boolean;
|
|
120
|
+
};
|
|
121
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
122
|
+
}>;
|
|
123
|
+
completeRecovery: import("@trpc/server").TRPCMutationProcedure<{
|
|
124
|
+
input: {
|
|
125
|
+
holdId: string;
|
|
126
|
+
resumeToken: string;
|
|
127
|
+
pinProof?: string | undefined;
|
|
128
|
+
};
|
|
129
|
+
output: {
|
|
130
|
+
sealedShare: {
|
|
131
|
+
version: 1;
|
|
132
|
+
algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
|
|
133
|
+
keyId: string;
|
|
134
|
+
ephemeralPublicKey: string;
|
|
135
|
+
salt: string;
|
|
136
|
+
iv: string;
|
|
137
|
+
ciphertext: string;
|
|
138
|
+
};
|
|
139
|
+
authShare: {
|
|
140
|
+
encryptedData: string;
|
|
141
|
+
encryptedDek: string;
|
|
142
|
+
iv: string;
|
|
143
|
+
};
|
|
144
|
+
primaryDid: string;
|
|
145
|
+
shareVersion: number;
|
|
146
|
+
rebindSessionToken: string;
|
|
147
|
+
};
|
|
148
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
149
|
+
}>;
|
|
150
|
+
}>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/routes/index.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export type Context = {
|
|
|
24
24
|
tenant: ResolvedTenant;
|
|
25
25
|
debug?: boolean;
|
|
26
26
|
clientIp?: string;
|
|
27
|
+
providerToken?: string;
|
|
27
28
|
};
|
|
28
29
|
export declare const t: import("@trpc/server").TRPCRootObject<Context, OpenApiMeta, import("@trpc/server").TRPCRuntimeConfigOptions<Context, OpenApiMeta>, {
|
|
29
30
|
ctx: Context;
|
|
@@ -32,6 +33,7 @@ export declare const t: import("@trpc/server").TRPCRootObject<Context, OpenApiMe
|
|
|
32
33
|
transformer: false;
|
|
33
34
|
}>;
|
|
34
35
|
export declare const createContext: (options: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2> | CreateFastifyContextOptions | NodeHTTPCreateContextFnOptions<IncomingMessage, ServerResponse>) => Promise<Context>;
|
|
36
|
+
export declare const redactSecretFields: (value: unknown, depth?: number, seen?: Set<object>) => unknown;
|
|
35
37
|
export declare const openRoute: import("@trpc/server").TRPCProcedureBuilder<Context, OpenApiMeta, {
|
|
36
38
|
debug: boolean | undefined;
|
|
37
39
|
user: {
|
|
@@ -42,6 +44,7 @@ export declare const openRoute: import("@trpc/server").TRPCProcedureBuilder<Cont
|
|
|
42
44
|
domain: string;
|
|
43
45
|
tenant: ResolvedTenant;
|
|
44
46
|
clientIp: string | undefined;
|
|
47
|
+
providerToken: string | undefined;
|
|
45
48
|
}, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
|
|
46
49
|
export declare const didRoute: import("@trpc/server").TRPCProcedureBuilder<Context, OpenApiMeta, {
|
|
47
50
|
debug: boolean | undefined;
|
|
@@ -53,6 +56,7 @@ export declare const didRoute: import("@trpc/server").TRPCProcedureBuilder<Conte
|
|
|
53
56
|
domain: string;
|
|
54
57
|
tenant: ResolvedTenant;
|
|
55
58
|
clientIp: string | undefined;
|
|
59
|
+
providerToken: string | undefined;
|
|
56
60
|
}, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
|
|
57
61
|
export declare const authorizedDidRoute: import("@trpc/server").TRPCProcedureBuilder<Context, OpenApiMeta, {
|
|
58
62
|
debug: boolean | undefined;
|
|
@@ -64,6 +68,7 @@ export declare const authorizedDidRoute: import("@trpc/server").TRPCProcedureBui
|
|
|
64
68
|
domain: string;
|
|
65
69
|
tenant: ResolvedTenant;
|
|
66
70
|
clientIp: string | undefined;
|
|
71
|
+
providerToken: string | undefined;
|
|
67
72
|
}, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
|
|
68
73
|
export declare const didAndChallengeRoute: import("@trpc/server").TRPCProcedureBuilder<Context, OpenApiMeta, {
|
|
69
74
|
debug: boolean | undefined;
|
|
@@ -75,4 +80,5 @@ export declare const didAndChallengeRoute: import("@trpc/server").TRPCProcedureB
|
|
|
75
80
|
domain: string;
|
|
76
81
|
tenant: ResolvedTenant;
|
|
77
82
|
clientIp: string | undefined;
|
|
83
|
+
providerToken: string | undefined;
|
|
78
84
|
}, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
|
package/dist/routes/keys.d.ts
CHANGED
|
@@ -1,9 +1,126 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { AuthProviderType } from '@helpers/auth.helpers';
|
|
3
|
+
import { type ContactMethod, type AuthProviderMapping, type MongoUserKeyType } from '@models';
|
|
4
|
+
export declare const AuthProviderTypeValidator: z.ZodEnum<{
|
|
5
|
+
firebase: "firebase";
|
|
6
|
+
supertokens: "supertokens";
|
|
7
|
+
keycloak: "keycloak";
|
|
8
|
+
oidc: "oidc";
|
|
9
|
+
}>;
|
|
10
|
+
export declare const AuthInputValidator: z.ZodObject<{
|
|
11
|
+
authToken: z.ZodString;
|
|
12
|
+
providerType: z.ZodEnum<{
|
|
13
|
+
firebase: "firebase";
|
|
14
|
+
supertokens: "supertokens";
|
|
15
|
+
keycloak: "keycloak";
|
|
16
|
+
oidc: "oidc";
|
|
17
|
+
}>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
export declare const RecoverySessionTokenValidator: z.ZodString;
|
|
20
|
+
export declare function verifyAndGetContactMethod(input: {
|
|
21
|
+
authToken: string;
|
|
22
|
+
providerType: AuthProviderType;
|
|
23
|
+
}): Promise<{
|
|
24
|
+
user: {
|
|
25
|
+
id: string;
|
|
26
|
+
email?: string;
|
|
27
|
+
phone?: string;
|
|
28
|
+
providerType: string;
|
|
29
|
+
};
|
|
30
|
+
contactMethod: ContactMethod;
|
|
31
|
+
authProvider: AuthProviderMapping;
|
|
32
|
+
}>;
|
|
33
|
+
export declare const requireUserKey: (authProvider: AuthProviderMapping, message?: string) => Promise<MongoUserKeyType>;
|
|
34
|
+
export declare const assertDidOwner: (userKey: MongoUserKeyType, authenticatedDid: string) => void;
|
|
35
|
+
export declare const requireRecoverySession: (token: string, scope: "recover" | "rebind") => Promise<AuthProviderMapping>;
|
|
1
36
|
export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
2
37
|
ctx: import("@routes").Context;
|
|
3
38
|
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
4
39
|
errorShape: import("@trpc/server").TRPCDefaultErrorShape;
|
|
5
40
|
transformer: false;
|
|
6
41
|
}, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
|
|
42
|
+
startRecoverySession: import("@trpc/server").TRPCMutationProcedure<{
|
|
43
|
+
input: {
|
|
44
|
+
email: string;
|
|
45
|
+
};
|
|
46
|
+
output: {
|
|
47
|
+
success: true;
|
|
48
|
+
};
|
|
49
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
50
|
+
}>;
|
|
51
|
+
verifyRecoverySession: import("@trpc/server").TRPCMutationProcedure<{
|
|
52
|
+
input: {
|
|
53
|
+
email: string;
|
|
54
|
+
code: string;
|
|
55
|
+
};
|
|
56
|
+
output: {
|
|
57
|
+
recoverySessionToken: string;
|
|
58
|
+
recoveryMethods: {
|
|
59
|
+
type: "email" | "passkey" | "backup" | "phrase" | "escrow";
|
|
60
|
+
createdAt: string;
|
|
61
|
+
credentialId?: string | undefined;
|
|
62
|
+
shareVersion?: number | undefined;
|
|
63
|
+
}[];
|
|
64
|
+
escrowPin: {
|
|
65
|
+
state: "enabled" | "none" | "locked" | "stale";
|
|
66
|
+
enabled: boolean;
|
|
67
|
+
attemptsRemaining: number;
|
|
68
|
+
salt?: string | undefined;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
72
|
+
}>;
|
|
73
|
+
useRecoverySession: import("@trpc/server").TRPCMutationProcedure<{
|
|
74
|
+
input: {
|
|
75
|
+
recoverySessionToken: string;
|
|
76
|
+
type: "email" | "passkey" | "backup" | "phrase" | "escrow";
|
|
77
|
+
credentialId?: string | undefined;
|
|
78
|
+
};
|
|
79
|
+
output: {
|
|
80
|
+
authShare: {
|
|
81
|
+
encryptedData: string;
|
|
82
|
+
encryptedDek: string;
|
|
83
|
+
iv: string;
|
|
84
|
+
};
|
|
85
|
+
primaryDid: string;
|
|
86
|
+
shareVersion: number;
|
|
87
|
+
rebindSessionToken: string;
|
|
88
|
+
encryptedShare?: {
|
|
89
|
+
encryptedData: string;
|
|
90
|
+
iv: string;
|
|
91
|
+
salt?: string | undefined;
|
|
92
|
+
} | undefined;
|
|
93
|
+
};
|
|
94
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
95
|
+
}>;
|
|
96
|
+
completeRecoveryRebind: import("@trpc/server").TRPCMutationProcedure<{
|
|
97
|
+
input: {
|
|
98
|
+
recoverySessionToken: string;
|
|
99
|
+
providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
|
|
100
|
+
primaryDid: string;
|
|
101
|
+
authShare: {
|
|
102
|
+
encryptedData: string;
|
|
103
|
+
encryptedDek: string;
|
|
104
|
+
iv: string;
|
|
105
|
+
};
|
|
106
|
+
newAuthToken?: string | undefined;
|
|
107
|
+
};
|
|
108
|
+
output: {
|
|
109
|
+
success: true;
|
|
110
|
+
shareVersion: number;
|
|
111
|
+
recoveryMethodsRequireConfirmation: ("email" | "passkey" | "backup" | "phrase" | "escrow")[];
|
|
112
|
+
};
|
|
113
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
114
|
+
}>;
|
|
115
|
+
issueDidChallenge: import("@trpc/server").TRPCMutationProcedure<{
|
|
116
|
+
input: {
|
|
117
|
+
did: string;
|
|
118
|
+
};
|
|
119
|
+
output: {
|
|
120
|
+
challenge: string;
|
|
121
|
+
};
|
|
122
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
123
|
+
}>;
|
|
7
124
|
getAuthShare: import("@trpc/server").TRPCMutationProcedure<{
|
|
8
125
|
input: {
|
|
9
126
|
authToken: string;
|
|
@@ -19,14 +136,23 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
19
136
|
primaryDid: string | null;
|
|
20
137
|
securityLevel: "basic" | "enhanced" | "advanced";
|
|
21
138
|
recoveryMethods: {
|
|
22
|
-
type: "email" | "passkey" | "backup" | "phrase";
|
|
139
|
+
type: "email" | "passkey" | "backup" | "phrase" | "escrow";
|
|
23
140
|
createdAt: string;
|
|
24
141
|
credentialId?: string | undefined;
|
|
25
142
|
shareVersion?: number | undefined;
|
|
143
|
+
confirmedAt?: string | undefined;
|
|
26
144
|
}[];
|
|
27
145
|
keyProvider: "web3auth" | "sss";
|
|
28
146
|
shareVersion: number;
|
|
29
147
|
maskedRecoveryEmail: string | null;
|
|
148
|
+
escrowOptedOut: boolean;
|
|
149
|
+
escrowPin: {
|
|
150
|
+
state: "enabled" | "none" | "locked" | "stale";
|
|
151
|
+
enabled: boolean;
|
|
152
|
+
attemptsRemaining: number;
|
|
153
|
+
salt?: string | undefined;
|
|
154
|
+
};
|
|
155
|
+
sssActivationState: "provisional" | "active";
|
|
30
156
|
} | null;
|
|
31
157
|
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
32
158
|
}>;
|
|
@@ -42,10 +168,13 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
42
168
|
primaryDid: string;
|
|
43
169
|
securityLevel?: "basic" | "enhanced" | "advanced" | undefined;
|
|
44
170
|
keyProvider?: "web3auth" | "sss" | undefined;
|
|
171
|
+
sssActivationState?: "provisional" | undefined;
|
|
172
|
+
expectedShareVersion?: number | undefined;
|
|
45
173
|
};
|
|
46
174
|
output: {
|
|
47
175
|
success: boolean;
|
|
48
176
|
shareVersion: number;
|
|
177
|
+
expectedShareVersionChecked: boolean;
|
|
49
178
|
};
|
|
50
179
|
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
51
180
|
}>;
|
|
@@ -53,7 +182,7 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
53
182
|
input: {
|
|
54
183
|
authToken: string;
|
|
55
184
|
providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
|
|
56
|
-
type: "email" | "passkey" | "backup" | "phrase";
|
|
185
|
+
type: "email" | "passkey" | "backup" | "phrase" | "escrow";
|
|
57
186
|
encryptedShare?: {
|
|
58
187
|
encryptedData: string;
|
|
59
188
|
iv: string;
|
|
@@ -67,11 +196,36 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
67
196
|
};
|
|
68
197
|
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
69
198
|
}>;
|
|
70
|
-
|
|
199
|
+
confirmRecoveryMethod: import("@trpc/server").TRPCMutationProcedure<{
|
|
71
200
|
input: {
|
|
72
201
|
authToken: string;
|
|
73
202
|
providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
|
|
74
|
-
type: "email" | "passkey" | "backup" | "phrase";
|
|
203
|
+
type: "email" | "passkey" | "backup" | "phrase" | "escrow";
|
|
204
|
+
credentialId?: string | undefined;
|
|
205
|
+
code?: string | undefined;
|
|
206
|
+
};
|
|
207
|
+
output: {
|
|
208
|
+
success: boolean;
|
|
209
|
+
};
|
|
210
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
211
|
+
}>;
|
|
212
|
+
deleteRecoveryMethod: import("@trpc/server").TRPCMutationProcedure<{
|
|
213
|
+
input: {
|
|
214
|
+
authToken: string;
|
|
215
|
+
providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
|
|
216
|
+
type: "email" | "passkey" | "backup" | "phrase" | "escrow";
|
|
217
|
+
credentialId?: string | undefined;
|
|
218
|
+
};
|
|
219
|
+
output: {
|
|
220
|
+
success: boolean;
|
|
221
|
+
};
|
|
222
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
223
|
+
}>;
|
|
224
|
+
getRecoveryShare: import("@trpc/server").TRPCQueryProcedure<{
|
|
225
|
+
input: {
|
|
226
|
+
providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
|
|
227
|
+
type: "email" | "passkey" | "backup" | "phrase" | "escrow";
|
|
228
|
+
authToken?: string | undefined;
|
|
75
229
|
credentialId?: string | undefined;
|
|
76
230
|
};
|
|
77
231
|
output: {
|
|
@@ -94,6 +248,16 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
94
248
|
};
|
|
95
249
|
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
96
250
|
}>;
|
|
251
|
+
activate: import("@trpc/server").TRPCMutationProcedure<{
|
|
252
|
+
input: {
|
|
253
|
+
authToken: string;
|
|
254
|
+
providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
|
|
255
|
+
};
|
|
256
|
+
output: {
|
|
257
|
+
success: boolean;
|
|
258
|
+
};
|
|
259
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
260
|
+
}>;
|
|
97
261
|
addRecoveryEmail: import("@trpc/server").TRPCMutationProcedure<{
|
|
98
262
|
input: {
|
|
99
263
|
authToken: string;
|
|
@@ -131,9 +295,18 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
131
295
|
input: {
|
|
132
296
|
authToken: string;
|
|
133
297
|
providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
298
|
+
relayPayload: {
|
|
299
|
+
version: 1;
|
|
300
|
+
algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
|
|
301
|
+
keyId: string;
|
|
302
|
+
ephemeralPublicKey: string;
|
|
303
|
+
salt: string;
|
|
304
|
+
iv: string;
|
|
305
|
+
ciphertext: string;
|
|
306
|
+
};
|
|
307
|
+
confirmationCode: string;
|
|
308
|
+
email: string;
|
|
309
|
+
shareVersion?: number | undefined;
|
|
137
310
|
};
|
|
138
311
|
output: {
|
|
139
312
|
success: boolean;
|
|
@@ -159,6 +332,7 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
159
332
|
input: {
|
|
160
333
|
authToken: string;
|
|
161
334
|
providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
|
|
335
|
+
challenge: string;
|
|
162
336
|
};
|
|
163
337
|
output: {
|
|
164
338
|
success: boolean;
|
|
@@ -22,7 +22,7 @@ export declare const qrLoginRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
22
22
|
output: {
|
|
23
23
|
sessionId: string;
|
|
24
24
|
publicKey: string;
|
|
25
|
-
status: "
|
|
25
|
+
status: "pending" | "approved";
|
|
26
26
|
encryptedPayload?: string | undefined;
|
|
27
27
|
approverDid?: string | undefined;
|
|
28
28
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type EscrowEnclave } from './types';
|
|
2
|
+
export * from './types';
|
|
3
|
+
export * from './softwareEnclave';
|
|
4
|
+
export * from './notifications';
|
|
5
|
+
export declare const isEscrowEnabled: () => boolean;
|
|
6
|
+
export declare const getEscrowHoldDurationMs: () => number;
|
|
7
|
+
export declare const getEscrowHoldRestartMinAgeMs: () => number;
|
|
8
|
+
export declare const getEscrowEnclave: () => EscrowEnclave;
|
|
9
|
+
export declare const __setEscrowEnclaveForTests: (replacement: EscrowEnclave | undefined) => void;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { EscrowHold } from '../../models/EscrowHold';
|
|
2
|
+
import type { MongoUserKeyType } from '../../models/UserKey';
|
|
3
|
+
export interface EscrowHoldEvent {
|
|
4
|
+
kind: 'started' | 'cancelled' | 'completed';
|
|
5
|
+
hold: EscrowHold;
|
|
6
|
+
userKey: MongoUserKeyType;
|
|
7
|
+
reason?: 'superseded';
|
|
8
|
+
}
|
|
9
|
+
export declare const notifyEscrowHoldEvent: (event: EscrowHoldEvent) => Promise<void>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type EscrowEnclave, type EnclaveAttestation, type VerifyEscrowBlobInput, type VerifyEscrowBlobResult, type ReleaseRequest, type ReleaseResult, type CarryPinVerifierInput } from './types';
|
|
2
|
+
export interface SoftwareEnclaveConfig {
|
|
3
|
+
privateKeys: Record<string, string>;
|
|
4
|
+
activeKeyId: string;
|
|
5
|
+
}
|
|
6
|
+
export declare class SoftwareEnclave implements EscrowEnclave {
|
|
7
|
+
private readonly privateKeys;
|
|
8
|
+
private readonly activeKeyId;
|
|
9
|
+
constructor(config: SoftwareEnclaveConfig);
|
|
10
|
+
getAttestation(): Promise<EnclaveAttestation>;
|
|
11
|
+
private decrypt;
|
|
12
|
+
verifyEscrowBlob(input: VerifyEscrowBlobInput): Promise<VerifyEscrowBlobResult>;
|
|
13
|
+
carryPinVerifier(input: CarryPinVerifierInput): Promise<{
|
|
14
|
+
envelope: import("@learncard/sss-key-manager").EscrowEnvelope;
|
|
15
|
+
}>;
|
|
16
|
+
releaseEscrow(input: ReleaseRequest): Promise<ReleaseResult>;
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { EscrowEnvelope } from '@learncard/sss-key-manager';
|
|
2
|
+
import type { EscrowHold } from '../../models/EscrowHold';
|
|
3
|
+
export interface EnclaveAttestation {
|
|
4
|
+
mode: 'software' | 'nitro';
|
|
5
|
+
keyId: string;
|
|
6
|
+
publicKey: string;
|
|
7
|
+
measurements: {
|
|
8
|
+
imageSha384?: string;
|
|
9
|
+
pcr0?: string;
|
|
10
|
+
pcr1?: string;
|
|
11
|
+
pcr2?: string;
|
|
12
|
+
};
|
|
13
|
+
document: string;
|
|
14
|
+
issuedAt: string;
|
|
15
|
+
}
|
|
16
|
+
export type EscrowHoldForEnclave = Pick<EscrowHold, '_id' | 'status' | 'releaseAfter' | 'releasePolicy' | 'primaryDid' | 'shareVersion' | 'clientEphemeralPublicKey'>;
|
|
17
|
+
export interface VerifyEscrowBlobInput {
|
|
18
|
+
envelope: EscrowEnvelope;
|
|
19
|
+
expectedDid: string;
|
|
20
|
+
expectedShareVersion: number;
|
|
21
|
+
}
|
|
22
|
+
export type VerifyEscrowBlobResult = {
|
|
23
|
+
ok: true;
|
|
24
|
+
hasPin: boolean;
|
|
25
|
+
} | {
|
|
26
|
+
ok: false;
|
|
27
|
+
hasPin: boolean;
|
|
28
|
+
reason: string;
|
|
29
|
+
};
|
|
30
|
+
export interface ReleaseRequest {
|
|
31
|
+
envelope: EscrowEnvelope;
|
|
32
|
+
hold: EscrowHoldForEnclave;
|
|
33
|
+
clientEphemeralPublicKey: string;
|
|
34
|
+
expectedDid: string;
|
|
35
|
+
now?: Date;
|
|
36
|
+
pinProof?: string;
|
|
37
|
+
}
|
|
38
|
+
export interface ReleaseResult {
|
|
39
|
+
sealed: EscrowEnvelope;
|
|
40
|
+
}
|
|
41
|
+
export interface CarryPinVerifierInput {
|
|
42
|
+
sourceEnvelope: EscrowEnvelope;
|
|
43
|
+
targetEnvelope: EscrowEnvelope;
|
|
44
|
+
expectedDid: string;
|
|
45
|
+
sourceShareVersion: number;
|
|
46
|
+
targetShareVersion: number;
|
|
47
|
+
}
|
|
48
|
+
export interface EscrowEnclave {
|
|
49
|
+
getAttestation(): Promise<EnclaveAttestation>;
|
|
50
|
+
verifyEscrowBlob(input: VerifyEscrowBlobInput): Promise<VerifyEscrowBlobResult>;
|
|
51
|
+
carryPinVerifier(input: CarryPinVerifierInput): Promise<{
|
|
52
|
+
envelope: EscrowEnvelope;
|
|
53
|
+
}>;
|
|
54
|
+
releaseEscrow(input: ReleaseRequest): Promise<ReleaseResult>;
|
|
55
|
+
}
|
|
56
|
+
export declare class EscrowPolicyError extends Error {
|
|
57
|
+
constructor();
|
|
58
|
+
}
|
|
59
|
+
export declare class EscrowPinMismatchError extends Error {
|
|
60
|
+
constructor();
|
|
61
|
+
}
|
|
62
|
+
export declare class EscrowBlobError extends Error {
|
|
63
|
+
constructor();
|
|
64
|
+
}
|
|
65
|
+
export declare class EscrowUnavailableError extends Error {
|
|
66
|
+
constructor();
|
|
67
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface SigningAuthoritySeedIdentity {
|
|
2
|
+
_id: string;
|
|
3
|
+
ownerDid: string;
|
|
4
|
+
name: string;
|
|
5
|
+
did?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface EncryptedSigningAuthoritySeed {
|
|
8
|
+
encryptedSeed: string;
|
|
9
|
+
encryptedDek: string;
|
|
10
|
+
keyVersion: 'kms-v1' | 'local-v1';
|
|
11
|
+
}
|
|
12
|
+
export interface SigningAuthoritySeedRecord {
|
|
13
|
+
_id?: unknown;
|
|
14
|
+
ownerDid: string;
|
|
15
|
+
name: string;
|
|
16
|
+
did?: string;
|
|
17
|
+
seed?: string;
|
|
18
|
+
encryptedSeed?: unknown;
|
|
19
|
+
encryptedDek?: unknown;
|
|
20
|
+
keyVersion?: unknown;
|
|
21
|
+
}
|
|
22
|
+
export interface SeedEncryptionConfig {
|
|
23
|
+
kmsKeyArn?: string;
|
|
24
|
+
localKek?: string;
|
|
25
|
+
allowLocal: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface SeedEncryption {
|
|
28
|
+
encrypt: (seed: string, identity: SigningAuthoritySeedIdentity) => Promise<EncryptedSigningAuthoritySeed>;
|
|
29
|
+
decrypt: (envelope: EncryptedSigningAuthoritySeed, identity: SigningAuthoritySeedIdentity) => Promise<string>;
|
|
30
|
+
}
|
|
31
|
+
export type SeedEncryptionFailure = 'invalid_record' | 'invalid_envelope' | 'configuration' | 'legacy_disabled' | 'authentication_failed' | 'kms_access_denied' | 'kms_unavailable' | 'kms_failed';
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Document, ObjectId } from 'mongodb';
|
|
2
|
+
import type { EncryptedSigningAuthoritySeed } from './seed-encryption';
|
|
3
|
+
export type SeedMigrationPhase = 'dry-run' | 'prepare' | 'verify' | 'purge';
|
|
4
|
+
export interface SeedMigrationRequest {
|
|
5
|
+
phase: SeedMigrationPhase;
|
|
6
|
+
batchSize?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface SeedMigrationCounts {
|
|
9
|
+
total: number;
|
|
10
|
+
encrypted: number;
|
|
11
|
+
legacyOnly: number;
|
|
12
|
+
malformed: number;
|
|
13
|
+
plaintextRemaining: number;
|
|
14
|
+
}
|
|
15
|
+
export interface SeedMigrationResult {
|
|
16
|
+
phase: SeedMigrationPhase;
|
|
17
|
+
done: boolean;
|
|
18
|
+
processed: number;
|
|
19
|
+
counts: SeedMigrationCounts;
|
|
20
|
+
countsReconciled: boolean;
|
|
21
|
+
rescanRequired?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface MigrationSigningAuthority extends Document {
|
|
24
|
+
_id: string | ObjectId;
|
|
25
|
+
ownerDid: string;
|
|
26
|
+
name: string;
|
|
27
|
+
did?: string;
|
|
28
|
+
seed?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface SeedMigrationState {
|
|
31
|
+
_id: string;
|
|
32
|
+
leaseOwner?: string;
|
|
33
|
+
leaseExpiresAt?: Date;
|
|
34
|
+
phase?: SeedMigrationPhase;
|
|
35
|
+
epoch?: string;
|
|
36
|
+
verifiedEpoch?: string;
|
|
37
|
+
status?: 'running' | 'complete' | 'failed';
|
|
38
|
+
processed?: number;
|
|
39
|
+
counts?: SeedMigrationCounts;
|
|
40
|
+
updatedAt?: Date;
|
|
41
|
+
cursor?: string | ObjectId;
|
|
42
|
+
}
|
|
43
|
+
export interface SeedMigrationReceipt extends EncryptedSigningAuthoritySeed {
|
|
44
|
+
_id: string | ObjectId;
|
|
45
|
+
epoch: string;
|
|
46
|
+
ownerDid: string;
|
|
47
|
+
name: string;
|
|
48
|
+
did: string | null;
|
|
49
|
+
}
|