@originals/auth 1.8.1 → 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/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@originals/auth",
3
- "version": "1.8.1",
3
+ "version": "1.8.3",
4
4
  "description": "Turnkey-based authentication for the Originals Protocol",
5
5
  "type": "module",
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
8
11
  "exports": {
9
12
  ".": {
10
13
  "types": "./dist/index.d.ts",
@@ -21,18 +24,11 @@
21
24
  "./types": {
22
25
  "types": "./dist/types.d.ts",
23
26
  "import": "./dist/types.js"
24
- },
25
- "./server/*.js": {
26
- "types": "./dist/server/*.d.ts",
27
- "import": "./dist/server/*.js"
28
- },
29
- "./client/*.js": {
30
- "types": "./dist/client/*.d.ts",
31
- "import": "./dist/client/*.js"
32
27
  }
33
28
  },
34
29
  "scripts": {
35
30
  "build": "bunx tsc && bunx tsc-alias",
31
+ "prepublishOnly": "npm run build",
36
32
  "test": "bun test tests/",
37
33
  "lint": "eslint src/**/*.ts",
38
34
  "format": "prettier --write src/**/*.ts"
@@ -56,7 +52,7 @@
56
52
  },
57
53
  "dependencies": {
58
54
  "@turnkey/sdk-server": "^5.0.0",
59
- "@originals/sdk": "^1.4.4",
55
+ "@originals/sdk": "^1.8.1",
60
56
  "jsonwebtoken": "^9.0.2",
61
57
  "@noble/hashes": "^2.0.1",
62
58
  "@noble/ed25519": "^2.0.0"
@@ -1 +0,0 @@
1
- $ bunx tsc && bunx tsc-alias
package/eslint.config.js DELETED
@@ -1,32 +0,0 @@
1
- import eslint from '@eslint/js';
2
- import tseslint from 'typescript-eslint';
3
-
4
- export default tseslint.config(
5
- eslint.configs.recommended,
6
- ...tseslint.configs.recommendedTypeChecked,
7
- {
8
- languageOptions: {
9
- parserOptions: {
10
- project: true,
11
- tsconfigRootDir: import.meta.dirname,
12
- },
13
- },
14
- rules: {
15
- '@typescript-eslint/no-explicit-any': 'warn',
16
- '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
17
- '@typescript-eslint/explicit-function-return-type': 'off',
18
- '@typescript-eslint/no-unsafe-assignment': 'warn',
19
- '@typescript-eslint/no-unsafe-member-access': 'warn',
20
- '@typescript-eslint/no-unsafe-call': 'warn',
21
- '@typescript-eslint/no-unsafe-return': 'warn',
22
- '@typescript-eslint/restrict-template-expressions': 'warn',
23
- 'no-console': 'off',
24
- 'no-empty': ['error', { allowEmptyCatch: false }],
25
- 'prefer-const': 'error',
26
- 'no-var': 'error',
27
- },
28
- },
29
- {
30
- ignores: ['dist/', 'node_modules/', '*.js', 'tests/'],
31
- }
32
- );
@@ -1,36 +0,0 @@
1
- /**
2
- * Client-side authentication utilities
3
- *
4
- * Pure library functions for Turnkey authentication.
5
- * No React dependencies - consuming apps should create their own hooks.
6
- *
7
- * @example
8
- * ```typescript
9
- * import {
10
- * initializeTurnkeyClient,
11
- * initOtp,
12
- * completeOtp,
13
- * fetchUser,
14
- * fetchWallets,
15
- * TurnkeyDIDSigner,
16
- * createDIDWithTurnkey
17
- * } from '@originals/auth/client';
18
- * ```
19
- */
20
-
21
- export {
22
- initializeTurnkeyClient,
23
- initOtp,
24
- completeOtp,
25
- fetchUser,
26
- fetchWallets,
27
- getKeyByCurve,
28
- createWalletWithAccounts,
29
- ensureWalletWithAccounts,
30
- TurnkeySessionExpiredError,
31
- withTokenExpiration,
32
- } from './turnkey-client';
33
-
34
- export { TurnkeyDIDSigner, createDIDWithTurnkey } from './turnkey-did-signer';
35
-
36
- export { sendOtp, verifyOtp, type ServerAuthOptions } from './server-auth';
@@ -1,101 +0,0 @@
1
- /**
2
- * Client-side helpers for server-proxied authentication
3
- * Use these when your server handles Turnkey API keys
4
- *
5
- * @example
6
- * ```typescript
7
- * import { sendOtp, verifyOtp } from '@originals/auth/client';
8
- *
9
- * // Send OTP to user's email via your server
10
- * const { sessionId } = await sendOtp('user@example.com');
11
- *
12
- * // Verify the code they enter
13
- * const { verified, email, subOrgId } = await verifyOtp(sessionId, '123456');
14
- * ```
15
- */
16
-
17
- import type { InitiateAuthResult, VerifyAuthResult } from '../types';
18
-
19
- /**
20
- * Options for server-proxied auth functions
21
- */
22
- export interface ServerAuthOptions {
23
- /** Custom fetch function (for testing) */
24
- fetch?: typeof fetch;
25
- }
26
-
27
- /**
28
- * Send OTP via your server endpoint
29
- * Server should call initiateEmailAuth() from @originals/auth/server
30
- *
31
- * @param email - User's email address
32
- * @param endpoint - Server endpoint URL (default: '/api/auth/send-otp')
33
- * @param options - Optional configuration
34
- * @returns Promise with sessionId and message
35
- *
36
- * @example
37
- * ```typescript
38
- * const { sessionId, message } = await sendOtp('user@example.com');
39
- * // sessionId is used for verification step
40
- * // message is user-friendly text to display
41
- * ```
42
- */
43
- export async function sendOtp(
44
- email: string,
45
- endpoint = '/api/auth/send-otp',
46
- options?: ServerAuthOptions
47
- ): Promise<InitiateAuthResult> {
48
- const fetchFn = options?.fetch ?? fetch;
49
- const response = await fetchFn(endpoint, {
50
- method: 'POST',
51
- headers: { 'Content-Type': 'application/json' },
52
- body: JSON.stringify({ email }),
53
- });
54
-
55
- if (!response.ok) {
56
- const error = await response.json().catch(() => ({ message: 'Failed to send OTP' })) as { message?: string };
57
- throw new Error(error.message ?? `HTTP ${response.status}`);
58
- }
59
-
60
- return response.json() as Promise<InitiateAuthResult>;
61
- }
62
-
63
- /**
64
- * Verify OTP via your server endpoint
65
- * Server should call verifyEmailAuth() from @originals/auth/server
66
- *
67
- * @param sessionId - Session ID from sendOtp result
68
- * @param code - OTP code entered by user
69
- * @param endpoint - Server endpoint URL (default: '/api/auth/verify-otp')
70
- * @param options - Optional configuration
71
- * @returns Promise with verification result
72
- *
73
- * @example
74
- * ```typescript
75
- * const { verified, email, subOrgId } = await verifyOtp(sessionId, '123456');
76
- * if (verified) {
77
- * // User is authenticated
78
- * console.log(`Authenticated: ${email}`);
79
- * }
80
- * ```
81
- */
82
- export async function verifyOtp(
83
- sessionId: string,
84
- code: string,
85
- endpoint = '/api/auth/verify-otp',
86
- options?: ServerAuthOptions
87
- ): Promise<VerifyAuthResult> {
88
- const fetchFn = options?.fetch ?? fetch;
89
- const response = await fetchFn(endpoint, {
90
- method: 'POST',
91
- headers: { 'Content-Type': 'application/json' },
92
- body: JSON.stringify({ sessionId, code }),
93
- });
94
-
95
- if (!response.ok) {
96
- const error = await response.json().catch(() => ({ message: 'Verification failed' })) as { message?: string };
97
- throw new Error(error.message ?? `HTTP ${response.status}`);
98
- }
99
-
100
- return response.json() as Promise<VerifyAuthResult>;
101
- }
@@ -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
- }