better-near-auth 1.8.3 → 1.10.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/README.md CHANGED
@@ -44,15 +44,46 @@ npm install better-near-auth
44
44
  siwn({
45
45
  recipient: "myapp.com",
46
46
 
47
- // Optional: enable gasless relay
48
- relayer: {
49
- whitelistedContracts: ["myapp.near"],
50
- },
47
+ // Optional: enable gasless relay (ephemeral mode by default)
48
+ relayer: true,
51
49
  }),
52
50
  ],
53
51
  });
54
52
  ```
55
53
 
54
+ **Relayer Configuration Options:**
55
+
56
+ ```ts
57
+ // Ephemeral mode (simplest) - auto-generated keypair
58
+ relayer: true
59
+
60
+ // Ephemeral mode (explicit) - same as above with custom settings
61
+ relayer: {
62
+ ephemeral: true,
63
+ whitelistedContracts: ["myapp.near"],
64
+ maxGasPerTransaction: "300000000000000",
65
+ maxDepositPerTransaction: "0",
66
+ }
67
+
68
+ // Explicit mode - bring your own key
69
+ relayer: {
70
+ accountId: "relayer.myapp.near",
71
+ privateKey: process.env.RELAYER_PRIVATE_KEY,
72
+ whitelistedContracts: ["myapp.near"],
73
+ }
74
+
75
+ // Per-network configuration
76
+ relayer: {
77
+ mainnet: {
78
+ whitelistedContracts: ["myapp.near"],
79
+ },
80
+ testnet: {
81
+ whitelistedContracts: ["myapp.testnet"],
82
+ maxGasPerTransaction: "100000000000000", // lower for testing
83
+ },
84
+ }
85
+ ```
86
+
56
87
  3. Generate the schema to add the necessary fields and tables to the database.
57
88
 
58
89
  ```bash
@@ -183,15 +214,42 @@ await authClient.near.disconnect();
183
214
 
184
215
  #### Relayer Configuration
185
216
 
217
+ The relayer can be configured in several ways:
218
+
219
+ | Value | Mode | Description |
220
+ |---|---|---|
221
+ | `true` | Ephemeral | Auto-generated ED25519 keypair (default) |
222
+ | `RelayerEphemeralConfig` | Ephemeral | Ephemeral with custom settings |
223
+ | `RelayerExplicitConfig` | Explicit | Bring your own key |
224
+ | `RelayerDualNetworkConfig` | Mixed | Per-network configuration |
225
+
226
+ **Ephemeral Config (`RelayerEphemeralConfig`):**
227
+
228
+ | Option | Type | Default | Description |
229
+ |---|---|---|---|
230
+ | `whitelistedContracts` | `string[]` | — | Restrict relay to these contracts |
231
+ | `maxGasPerTransaction` | `string` | — | Max gas per relayed tx |
232
+ | `maxDepositPerTransaction` | `string` | — | Max deposit per relayed tx |
233
+
234
+ **Explicit Config (`RelayerExplicitConfig`):**
235
+
186
236
  | Option | Type | Default | Description |
187
237
  |---|---|---|---|
188
- | `accountId` | `string` | — | Named relayer account (explicit mode) |
189
- | `privateKey` | `string` | — | Base64 private key (explicit mode) |
238
+ | `accountId` | `string` | — | Named relayer account (required) |
239
+ | `privateKey` | `string` | — | Base64 private key (required) |
240
+ | `privateKeys` | `string[]` | — | Multiple keys for rotation |
190
241
  | `whitelistedContracts` | `string[]` | — | Restrict relay to these contracts |
191
242
  | `maxGasPerTransaction` | `string` | — | Max gas per relayed tx |
192
243
  | `maxDepositPerTransaction` | `string` | — | Max deposit per relayed tx |
193
244
 
194
- When `accountId` and `privateKey` are omitted, the relayer starts in **ephemeral mode**: an ED25519 keypair is generated on first startup, the implicit account ID is derived from the public key, and the private key is encrypted with AES-256-GCM (using `BETTER_AUTH_SECRET` as KEK via HKDF-SHA256) and stored in the database. The same keypair is recovered on restart.
245
+ **Dual Network Config (`RelayerDualNetworkConfig`):**
246
+
247
+ | Option | Type | Default | Description |
248
+ |---|---|---|---|
249
+ | `mainnet` | `RelayerEphemeralConfig \| RelayerExplicitConfig` | — | Mainnet relayer config |
250
+ | `testnet` | `RelayerEphemeralConfig \| RelayerExplicitConfig` | — | Testnet relayer config |
251
+
252
+ When using `relayer: true` or ephemeral config (no `accountId`/`privateKey`), an ED25519 keypair is generated on first startup, the implicit account ID is derived from the public key, and the private key is encrypted with AES-256-GCM (using `BETTER_AUTH_SECRET` as KEK via HKDF-SHA256) and stored in the database. The same keypair is recovered on restart.
195
253
 
