@originals/auth 1.7.1 → 1.8.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.
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Client-side Turnkey utilities
3
- * Handles Turnkey client initialization and authentication flow in the browser
3
+ * Uses @turnkey/sdk-server for all Turnkey operations (no viem/ethers dependency)
4
4
  */
5
5
 
6
- import { TurnkeyClient, OtpType, WalletAccount } from '@turnkey/core';
7
- import type { TurnkeyWallet } from '../types';
6
+ import { Turnkey } from '@turnkey/sdk-server';
7
+ import type { TurnkeyWallet, TurnkeyWalletAccount } from '../types';
8
8
 
9
9
  /**
10
10
  * Session expired error for handling token expiration
@@ -43,39 +43,59 @@ export async function withTokenExpiration<T>(
43
43
  }
44
44
 
45
45
  /**
46
- * Initialize Turnkey client with auth proxy configuration
46
+ * Initialize Turnkey server client
47
+ * Reads from environment variables or provided config
47
48
  */
48
- export function initializeTurnkeyClient(): TurnkeyClient {
49
- // Access Vite environment variables
50
- const env = (import.meta as { env?: Record<string, string | undefined> }).env;
51
- const authProxyConfigId = env?.VITE_TURNKEY_AUTH_PROXY_CONFIG_ID;
52
- const organizationId = env?.VITE_TURNKEY_ORGANIZATION_ID ?? '';
53
-
54
- if (!authProxyConfigId) {
55
- throw new Error('VITE_TURNKEY_AUTH_PROXY_CONFIG_ID environment variable not set');
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');
56
67
  }
57
68
 
58
- return new TurnkeyClient({
59
- authProxyConfigId,
60
- organizationId,
69
+ return new Turnkey({
70
+ apiBaseUrl: config?.apiBaseUrl ?? 'https://api.turnkey.com',
71
+ apiPublicKey,
72
+ apiPrivateKey,
73
+ defaultOrganizationId: organizationId,
61
74
  });
62
75
  }
63
76
 
64
77
  /**
65
- * Send OTP code to email
78
+ * Send OTP code to email via Turnkey
66
79
  */
67
- export async function initOtp(turnkeyClient: TurnkeyClient, email: string): Promise<string> {
80
+ export async function initOtp(
81
+ turnkeyClient: Turnkey,
82
+ email: string,
83
+ subOrgId?: string
84
+ ): Promise<string> {
68
85
  try {
69
- const response = await turnkeyClient.initOtp({
70
- otpType: OtpType.Email,
86
+ const result = await turnkeyClient.apiClient().initOtp({
87
+ otpType: 'OTP_TYPE_EMAIL',
71
88
  contact: email,
89
+ appName: 'Originals',
90
+ ...(subOrgId ? { organizationId: subOrgId } : {}),
72
91
  });
73
92
 
74
- if (!response || typeof response !== 'string') {
93
+ const otpId = result.otpId;
94
+ if (!otpId) {
75
95
  throw new Error('No OTP ID returned from Turnkey');
76
96
  }
77
97
 
78
- return response;
98
+ return otpId;
79
99
  } catch (error) {
80
100
  console.error('Error initializing OTP:', error);
81
101
  throw new Error(
@@ -85,33 +105,30 @@ export async function initOtp(turnkeyClient: TurnkeyClient, email: string): Prom
85
105
  }
86
106
 
87
107
  /**
88
- * Complete OTP authentication flow (verifies OTP and logs in/signs up)
108
+ * Complete OTP verification flow
109
+ * Returns verification token and sub-org ID
89
110
  */
90
111
  export async function completeOtp(
91
- turnkeyClient: TurnkeyClient,
112
+ turnkeyClient: Turnkey,
92
113
  otpId: string,
93
114
  otpCode: string,
94
- email: string
95
- ): Promise<{ sessionToken: string; userId: string; action: 'login' | 'signup' }> {
115
+ subOrgId: string
116
+ ): Promise<{ verificationToken: string; subOrgId: string }> {
96
117
  try {
97
- const response = await turnkeyClient.completeOtp({
118
+ const result = await turnkeyClient.apiClient().verifyOtp({
98
119
  otpId,
99
120
  otpCode,
100
- contact: email,
101
- otpType: OtpType.Email,
121
+ expirationSeconds: '900',
122
+ organizationId: subOrgId,
102
123
  });
103
124
 
104
- if (!response.sessionToken) {
105
- throw new Error('No session token returned from completeOtp');
125
+ if (!result.verificationToken) {
126
+ throw new Error('OTP verification failed - no verification token returned');
106
127
  }
107
128
 
108
- // Fetch user info to get stable identifiers
109
- const userInfo = await turnkeyClient.fetchUser();
110
-
111
129
  return {
112
- sessionToken: response.sessionToken,
113
- userId: userInfo.userId,
114
- action: String(response.action) === 'LOGIN' ? 'login' : 'signup',
130
+ verificationToken: result.verificationToken,
131
+ subOrgId,
115
132
  };
116
133
  } catch (error) {
117
134
  console.error('Error completing OTP:', error);
@@ -122,16 +139,20 @@ export async function completeOtp(
122
139
  }
123
140
 
124
141
  /**
125
- * Fetch user information
142
+ * Fetch users in a sub-organization
126
143
  */
127
144
  export async function fetchUser(
128
- turnkeyClient: TurnkeyClient,
145
+ turnkeyClient: Turnkey,
146
+ subOrgId: string,
129
147
  onExpired?: () => void
130
148
  ): Promise<unknown> {
131
149
  return withTokenExpiration(async () => {
132
150
  try {
133
- const response = await turnkeyClient.fetchUser();
134
- return response;
151
+ const response = await turnkeyClient.apiClient().getUsers({
152
+ organizationId: subOrgId,
153
+ });
154
+ const users = response.users ?? [];
155
+ return users[0] ?? null;
135
156
  } catch (error) {
136
157
  console.error('Error fetching user:', error);
137
158
  throw new Error(
@@ -142,32 +163,38 @@ export async function fetchUser(
142
163
  }
143
164
 
144
165
  /**
145
- * Fetch user's wallets
166
+ * Fetch user's wallets with accounts
146
167
  */
147
168
  export async function fetchWallets(
148
- turnkeyClient: TurnkeyClient,
169
+ turnkeyClient: Turnkey,
170
+ subOrgId: string,
149
171
  onExpired?: () => void
150
172
  ): Promise<TurnkeyWallet[]> {
151
173
  return withTokenExpiration(async () => {
152
174
  try {
153
- const response = await turnkeyClient.fetchWallets();
175
+ const response = await turnkeyClient.apiClient().getWallets({
176
+ organizationId: subOrgId,
177
+ });
154
178
 
155
179
  const wallets: TurnkeyWallet[] = [];
156
180
 
157
- for (const wallet of response || []) {
158
- const accountsResponse = await turnkeyClient.fetchWalletAccounts({
159
- wallet: wallet,
181
+ for (const wallet of response.wallets || []) {
182
+ const accountsResponse = await turnkeyClient.apiClient().getWalletAccounts({
183
+ organizationId: subOrgId,
184
+ walletId: wallet.walletId,
160
185
  });
161
186
 
162
187
  wallets.push({
163
188
  walletId: wallet.walletId,
164
189
  walletName: wallet.walletName,
165
- accounts: accountsResponse.map((acc: WalletAccount) => ({
166
- address: acc.address,
167
- curve: acc.curve as 'CURVE_SECP256K1' | 'CURVE_ED25519',
168
- path: acc.path,
169
- addressFormat: acc.addressFormat,
170
- })),
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
+ ),
171
198
  });
172
199
  }
173
200
 
@@ -182,16 +209,16 @@ export async function fetchWallets(
182
209
  }
183
210
 
184
211
  /**
185
- * Get key by curve type
212
+ * Get key by curve type from wallets
186
213
  */
187
214
  export function getKeyByCurve(
188
215
  wallets: TurnkeyWallet[],
189
216
  curve: 'CURVE_SECP256K1' | 'CURVE_ED25519'
190
- ): WalletAccount | null {
217
+ ): TurnkeyWalletAccount | null {
191
218
  for (const wallet of wallets) {
192
219
  for (const account of wallet.accounts) {
193
220
  if (account.curve === curve) {
194
- return account as unknown as WalletAccount;
221
+ return account;
195
222
  }
196
223
  }
197
224
  }
@@ -202,12 +229,13 @@ export function getKeyByCurve(
202
229
  * Create a wallet with the required accounts for DID creation
203
230
  */
204
231
  export async function createWalletWithAccounts(
205
- turnkeyClient: TurnkeyClient,
232
+ turnkeyClient: Turnkey,
233
+ subOrgId: string,
206
234
  onExpired?: () => void
207
235
  ): Promise<TurnkeyWallet> {
208
236
  return withTokenExpiration(async () => {
209
237
  try {
210
- const response = await turnkeyClient.createWallet({
238
+ const response = await turnkeyClient.apiClient().createWallet({
211
239
  walletName: 'default-wallet',
212
240
  accounts: [
213
241
  {
@@ -229,15 +257,10 @@ export async function createWalletWithAccounts(
229
257
  addressFormat: 'ADDRESS_FORMAT_SOLANA',
230
258
  },
231
259
  ],
260
+ organizationId: subOrgId,
232
261
  });
233
262
 
234
- let walletId: string;
235
- if (typeof response === 'string') {
236
- walletId = response;
237
- } else {
238
- walletId = (response as { walletId?: string })?.walletId ?? '';
239
- }
240
-
263
+ const walletId = response.walletId;
241
264
  if (!walletId) {
242
265
  throw new Error('No wallet ID returned from createWallet');
243
266
  }
@@ -245,7 +268,7 @@ export async function createWalletWithAccounts(
245
268
  // Wait for wallet to be created, then fetch it
246
269
  await new Promise((resolve) => setTimeout(resolve, 500));
247
270
 
248
- const wallets = await fetchWallets(turnkeyClient, onExpired);
271
+ const wallets = await fetchWallets(turnkeyClient, subOrgId, onExpired);
249
272
  const createdWallet = wallets.find((w) => w.walletId === walletId);
250
273
 
251
274
  if (!createdWallet) {
@@ -266,16 +289,17 @@ export async function createWalletWithAccounts(
266
289
  * Ensure user has a wallet with the required accounts for DID creation
267
290
  */
268
291
  export async function ensureWalletWithAccounts(
269
- turnkeyClient: TurnkeyClient,
292
+ turnkeyClient: Turnkey,
293
+ subOrgId: string,
270
294
  onExpired?: () => void
271
295
  ): Promise<TurnkeyWallet[]> {
272
296
  return withTokenExpiration(async () => {
273
297
  try {
274
- let wallets = await fetchWallets(turnkeyClient, onExpired);
298
+ let wallets = await fetchWallets(turnkeyClient, subOrgId, onExpired);
275
299
 
276
300
  if (wallets.length === 0) {
277
301
  console.log('No wallets found, creating new wallet with accounts...');
278
- const newWallet = await createWalletWithAccounts(turnkeyClient, onExpired);
302
+ const newWallet = await createWalletWithAccounts(turnkeyClient, subOrgId, onExpired);
279
303
  wallets = [newWallet];
280
304
  return wallets;
281
305
  }
@@ -320,12 +344,13 @@ export async function ensureWalletWithAccounts(
320
344
 
321
345
  if (accountsToCreate.length > 0) {
322
346
  console.log(`Creating ${accountsToCreate.length} missing account(s)...`);
323
- await turnkeyClient.createWalletAccounts({
347
+ await turnkeyClient.apiClient().createWalletAccounts({
324
348
  walletId: defaultWallet.walletId,
325
349
  accounts: accountsToCreate,
350
+ organizationId: subOrgId,
326
351
  });
327
352
 
328
- wallets = await fetchWallets(turnkeyClient, onExpired);
353
+ wallets = await fetchWallets(turnkeyClient, subOrgId, onExpired);
329
354
  }
330
355
 
331
356
  return wallets;
@@ -337,4 +362,3 @@ export async function ensureWalletWithAccounts(
337
362
  }
338
363
  }, onExpired);
339
364
  }
340
-
@@ -1,10 +1,12 @@
1
1
  /**
2
2
  * Turnkey DID Signer Adapter
3
3
  * Adapts Turnkey signing to work with didwebvh-ts signer interface
4
+ * Uses @turnkey/sdk-server for all Turnkey operations (no viem/ethers dependency)
4
5
  */
5
6
 
6
- import { TurnkeyClient, WalletAccount } from '@turnkey/core';
7
+ import { Turnkey } from '@turnkey/sdk-server';
7
8
  import { OriginalsSDK, encoding } from '@originals/sdk';
9
+ import type { TurnkeyWalletAccount } from '../types';
8
10
  import { TurnkeySessionExpiredError, withTokenExpiration } from './turnkey-client';
9
11
 
10
12
  interface SigningInput {
@@ -21,19 +23,22 @@ interface SigningOutput {
21
23
  * Compatible with didwebvh-ts signer interface
22
24
  */
23
25
  export class TurnkeyDIDSigner {
24
- private turnkeyClient: TurnkeyClient;
25
- private walletAccount: WalletAccount;
26
+ private turnkeyClient: Turnkey;
27
+ private signWith: string;
28
+ private subOrgId: string;
26
29
  private publicKeyMultibase: string;
27
30
  private onExpired?: () => void;
28
31
 
29
32
  constructor(
30
- turnkeyClient: TurnkeyClient,
31
- walletAccount: WalletAccount,
33
+ turnkeyClient: Turnkey,
34
+ signWith: string,
35
+ subOrgId: string,
32
36
  publicKeyMultibase: string,
33
37
  onExpired?: () => void
34
38
  ) {
35
39
  this.turnkeyClient = turnkeyClient;
36
- this.walletAccount = walletAccount;
40
+ this.signWith = signWith;
41
+ this.subOrgId = subOrgId;
37
42
  this.publicKeyMultibase = publicKeyMultibase;
38
43
  this.onExpired = onExpired;
39
44
  }
@@ -47,21 +52,25 @@ export class TurnkeyDIDSigner {
47
52
  // Use SDK's prepareDIDDataForSigning
48
53
  const dataToSign = await OriginalsSDK.prepareDIDDataForSigning(input.document, input.proof);
49
54
 
50
- // Sign with Turnkey
51
- const response = await this.turnkeyClient.httpClient.signRawPayload({
52
- signWith: this.walletAccount.address,
55
+ // Sign with Turnkey via server SDK
56
+ const result = await this.turnkeyClient.apiClient().signRawPayload({
57
+ organizationId: this.subOrgId,
58
+ signWith: this.signWith,
53
59
  payload: Buffer.from(dataToSign).toString('hex'),
54
60
  encoding: 'PAYLOAD_ENCODING_HEXADECIMAL',
55
- hashFunction: 'HASH_FUNCTION_NOT_APPLICABLE',
61
+ hashFunction: 'HASH_FUNCTION_NO_OP',
56
62
  });
57
63
 
58
- if (!response.r || !response.s) {
64
+ const r = result.r;
65
+ const s = result.s;
66
+
67
+ if (!r || !s) {
59
68
  throw new Error('Invalid signature response from Turnkey');
60
69
  }
61
70
 
62
71
  // For Ed25519, combine r+s only (64 bytes total)
63
- const cleanR = response.r.startsWith('0x') ? response.r.slice(2) : response.r;
64
- const cleanS = response.s.startsWith('0x') ? response.s.slice(2) : response.s;
72
+ const cleanR = r.startsWith('0x') ? r.slice(2) : r;
73
+ const cleanS = s.startsWith('0x') ? s.slice(2) : s;
65
74
  const combinedHex = cleanR + cleanS;
66
75
 
67
76
  const signatureBytes = Buffer.from(combinedHex, 'hex');
@@ -124,8 +133,9 @@ export class TurnkeyDIDSigner {
124
133
  * Create a DID:WebVH using OriginalsSDK.createDIDOriginal() with Turnkey signing
125
134
  */
126
135
  export async function createDIDWithTurnkey(params: {
127
- turnkeyClient: TurnkeyClient;
128
- updateKeyAccount: WalletAccount;
136
+ turnkeyClient: Turnkey;
137
+ updateKeyAccount: TurnkeyWalletAccount;
138
+ subOrgId: string;
129
139
  authKeyPublic: string;
130
140
  assertionKeyPublic: string;
131
141
  updateKeyPublic: string;
@@ -140,6 +150,7 @@ export async function createDIDWithTurnkey(params: {
140
150
  const {
141
151
  turnkeyClient,
142
152
  updateKeyAccount,
153
+ subOrgId,
143
154
  authKeyPublic,
144
155
  assertionKeyPublic,
145
156
  updateKeyPublic,
@@ -149,7 +160,13 @@ export async function createDIDWithTurnkey(params: {
149
160
  } = params;
150
161
 
151
162
  // Create Turnkey signer for the update key
152
- const signer = new TurnkeyDIDSigner(turnkeyClient, updateKeyAccount, updateKeyPublic, onExpired);
163
+ const signer = new TurnkeyDIDSigner(
164
+ turnkeyClient,
165
+ updateKeyAccount.address,
166
+ subOrgId,
167
+ updateKeyPublic,
168
+ onExpired
169
+ );
153
170
 
154
171
  // Use SDK's createDIDOriginal
155
172
  const result = await OriginalsSDK.createDIDOriginal({
@@ -184,10 +201,3 @@ export async function createDIDWithTurnkey(params: {
184
201
  didLog: result.log,
185
202
  };
186
203
  }
187
-
188
-
189
-
190
-
191
-
192
-
193
-
@@ -3,10 +3,11 @@
3
3
  * Implements email-based authentication using Turnkey's OTP flow
4
4
  */
5
5
 
6
+ import { Turnkey } from '@turnkey/sdk-server';
6
7
  import { sha256 } from '@noble/hashes/sha2.js';
7
8
  import { bytesToHex } from '@noble/hashes/utils.js';
8
9
  import type { EmailAuthSession, InitiateAuthResult, VerifyAuthResult } from '../types';
9
- import { getOrCreateTurnkeySubOrg, type TurnkeyHttpClient } from './turnkey-client';
10
+ import { getOrCreateTurnkeySubOrg } from './turnkey-client';
10
11
 
11
12
  // Session timeout (15 minutes to match Turnkey OTP)
12
13
  const SESSION_TIMEOUT = 15 * 60 * 1000;
@@ -77,7 +78,7 @@ function generateSessionId(): string {
77
78
  */
78
79
  export async function initiateEmailAuth(
79
80
  email: string,
80
- turnkeyClient: TurnkeyHttpClient,
81
+ turnkeyClient: Turnkey,
81
82
  sessionStorage?: SessionStorage
82
83
  ): Promise<InitiateAuthResult> {
83
84
  const storage = sessionStorage ?? getDefaultSessionStorage();
@@ -146,7 +147,7 @@ export async function initiateEmailAuth(
146
147
  export async function verifyEmailAuth(
147
148
  sessionId: string,
148
149
  code: string,
149
- turnkeyClient: TurnkeyHttpClient,
150
+ turnkeyClient: Turnkey,
150
151
  sessionStorage?: SessionStorage
151
152
  ): Promise<VerifyAuthResult> {
152
153
  const storage = sessionStorage ?? getDefaultSessionStorage();
@@ -15,7 +15,7 @@
15
15
  * ```
16
16
  */
17
17
 
18
- export { createTurnkeyClient, getOrCreateTurnkeySubOrg, TurnkeyHttpClient } from './turnkey-client';
18
+ export { createTurnkeyClient, getOrCreateTurnkeySubOrg } from './turnkey-client';
19
19
  export {
20
20
  initiateEmailAuth,
21
21
  verifyEmailAuth,
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import type { Request, Response, NextFunction } from 'express';
6
- import { verifyToken } from './jwt';
6
+ import { verifyToken } from './jwt.js';
7
7
  import type { AuthMiddlewareOptions, AuthUser, AuthenticatedRequest } from '../types';
8
8
 
9
9
  /**