@originals/auth 1.8.2 → 1.8.3
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/{src/client/index.ts → dist/client/index.d.ts} +2 -15
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +23 -0
- package/dist/client/index.js.map +1 -0
- package/{src/client/server-auth.ts → dist/client/server-auth.d.ts} +5 -45
- package/dist/client/server-auth.d.ts.map +1 -0
- package/dist/client/server-auth.js +77 -0
- package/dist/client/server-auth.js.map +1 -0
- package/dist/client/turnkey-client.d.ts +59 -0
- package/dist/client/turnkey-client.d.ts.map +1 -0
- package/dist/client/turnkey-client.js +279 -0
- package/dist/client/turnkey-client.js.map +1 -0
- package/dist/client/turnkey-did-signer.d.ts +58 -0
- package/dist/client/turnkey-did-signer.d.ts.map +1 -0
- package/dist/client/turnkey-did-signer.js +131 -0
- package/dist/client/turnkey-did-signer.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/{src/index.ts → dist/index.js} +1 -6
- package/dist/index.js.map +1 -0
- package/dist/server/email-auth.d.ts +42 -0
- package/dist/server/email-auth.d.ts.map +1 -0
- package/dist/server/email-auth.js +187 -0
- package/dist/server/email-auth.js.map +1 -0
- package/dist/server/index.d.ts +22 -0
- package/dist/server/index.d.ts.map +1 -0
- package/{src/server/index.ts → dist/server/index.js} +3 -23
- package/dist/server/index.js.map +1 -0
- package/dist/server/jwt.d.ts +49 -0
- package/dist/server/jwt.d.ts.map +1 -0
- package/dist/server/jwt.js +113 -0
- package/dist/server/jwt.js.map +1 -0
- package/dist/server/middleware.d.ts +39 -0
- package/dist/server/middleware.d.ts.map +1 -0
- package/dist/server/middleware.js +112 -0
- package/dist/server/middleware.js.map +1 -0
- package/dist/server/turnkey-client.d.ts +24 -0
- package/dist/server/turnkey-client.d.ts.map +1 -0
- package/dist/server/turnkey-client.js +118 -0
- package/dist/server/turnkey-client.js.map +1 -0
- package/dist/server/turnkey-signer.d.ts +40 -0
- package/dist/server/turnkey-signer.d.ts.map +1 -0
- package/dist/server/turnkey-signer.js +121 -0
- package/dist/server/turnkey-signer.js.map +1 -0
- package/dist/types.d.ts +155 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +13 -12
- package/src/client/turnkey-client.ts +0 -364
- package/src/client/turnkey-did-signer.ts +0 -203
- package/src/server/email-auth.ts +0 -258
- package/src/server/jwt.ts +0 -154
- package/src/server/middleware.ts +0 -142
- package/src/server/turnkey-client.ts +0 -156
- package/src/server/turnkey-signer.ts +0 -170
- package/src/types.ts +0 -172
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
package/package.json
CHANGED
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@originals/auth",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.3",
|
|
4
4
|
"description": "Turnkey-based authentication for the Originals Protocol",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "
|
|
7
|
-
"types": "
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
8
|
"files": [
|
|
9
|
-
"
|
|
9
|
+
"dist"
|
|
10
10
|
],
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
|
-
"types": "./
|
|
14
|
-
"import": "./
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
15
|
},
|
|
16
16
|
"./server": {
|
|
17
|
-
"types": "./
|
|
18
|
-
"import": "./
|
|
17
|
+
"types": "./dist/server/index.d.ts",
|
|
18
|
+
"import": "./dist/server/index.js"
|
|
19
19
|
},
|
|
20
20
|
"./client": {
|
|
21
|
-
"types": "./
|
|
22
|
-
"import": "./
|
|
21
|
+
"types": "./dist/client/index.d.ts",
|
|
22
|
+
"import": "./dist/client/index.js"
|
|
23
23
|
},
|
|
24
24
|
"./types": {
|
|
25
|
-
"types": "./
|
|
26
|
-
"import": "./
|
|
25
|
+
"types": "./dist/types.d.ts",
|
|
26
|
+
"import": "./dist/types.js"
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "bunx tsc && bunx tsc-alias",
|
|
31
|
+
"prepublishOnly": "npm run build",
|
|
31
32
|
"test": "bun test tests/",
|
|
32
33
|
"lint": "eslint src/**/*.ts",
|
|
33
34
|
"format": "prettier --write src/**/*.ts"
|
|
@@ -1,364 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client-side Turnkey utilities
|
|
3
|
-
* Uses @turnkey/sdk-server for all Turnkey operations (no viem/ethers dependency)
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { Turnkey } from '@turnkey/sdk-server';
|
|
7
|
-
import type { TurnkeyWallet, TurnkeyWalletAccount } from '../types';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Session expired error for handling token expiration
|
|
11
|
-
*/
|
|
12
|
-
export class TurnkeySessionExpiredError extends Error {
|
|
13
|
-
constructor(message: string = 'Your Turnkey session has expired. Please log in again.') {
|
|
14
|
-
super(message);
|
|
15
|
-
this.name = 'TurnkeySessionExpiredError';
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Wrapper to handle token expiration errors
|
|
21
|
-
*/
|
|
22
|
-
export async function withTokenExpiration<T>(
|
|
23
|
-
fn: () => Promise<T>,
|
|
24
|
-
onExpired?: () => void
|
|
25
|
-
): Promise<T> {
|
|
26
|
-
try {
|
|
27
|
-
return await fn();
|
|
28
|
-
} catch (error) {
|
|
29
|
-
const errorStr = JSON.stringify(error);
|
|
30
|
-
if (
|
|
31
|
-
errorStr.toLowerCase().includes('api_key_expired') ||
|
|
32
|
-
errorStr.toLowerCase().includes('expired api key') ||
|
|
33
|
-
errorStr.toLowerCase().includes('"code":16')
|
|
34
|
-
) {
|
|
35
|
-
console.warn('Detected expired API key, calling onExpired');
|
|
36
|
-
if (onExpired) {
|
|
37
|
-
onExpired();
|
|
38
|
-
}
|
|
39
|
-
throw new TurnkeySessionExpiredError();
|
|
40
|
-
}
|
|
41
|
-
throw error;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Initialize Turnkey server client
|
|
47
|
-
* Reads from environment variables or provided config
|
|
48
|
-
*/
|
|
49
|
-
export function initializeTurnkeyClient(config?: {
|
|
50
|
-
apiBaseUrl?: string;
|
|
51
|
-
apiPublicKey?: string;
|
|
52
|
-
apiPrivateKey?: string;
|
|
53
|
-
organizationId?: string;
|
|
54
|
-
}): Turnkey {
|
|
55
|
-
const apiPublicKey = config?.apiPublicKey ?? process.env.TURNKEY_API_PUBLIC_KEY;
|
|
56
|
-
const apiPrivateKey = config?.apiPrivateKey ?? process.env.TURNKEY_API_PRIVATE_KEY;
|
|
57
|
-
const organizationId = config?.organizationId ?? process.env.TURNKEY_ORGANIZATION_ID;
|
|
58
|
-
|
|
59
|
-
if (!apiPublicKey) {
|
|
60
|
-
throw new Error('TURNKEY_API_PUBLIC_KEY is required');
|
|
61
|
-
}
|
|
62
|
-
if (!apiPrivateKey) {
|
|
63
|
-
throw new Error('TURNKEY_API_PRIVATE_KEY is required');
|
|
64
|
-
}
|
|
65
|
-
if (!organizationId) {
|
|
66
|
-
throw new Error('TURNKEY_ORGANIZATION_ID is required');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return new Turnkey({
|
|
70
|
-
apiBaseUrl: config?.apiBaseUrl ?? 'https://api.turnkey.com',
|
|
71
|
-
apiPublicKey,
|
|
72
|
-
apiPrivateKey,
|
|
73
|
-
defaultOrganizationId: organizationId,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Send OTP code to email via Turnkey
|
|
79
|
-
*/
|
|
80
|
-
export async function initOtp(
|
|
81
|
-
turnkeyClient: Turnkey,
|
|
82
|
-
email: string,
|
|
83
|
-
subOrgId?: string
|
|
84
|
-
): Promise<string> {
|
|
85
|
-
try {
|
|
86
|
-
const result = await turnkeyClient.apiClient().initOtp({
|
|
87
|
-
otpType: 'OTP_TYPE_EMAIL',
|
|
88
|
-
contact: email,
|
|
89
|
-
appName: 'Originals',
|
|
90
|
-
...(subOrgId ? { organizationId: subOrgId } : {}),
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const otpId = result.otpId;
|
|
94
|
-
if (!otpId) {
|
|
95
|
-
throw new Error('No OTP ID returned from Turnkey');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return otpId;
|
|
99
|
-
} catch (error) {
|
|
100
|
-
console.error('Error initializing OTP:', error);
|
|
101
|
-
throw new Error(
|
|
102
|
-
`Failed to send OTP: ${error instanceof Error ? error.message : String(error)}`
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Complete OTP verification flow
|
|
109
|
-
* Returns verification token and sub-org ID
|
|
110
|
-
*/
|
|
111
|
-
export async function completeOtp(
|
|
112
|
-
turnkeyClient: Turnkey,
|
|
113
|
-
otpId: string,
|
|
114
|
-
otpCode: string,
|
|
115
|
-
subOrgId: string
|
|
116
|
-
): Promise<{ verificationToken: string; subOrgId: string }> {
|
|
117
|
-
try {
|
|
118
|
-
const result = await turnkeyClient.apiClient().verifyOtp({
|
|
119
|
-
otpId,
|
|
120
|
-
otpCode,
|
|
121
|
-
expirationSeconds: '900',
|
|
122
|
-
organizationId: subOrgId,
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
if (!result.verificationToken) {
|
|
126
|
-
throw new Error('OTP verification failed - no verification token returned');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
verificationToken: result.verificationToken,
|
|
131
|
-
subOrgId,
|
|
132
|
-
};
|
|
133
|
-
} catch (error) {
|
|
134
|
-
console.error('Error completing OTP:', error);
|
|
135
|
-
throw new Error(
|
|
136
|
-
`Failed to complete OTP: ${error instanceof Error ? error.message : String(error)}`
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Fetch users in a sub-organization
|
|
143
|
-
*/
|
|
144
|
-
export async function fetchUser(
|
|
145
|
-
turnkeyClient: Turnkey,
|
|
146
|
-
subOrgId: string,
|
|
147
|
-
onExpired?: () => void
|
|
148
|
-
): Promise<unknown> {
|
|
149
|
-
return withTokenExpiration(async () => {
|
|
150
|
-
try {
|
|
151
|
-
const response = await turnkeyClient.apiClient().getUsers({
|
|
152
|
-
organizationId: subOrgId,
|
|
153
|
-
});
|
|
154
|
-
const users = response.users ?? [];
|
|
155
|
-
return users[0] ?? null;
|
|
156
|
-
} catch (error) {
|
|
157
|
-
console.error('Error fetching user:', error);
|
|
158
|
-
throw new Error(
|
|
159
|
-
`Failed to fetch user: ${error instanceof Error ? error.message : String(error)}`
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
}, onExpired);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Fetch user's wallets with accounts
|
|
167
|
-
*/
|
|
168
|
-
export async function fetchWallets(
|
|
169
|
-
turnkeyClient: Turnkey,
|
|
170
|
-
subOrgId: string,
|
|
171
|
-
onExpired?: () => void
|
|
172
|
-
): Promise<TurnkeyWallet[]> {
|
|
173
|
-
return withTokenExpiration(async () => {
|
|
174
|
-
try {
|
|
175
|
-
const response = await turnkeyClient.apiClient().getWallets({
|
|
176
|
-
organizationId: subOrgId,
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
const wallets: TurnkeyWallet[] = [];
|
|
180
|
-
|
|
181
|
-
for (const wallet of response.wallets || []) {
|
|
182
|
-
const accountsResponse = await turnkeyClient.apiClient().getWalletAccounts({
|
|
183
|
-
organizationId: subOrgId,
|
|
184
|
-
walletId: wallet.walletId,
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
wallets.push({
|
|
188
|
-
walletId: wallet.walletId,
|
|
189
|
-
walletName: wallet.walletName,
|
|
190
|
-
accounts: (accountsResponse.accounts || []).map(
|
|
191
|
-
(acc: { address: string; curve: string; path: string; addressFormat: string }) => ({
|
|
192
|
-
address: acc.address,
|
|
193
|
-
curve: acc.curve as 'CURVE_SECP256K1' | 'CURVE_ED25519',
|
|
194
|
-
path: acc.path,
|
|
195
|
-
addressFormat: acc.addressFormat,
|
|
196
|
-
})
|
|
197
|
-
),
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return wallets;
|
|
202
|
-
} catch (error) {
|
|
203
|
-
console.error('Error fetching wallets:', error);
|
|
204
|
-
throw new Error(
|
|
205
|
-
`Failed to fetch wallets: ${error instanceof Error ? error.message : String(error)}`
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
}, onExpired);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Get key by curve type from wallets
|
|
213
|
-
*/
|
|
214
|
-
export function getKeyByCurve(
|
|
215
|
-
wallets: TurnkeyWallet[],
|
|
216
|
-
curve: 'CURVE_SECP256K1' | 'CURVE_ED25519'
|
|
217
|
-
): TurnkeyWalletAccount | null {
|
|
218
|
-
for (const wallet of wallets) {
|
|
219
|
-
for (const account of wallet.accounts) {
|
|
220
|
-
if (account.curve === curve) {
|
|
221
|
-
return account;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
return null;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Create a wallet with the required accounts for DID creation
|
|
230
|
-
*/
|
|
231
|
-
export async function createWalletWithAccounts(
|
|
232
|
-
turnkeyClient: Turnkey,
|
|
233
|
-
subOrgId: string,
|
|
234
|
-
onExpired?: () => void
|
|
235
|
-
): Promise<TurnkeyWallet> {
|
|
236
|
-
return withTokenExpiration(async () => {
|
|
237
|
-
try {
|
|
238
|
-
const response = await turnkeyClient.apiClient().createWallet({
|
|
239
|
-
walletName: 'default-wallet',
|
|
240
|
-
accounts: [
|
|
241
|
-
{
|
|
242
|
-
curve: 'CURVE_SECP256K1',
|
|
243
|
-
pathFormat: 'PATH_FORMAT_BIP32',
|
|
244
|
-
path: "m/44'/0'/0'/0/0",
|
|
245
|
-
addressFormat: 'ADDRESS_FORMAT_BITCOIN_MAINNET_P2TR',
|
|
246
|
-
},
|
|
247
|
-
{
|
|
248
|
-
curve: 'CURVE_ED25519',
|
|
249
|
-
pathFormat: 'PATH_FORMAT_BIP32',
|
|
250
|
-
path: "m/44'/501'/0'/0'",
|
|
251
|
-
addressFormat: 'ADDRESS_FORMAT_SOLANA',
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
curve: 'CURVE_ED25519',
|
|
255
|
-
pathFormat: 'PATH_FORMAT_BIP32',
|
|
256
|
-
path: "m/44'/501'/1'/0'",
|
|
257
|
-
addressFormat: 'ADDRESS_FORMAT_SOLANA',
|
|
258
|
-
},
|
|
259
|
-
],
|
|
260
|
-
organizationId: subOrgId,
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
const walletId = response.walletId;
|
|
264
|
-
if (!walletId) {
|
|
265
|
-
throw new Error('No wallet ID returned from createWallet');
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Wait for wallet to be created, then fetch it
|
|
269
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
270
|
-
|
|
271
|
-
const wallets = await fetchWallets(turnkeyClient, subOrgId, onExpired);
|
|
272
|
-
const createdWallet = wallets.find((w) => w.walletId === walletId);
|
|
273
|
-
|
|
274
|
-
if (!createdWallet) {
|
|
275
|
-
throw new Error('Failed to fetch created wallet');
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return createdWallet;
|
|
279
|
-
} catch (error) {
|
|
280
|
-
console.error('Error creating wallet:', error);
|
|
281
|
-
throw new Error(
|
|
282
|
-
`Failed to create wallet: ${error instanceof Error ? error.message : String(error)}`
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
}, onExpired);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
/**
|
|
289
|
-
* Ensure user has a wallet with the required accounts for DID creation
|
|
290
|
-
*/
|
|
291
|
-
export async function ensureWalletWithAccounts(
|
|
292
|
-
turnkeyClient: Turnkey,
|
|
293
|
-
subOrgId: string,
|
|
294
|
-
onExpired?: () => void
|
|
295
|
-
): Promise<TurnkeyWallet[]> {
|
|
296
|
-
return withTokenExpiration(async () => {
|
|
297
|
-
try {
|
|
298
|
-
let wallets = await fetchWallets(turnkeyClient, subOrgId, onExpired);
|
|
299
|
-
|
|
300
|
-
if (wallets.length === 0) {
|
|
301
|
-
console.log('No wallets found, creating new wallet with accounts...');
|
|
302
|
-
const newWallet = await createWalletWithAccounts(turnkeyClient, subOrgId, onExpired);
|
|
303
|
-
wallets = [newWallet];
|
|
304
|
-
return wallets;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const defaultWallet = wallets[0];
|
|
308
|
-
const allAccounts = defaultWallet.accounts;
|
|
309
|
-
const secp256k1Accounts = allAccounts.filter((acc) => acc.curve === 'CURVE_SECP256K1');
|
|
310
|
-
const ed25519Accounts = allAccounts.filter((acc) => acc.curve === 'CURVE_ED25519');
|
|
311
|
-
|
|
312
|
-
// Check if we need more accounts
|
|
313
|
-
if (secp256k1Accounts.length >= 1 && ed25519Accounts.length >= 2) {
|
|
314
|
-
return wallets;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Need to create additional accounts
|
|
318
|
-
const accountsToCreate: Array<{
|
|
319
|
-
curve: 'CURVE_SECP256K1' | 'CURVE_ED25519';
|
|
320
|
-
pathFormat: 'PATH_FORMAT_BIP32';
|
|
321
|
-
path: string;
|
|
322
|
-
addressFormat: 'ADDRESS_FORMAT_BITCOIN_MAINNET_P2TR' | 'ADDRESS_FORMAT_SOLANA';
|
|
323
|
-
}> = [];
|
|
324
|
-
|
|
325
|
-
if (secp256k1Accounts.length === 0) {
|
|
326
|
-
accountsToCreate.push({
|
|
327
|
-
curve: 'CURVE_SECP256K1',
|
|
328
|
-
pathFormat: 'PATH_FORMAT_BIP32',
|
|
329
|
-
path: "m/44'/0'/0'/0/0",
|
|
330
|
-
addressFormat: 'ADDRESS_FORMAT_BITCOIN_MAINNET_P2TR',
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
const ed25519Needed = 2 - ed25519Accounts.length;
|
|
335
|
-
for (let i = 0; i < ed25519Needed; i++) {
|
|
336
|
-
const pathIndex = ed25519Accounts.length + i;
|
|
337
|
-
accountsToCreate.push({
|
|
338
|
-
curve: 'CURVE_ED25519',
|
|
339
|
-
pathFormat: 'PATH_FORMAT_BIP32',
|
|
340
|
-
path: pathIndex === 0 ? "m/44'/501'/0'/0'" : "m/44'/501'/1'/0'",
|
|
341
|
-
addressFormat: 'ADDRESS_FORMAT_SOLANA',
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
if (accountsToCreate.length > 0) {
|
|
346
|
-
console.log(`Creating ${accountsToCreate.length} missing account(s)...`);
|
|
347
|
-
await turnkeyClient.apiClient().createWalletAccounts({
|
|
348
|
-
walletId: defaultWallet.walletId,
|
|
349
|
-
accounts: accountsToCreate,
|
|
350
|
-
organizationId: subOrgId,
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
wallets = await fetchWallets(turnkeyClient, subOrgId, onExpired);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
return wallets;
|
|
357
|
-
} catch (error) {
|
|
358
|
-
console.error('Error ensuring wallet with accounts:', error);
|
|
359
|
-
throw new Error(
|
|
360
|
-
`Failed to ensure wallet with accounts: ${error instanceof Error ? error.message : String(error)}`
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
}, onExpired);
|
|
364
|
-
}
|
|
@@ -1,203 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Turnkey DID Signer Adapter
|
|
3
|
-
* Adapts Turnkey signing to work with didwebvh-ts signer interface
|
|
4
|
-
* Uses @turnkey/sdk-server for all Turnkey operations (no viem/ethers dependency)
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { Turnkey } from '@turnkey/sdk-server';
|
|
8
|
-
import { OriginalsSDK, encoding } from '@originals/sdk';
|
|
9
|
-
import type { TurnkeyWalletAccount } from '../types';
|
|
10
|
-
import { TurnkeySessionExpiredError, withTokenExpiration } from './turnkey-client';
|
|
11
|
-
|
|
12
|
-
interface SigningInput {
|
|
13
|
-
document: Record<string, unknown>;
|
|
14
|
-
proof: Record<string, unknown>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
interface SigningOutput {
|
|
18
|
-
proofValue: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Signer that uses Turnkey for signing DID documents
|
|
23
|
-
* Compatible with didwebvh-ts signer interface
|
|
24
|
-
*/
|
|
25
|
-
export class TurnkeyDIDSigner {
|
|
26
|
-
private turnkeyClient: Turnkey;
|
|
27
|
-
private signWith: string;
|
|
28
|
-
private subOrgId: string;
|
|
29
|
-
private publicKeyMultibase: string;
|
|
30
|
-
private onExpired?: () => void;
|
|
31
|
-
|
|
32
|
-
constructor(
|
|
33
|
-
turnkeyClient: Turnkey,
|
|
34
|
-
signWith: string,
|
|
35
|
-
subOrgId: string,
|
|
36
|
-
publicKeyMultibase: string,
|
|
37
|
-
onExpired?: () => void
|
|
38
|
-
) {
|
|
39
|
-
this.turnkeyClient = turnkeyClient;
|
|
40
|
-
this.signWith = signWith;
|
|
41
|
-
this.subOrgId = subOrgId;
|
|
42
|
-
this.publicKeyMultibase = publicKeyMultibase;
|
|
43
|
-
this.onExpired = onExpired;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Sign the document and proof using Turnkey
|
|
48
|
-
*/
|
|
49
|
-
async sign(input: SigningInput): Promise<SigningOutput> {
|
|
50
|
-
return withTokenExpiration(async () => {
|
|
51
|
-
try {
|
|
52
|
-
// Use SDK's prepareDIDDataForSigning
|
|
53
|
-
const dataToSign = await OriginalsSDK.prepareDIDDataForSigning(input.document, input.proof);
|
|
54
|
-
|
|
55
|
-
// Sign with Turnkey via server SDK
|
|
56
|
-
const result = await this.turnkeyClient.apiClient().signRawPayload({
|
|
57
|
-
organizationId: this.subOrgId,
|
|
58
|
-
signWith: this.signWith,
|
|
59
|
-
payload: Buffer.from(dataToSign).toString('hex'),
|
|
60
|
-
encoding: 'PAYLOAD_ENCODING_HEXADECIMAL',
|
|
61
|
-
hashFunction: 'HASH_FUNCTION_NO_OP',
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const r = result.r;
|
|
65
|
-
const s = result.s;
|
|
66
|
-
|
|
67
|
-
if (!r || !s) {
|
|
68
|
-
throw new Error('Invalid signature response from Turnkey');
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// For Ed25519, combine r+s only (64 bytes total)
|
|
72
|
-
const cleanR = r.startsWith('0x') ? r.slice(2) : r;
|
|
73
|
-
const cleanS = s.startsWith('0x') ? s.slice(2) : s;
|
|
74
|
-
const combinedHex = cleanR + cleanS;
|
|
75
|
-
|
|
76
|
-
const signatureBytes = Buffer.from(combinedHex, 'hex');
|
|
77
|
-
|
|
78
|
-
if (signatureBytes.length !== 64) {
|
|
79
|
-
throw new Error(
|
|
80
|
-
`Invalid Ed25519 signature length: ${signatureBytes.length} (expected 64 bytes)`
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const proofValue = encoding.multibase.encode(signatureBytes, 'base58btc');
|
|
85
|
-
|
|
86
|
-
return { proofValue };
|
|
87
|
-
} catch (error) {
|
|
88
|
-
console.error('[TurnkeyDIDSigner] Error signing with Turnkey:', error);
|
|
89
|
-
|
|
90
|
-
const errorStr = JSON.stringify(error);
|
|
91
|
-
if (
|
|
92
|
-
errorStr.toLowerCase().includes('api_key_expired') ||
|
|
93
|
-
errorStr.toLowerCase().includes('expired api key') ||
|
|
94
|
-
errorStr.toLowerCase().includes('"code":16')
|
|
95
|
-
) {
|
|
96
|
-
console.warn('Detected expired API key in sign method, calling onExpired');
|
|
97
|
-
if (this.onExpired) {
|
|
98
|
-
this.onExpired();
|
|
99
|
-
}
|
|
100
|
-
throw new TurnkeySessionExpiredError();
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
throw error;
|
|
104
|
-
}
|
|
105
|
-
}, this.onExpired);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Get the verification method ID for this signer
|
|
110
|
-
*/
|
|
111
|
-
getVerificationMethodId(): string {
|
|
112
|
-
return `did:key:${this.publicKeyMultibase}`;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Verify a signature
|
|
117
|
-
*/
|
|
118
|
-
async verify(
|
|
119
|
-
signature: Uint8Array,
|
|
120
|
-
message: Uint8Array,
|
|
121
|
-
publicKey: Uint8Array
|
|
122
|
-
): Promise<boolean> {
|
|
123
|
-
try {
|
|
124
|
-
return await OriginalsSDK.verifyDIDSignature(signature, message, publicKey);
|
|
125
|
-
} catch (error) {
|
|
126
|
-
console.error('[TurnkeyDIDSigner] Error verifying signature:', error);
|
|
127
|
-
return false;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Create a DID:WebVH using OriginalsSDK.createDIDOriginal() with Turnkey signing
|
|
134
|
-
*/
|
|
135
|
-
export async function createDIDWithTurnkey(params: {
|
|
136
|
-
turnkeyClient: Turnkey;
|
|
137
|
-
updateKeyAccount: TurnkeyWalletAccount;
|
|
138
|
-
subOrgId: string;
|
|
139
|
-
authKeyPublic: string;
|
|
140
|
-
assertionKeyPublic: string;
|
|
141
|
-
updateKeyPublic: string;
|
|
142
|
-
domain: string;
|
|
143
|
-
slug: string;
|
|
144
|
-
onExpired?: () => void;
|
|
145
|
-
}): Promise<{
|
|
146
|
-
did: string;
|
|
147
|
-
didDocument: unknown;
|
|
148
|
-
didLog: unknown;
|
|
149
|
-
}> {
|
|
150
|
-
const {
|
|
151
|
-
turnkeyClient,
|
|
152
|
-
updateKeyAccount,
|
|
153
|
-
subOrgId,
|
|
154
|
-
authKeyPublic,
|
|
155
|
-
assertionKeyPublic,
|
|
156
|
-
updateKeyPublic,
|
|
157
|
-
domain,
|
|
158
|
-
slug,
|
|
159
|
-
onExpired,
|
|
160
|
-
} = params;
|
|
161
|
-
|
|
162
|
-
// Create Turnkey signer for the update key
|
|
163
|
-
const signer = new TurnkeyDIDSigner(
|
|
164
|
-
turnkeyClient,
|
|
165
|
-
updateKeyAccount.address,
|
|
166
|
-
subOrgId,
|
|
167
|
-
updateKeyPublic,
|
|
168
|
-
onExpired
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
// Use SDK's createDIDOriginal
|
|
172
|
-
const result = await OriginalsSDK.createDIDOriginal({
|
|
173
|
-
type: 'did',
|
|
174
|
-
domain,
|
|
175
|
-
signer,
|
|
176
|
-
verifier: signer,
|
|
177
|
-
updateKeys: [signer.getVerificationMethodId()],
|
|
178
|
-
verificationMethods: [
|
|
179
|
-
{
|
|
180
|
-
id: '#key-0',
|
|
181
|
-
type: 'Multikey',
|
|
182
|
-
controller: '',
|
|
183
|
-
publicKeyMultibase: authKeyPublic,
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
id: '#key-1',
|
|
187
|
-
type: 'Multikey',
|
|
188
|
-
controller: '',
|
|
189
|
-
publicKeyMultibase: assertionKeyPublic,
|
|
190
|
-
},
|
|
191
|
-
],
|
|
192
|
-
paths: [slug],
|
|
193
|
-
portable: false,
|
|
194
|
-
authentication: ['#key-0'],
|
|
195
|
-
assertionMethod: ['#key-1'],
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
return {
|
|
199
|
-
did: result.did,
|
|
200
|
-
didDocument: result.doc,
|
|
201
|
-
didLog: result.log,
|
|
202
|
-
};
|
|
203
|
-
}
|