196
254
  ### Client Options
197
255
 
@@ -332,6 +390,10 @@ export const auth = betterAuth({
332
390
 
333
391
  apiKey: process.env.FASTNEAR_API_KEY,
334
392
 
393
+ // Ephemeral mode (simplest)
394
+ relayer: true,
395
+
396
+ // OR explicit mode
335
397
  relayer: {
336
398
  accountId: "relayer.myapp.near",
337
399
  privateKey: process.env.RELAYER_PRIVATE_KEY,
@@ -339,6 +401,17 @@ export const auth = betterAuth({
339
401
  maxGasPerTransaction: "300000000000000",
340
402
  maxDepositPerTransaction: "0",
341
403
  },
404
+
405
+ // OR per-network configuration
406
+ relayer: {
407
+ mainnet: {
408
+ whitelistedContracts: ["myapp.near"],
409
+ },
410
+ testnet: {
411
+ whitelistedContracts: ["myapp.testnet"],
412
+ maxGasPerTransaction: "100000000000000",
413
+ },
414
+ },
342
415
  }),
343
416
  ],
344
417
  });
@@ -378,6 +451,9 @@ The plugin detects the network from the account ID:
378
451
  | "Network ID mismatch" | Ensure networkId matches the account's network |
379
452
  | Relay fails with "insufficient balance" | Fund the relayer account with NEAR |
380
453
  | Relay fails with "contract not whitelisted" | Add `receiverId` to `whitelistedContracts` |
454
+ | Relayer not initializing (ephemeral mode) | Ensure `BETTER_AUTH_SECRET` is set; check startup logs for `[siwn] Relayer initialized` |
455
+ | Empty `relayer_key` table | Relayer config not being passed; use `relayer: true` or provide config object |
456
+ | "BETTER_AUTH_SECRET required" error | Required for ephemeral mode encryption; generate a secure random string |
381
457
 
382
458
  ## Examples
383
459
 
package/dist/index.d.ts CHANGED
@@ -2,14 +2,27 @@ import type { DBAdapter } from "better-auth/types";
2
2
  import z from "zod";
3
3
  import { type AccountId, type DualNetworkConfig, type ListAccountsResponseT, type ListedNearAccount, type Profile, type SubAccountConfig } from "./types.js";
4
4
  export * from "./types.js";
5
- export interface RelayerConfig {
6
- accountId?: string;
7
- privateKey?: string;
5
+ interface RelayerEphemeralConfig {
6
+ accountId?: never;
7
+ privateKey?: never;
8
+ ephemeral?: true;
9
+ whitelistedContracts?: string[];
10
+ maxGasPerTransaction?: string;
11
+ maxDepositPerTransaction?: string;
12
+ }
13
+ interface RelayerExplicitConfig {
14
+ accountId: string;
15
+ privateKey: string;
8
16
  privateKeys?: string[];
9
17
  whitelistedContracts?: string[];
10
18
  maxGasPerTransaction?: string;
11
19
  maxDepositPerTransaction?: string;
12
20
  }
21
+ interface RelayerDualNetworkConfig {
22
+ mainnet?: RelayerEphemeralConfig | RelayerExplicitConfig;
23
+ testnet?: RelayerEphemeralConfig | RelayerExplicitConfig;
24
+ }
25
+ export type RelayerConfig = true | RelayerEphemeralConfig | RelayerExplicitConfig | RelayerDualNetworkConfig;
13
26
  export interface SIWNPluginOptions {
14
27
  recipient?: string;
15
28
  recipients?: DualNetworkConfig<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -376,19 +376,29 @@ async function initRelayer(relayerConfig, network, adapter, secret, apiKey, rpcU
376
376
  if (!relayerConfig) return null;
377
377
  const headers = {};
378
378
  if (apiKey) headers["Authorization"] = `Bearer ${apiKey}`;
379
- if (relayerConfig.accountId && (relayerConfig.privateKey || relayerConfig.privateKeys)) {
380
- const keys = relayerConfig.privateKeys ?? (relayerConfig.privateKey ? [relayerConfig.privateKey] : []);
379
+ let networkConfig;
380
+ if (relayerConfig === true) networkConfig = {};
381
+ else if ("mainnet" in relayerConfig || "testnet" in relayerConfig) {
382
+ const dualConfig = relayerConfig;
383
+ networkConfig = network === "mainnet" ? dualConfig.mainnet : dualConfig.testnet;
384
+ if (!networkConfig) return null;
385
+ } else networkConfig = relayerConfig;
386
+ if ("accountId" in networkConfig && networkConfig.accountId && (networkConfig.privateKey || networkConfig.privateKeys)) {
387
+ const keys = networkConfig.privateKeys ?? (networkConfig.privateKey ? [networkConfig.privateKey] : []);
381
388
  let keyStore;
382
- if (keys.length === 1) keyStore = new InMemoryKeyStore({ [relayerConfig.accountId]: keys[0] });
383
- else keyStore = new RotatingKeyStore({ [relayerConfig.accountId]: keys });
389
+ if (keys.length === 1) keyStore = new InMemoryKeyStore({ [networkConfig.accountId]: keys[0] });
390
+ else keyStore = new RotatingKeyStore({ [networkConfig.accountId]: keys });
384
391
  const near = createNear(network, headers, rpcUrl, keyStore);
385
392
  const explicitPublicKey = parseKey(keys[0]);
386
393
  return {
387
394
  near,
388
- accountId: relayerConfig.accountId,
395
+ accountId: networkConfig.accountId,
389
396
  network,
390
397
  mode: "explicit",
391
- publicKey: explicitPublicKey.publicKey.toString()
398
+ publicKey: explicitPublicKey.publicKey.toString(),
399
+ whitelistedContracts: networkConfig.whitelistedContracts,
400
+ maxGasPerTransaction: networkConfig.maxGasPerTransaction,
401
+ maxDepositPerTransaction: networkConfig.maxDepositPerTransaction
392
402
  };
393
403
  }
394
404
  const existing = await adapter.findOne({
@@ -413,6 +423,9 @@ async function initRelayer(relayerConfig, network, adapter, secret, apiKey, rpcU
413
423
  network,
414
424
  mode: "ephemeral",
415
425
  publicKey: keyPair.publicKey.toString(),
426
+ whitelistedContracts: networkConfig.whitelistedContracts,
427
+ maxGasPerTransaction: networkConfig.maxGasPerTransaction,
428
+ maxDepositPerTransaction: networkConfig.maxDepositPerTransaction,
416
429
  createdAt: existing.createdAt,
417
430
  lastUsedAt: existing.lastUsedAt
418
431
  };
@@ -447,6 +460,9 @@ async function initRelayer(relayerConfig, network, adapter, secret, apiKey, rpcU
447
460
  network,
448
461
  mode: "ephemeral",
449
462
  publicKey: keyPair.publicKey.toString(),
463
+ whitelistedContracts: networkConfig.whitelistedContracts,
464
+ maxGasPerTransaction: networkConfig.maxGasPerTransaction,
465
+ maxDepositPerTransaction: networkConfig.maxDepositPerTransaction,
450
466
  createdAt,
451
467
  lastUsedAt: createdAt
452
468
  };
@@ -494,7 +510,8 @@ const siwn = (options) => {
494
510
  };
495
511
  const getRelayerConfig = (network) => {
496
512
  if (!options.relayer) return void 0;
497
- if ("mainnet" in options.relayer && typeof options.relayer.mainnet === "object") return options.relayer[network];
513
+ if (options.relayer === true) return true;
514
+ if ("mainnet" in options.relayer || "testnet" in options.relayer) return options.relayer[network];
498
515
  return options.relayer;
499
516
  };
500
517
  const getRpcUrl = (network) => {
@@ -516,8 +533,12 @@ const siwn = (options) => {
516
533
  const fromSecrets = getParentKey(network);
517
534
  if (fromSecrets) return fromSecrets;
518
535
  const relayerCfg = getRelayerConfig(network);
519
- const parent = getSubAccountConfig(network)?.parentAccount ?? relayerCfg?.accountId;
520
- if (parent && parent === relayerCfg?.accountId) return relayerCfg.privateKey ?? relayerCfg.privateKeys?.[0];
536
+ const subAccountCfg = getSubAccountConfig(network);
537
+ if (!relayerCfg || relayerCfg === true) return void 0;
538
+ if ("mainnet" in relayerCfg || "testnet" in relayerCfg) return void 0;
539
+ const explicitCfg = relayerCfg;
540
+ const parent = subAccountCfg?.parentAccount ?? explicitCfg.accountId;
541
+ if (parent && parent === explicitCfg.accountId) return explicitCfg.privateKey ?? explicitCfg.privateKeys?.[0];
521
542
  };
522
543
  const primaryNetwork = getSupportedNetworks()[0];
523
544
  const relayerStates = /* @__PURE__ */ new Map();
@@ -528,12 +549,21 @@ const siwn = (options) => {
528
549
  if (relayerStates.has(network)) return relayerStates.get(network) ?? null;
529
550
  const existingInit = relayerInitPromises.get(network);
530
551
  if (existingInit) return existingInit;
531
- const initPromise = initRelayer(getRelayerConfig(network), network, adapter, secret, apiKey, getRpcUrl(network)).then((state) => {
552
+ const relayerCfg = getRelayerConfig(network);
553
+ const initPromise = initRelayer(relayerCfg, network, adapter, secret, apiKey, getRpcUrl(network)).then((state) => {
532
554
  relayerStates.set(network, state);
533
555
  relayerInitPromises.delete(network);
556
+ if (state) {
557
+ console.log(`[siwn] Relayer initialized: ${state.accountId} (${network}, ${state.mode})`);
558
+ if (state.mode === "ephemeral") {
559
+ console.log(`[siwn] Fund this account with NEAR to enable gasless relay`);
560
+ console.log(`[siwn] Private key is encrypted in DB — persists across restarts`);
561
+ }
562
+ } else if (relayerCfg) console.warn(`[siwn] Relayer config provided but not initialized for ${network}. Check BETTER_AUTH_SECRET for ephemeral mode.`);
534
563
  return state;
535
564
  }).catch((error) => {
536
565
  relayerInitPromises.delete(network);
566
+ console.error(`[siwn] Failed to initialize relayer for ${network}:`, error);
537
567
  throw error;
538
568
  });
539
569
  relayerInitPromises.set(network, initPromise);
@@ -1148,29 +1178,34 @@ const siwn = (options) => {
1148
1178
  status: 401
1149
1179
  });
1150
1180
  const relayerConfigForNetwork = getRelayerConfig(network);
1151
- if (relayerConfigForNetwork?.whitelistedContracts?.length) {
1152
- if (!relayerConfigForNetwork.whitelistedContracts.includes(delegateAction.receiverId)) throw new APIError("FORBIDDEN", {
1181
+ const isDualNetwork = relayerConfigForNetwork && typeof relayerConfigForNetwork === "object" && ("mainnet" in relayerConfigForNetwork || "testnet" in relayerConfigForNetwork);
1182
+ const configWithLimits = relayerConfigForNetwork && typeof relayerConfigForNetwork === "object" && !isDualNetwork ? relayerConfigForNetwork : void 0;
1183
+ const whitelistedContracts = configWithLimits?.whitelistedContracts;
1184
+ if (whitelistedContracts?.length) {
1185
+ if (!whitelistedContracts.includes(delegateAction.receiverId)) throw new APIError("FORBIDDEN", {
1153
1186
  message: `Contract ${delegateAction.receiverId} is not whitelisted for relay`,
1154
1187
  status: 403
1155
1188
  });
1156
1189
  }
1157
- if (relayerConfigForNetwork?.maxGasPerTransaction) {
1190
+ const maxGasPerTransaction = configWithLimits?.maxGasPerTransaction;
1191
+ if (maxGasPerTransaction) {
1158
1192
  const totalGas = delegateAction.actions.reduce((sum, a) => {
1159
1193
  return sum + ("functionCall" in a ? a.functionCall.gas : 0n);
1160
1194
  }, 0n);
1161
- if (totalGas > BigInt(relayerConfigForNetwork.maxGasPerTransaction)) throw new APIError("BAD_REQUEST", {
1162
- message: `Transaction gas (${totalGas}) exceeds relayer limit (${relayerConfigForNetwork.maxGasPerTransaction})`,
1195
+ if (totalGas > BigInt(maxGasPerTransaction)) throw new APIError("BAD_REQUEST", {
1196
+ message: `Transaction gas (${totalGas}) exceeds relayer limit (${maxGasPerTransaction})`,
1163
1197
  status: 400
1164
1198
  });
1165
1199
  }
1166
- if (relayerConfigForNetwork?.maxDepositPerTransaction) {
1200
+ const maxDepositPerTransaction = configWithLimits?.maxDepositPerTransaction;
1201
+ if (maxDepositPerTransaction) {
1167
1202
  const totalDeposit = delegateAction.actions.reduce((sum, a) => {
1168
1203
  if ("functionCall" in a) return sum + a.functionCall.deposit;
1169
1204
  if ("transfer" in a) return sum + a.transfer.deposit;
1170
1205
  return sum;
1171
1206
  }, 0n);
1172
- if (totalDeposit > BigInt(relayerConfigForNetwork.maxDepositPerTransaction)) throw new APIError("BAD_REQUEST", {
1173
- message: `Transaction deposit (${totalDeposit}) exceeds relayer limit (${relayerConfigForNetwork.maxDepositPerTransaction})`,
1207
+ if (totalDeposit > BigInt(maxDepositPerTransaction)) throw new APIError("BAD_REQUEST", {
1208
+ message: `Transaction deposit (${totalDeposit}) exceeds relayer limit (${maxDepositPerTransaction})`,
1174
1209
  status: 400
1175
1210
  });
1176
1211
  }