better-near-auth 0.5.2 → 1.0.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
@@ -8,12 +8,19 @@
8
8
  <h1 style="font-size: 2.5rem; font-weight: bold;">better-near-auth</h1>
9
9
 
10
10
  <p>
11
- <strong>Sign in with NEAR (SIWN) plugin for better-auth</strong>
11
+ <strong>Sign in with NEAR + gasless relay plugin for Better Auth</strong>
12
12
  </p>
13
13
 
14
14
  </div>
15
15
 
16
- This [Better Auth](https://better-auth.com) plugin enables secure authentication via NEAR wallets and keypairs by following the [NEP-413 standard](https://github.com/near/NEPs/blob/master/neps/nep-0413.md). It leverages [near-sign-verify](https://github.com/elliotBraem/near-sign-verify), [near-kit](https://kit.near.tools/), and [NEAR Connect](https://github.com/azbang/near-connect) to provide a complete drop-in solution with session management, secure defaults, and automatic profile integration.
16
+ This [Better Auth](https://better-auth.com) plugin enables secure authentication via NEAR wallets following [NEP-413](https://github.com/near/NEPs/blob/master/neps/nep-0413.md) and adds a built-in [NEP-366](https://github.com/near/NEPs/blob/master/neps/nep-0366.md) delegate action relayer so authenticated users can call on-chain contracts gaslessly. It uses [near-kit](https://github.com/elliotBraem/near-kit) for RPC queries and transaction broadcasting, and [@hot-labs/near-connect](https://github.com/azbang/near-connect) for wallet connection.
17
+
18
+ ## Features
19
+
20
+ - **SIWN authentication** — wallet-based sign-in with automatic single-step/two-step flow detection
21
+ - **Gasless relay** — server relays signed delegate actions on-chain, paying gas from a relayer account
22
+ - **Ephemeral relayer keypair** — auto-generated ED25519 keypair on first startup, private key encrypted with AES-256-GCM in the database, persists across restarts
23
+ - **Profile integration** — near-kit profile lookup primary, NEAR Social fallback
17
24
 
18
25
  ## Installation
19
26
 
@@ -36,7 +43,11 @@ npm install better-near-auth
36
43
  plugins: [
37
44
  siwn({
38
45
  recipient: "myapp.com",
39
- anonymous: true, // optional, default is true
46
+
47
+ // Optional: enable gasless relay
48
+ relayer: {
49
+ whitelistedContracts: ["myapp.near"],
50
+ },
40
51
  }),
41
52
  ],
42
53
  });
@@ -58,7 +69,7 @@ npm install better-near-auth
58
69
  plugins: [
59
70
  siwnClient({
60
71
  recipient: "myapp.com",
61
- networkId: "mainnet", // optional, default is "mainnet"
72
+ networkId: "mainnet",
62
73
  })
63
74
  ],
64
75
  });
@@ -66,9 +77,9 @@ npm install better-near-auth
66
77
 
67
78
  ## Usage
68
79
 
69
- ### Single-Step Authentication Flow
80
+ ### Sign In with NEAR
70
81
 
71
- The plugin uses a single-step authentication flow that automatically handles both wallet connection and message signing:
82
+ The `signIn.near()` method automatically detects wallet capabilities and uses the best available flow:
72
83
 
73
84
  ```tsx title="LoginButton.tsx"
74
85
  import { authClient } from "./auth-client";
@@ -89,11 +100,9 @@ export function LoginButton() {
89
100
 
90
101
  const handleSignIn = async () => {
91
102
  setIsSigningIn(true);
92
-
93
103
  await authClient.signIn.near({
94
104
  onSuccess: () => {
95
105
  setIsSigningIn(false);
96
- console.log("Successfully signed in!");
97
106
  },
98
107
  onError: (error) => {
99
108
  setIsSigningIn(false);
@@ -110,83 +119,46 @@ export function LoginButton() {
110
119
  }
111
120
  ```
112
121
 
113
- ### How It Works
114
-
115
- The `signIn.near()` method automatically:
116
-
117
- 1. **Checks wallet capabilities** - Detects if the wallet supports `signInAndSignMessage`
118
- 2. **Single-step flow** (supported wallets): One popup for connection + signing
119
- 3. **Two-step fallback** (unsupported wallets): Automatic fallback to connect then sign
122
+ **Supported wallets:** HOT Wallet, Meteor Wallet, Intear Wallet, MyNearWallet, and more.
120
123
 
121
- **Supported wallets for single-step:**
122
- - Meteor Wallet
123
- - Intear Wallet
124
- - NEAR CLI
125
- - HOT Wallet
126
- - MyNearWallet
127
- - And more...
124
+ ### Gasless Relay
128
125
 
129
- ### Manual Two-Step Flow (Optional)
130
-
131
- If you need explicit control over the connection step:
126
+ Once the relayer is configured on the server, authenticated users can call on-chain contracts without paying gas:
132
127
 
133
128
  ```ts
134
- // Step 1: Connect wallet (optional, for explicit control)
135
- await authClient.requestSignIn.near({
136
- onSuccess: () => console.log("Wallet connected"),
129
+ // 1. Build a signed delegate action using the wallet's FAK
130
+ import { Gas } from "near-kit";
131
+
132
+ const signedAction = await authClient.near.buildSignedDelegateAction(
133
+ "myapp.near",
134
+ (builder, receiverId) => builder.functionCall(receiverId, "some_method", { key: "value" }, {
135
+ gas: Gas.Tgas(30),
136
+ attachedDeposit: BigInt(0),
137
+ })
138
+ );
139
+
140
+ // 2. Relay it — the server pays gas
141
+ const result = await authClient.near.relayTransaction({
142
+ payload: signedAction,
137
143
  });
138
144
 
139
- // Step 2: Sign and authenticate
140
- await authClient.signIn.near({
141
- onSuccess: () => console.log("Signed in!"),
142
- });
143
- ```
144
- setIsSigningIn(false);
145
- console.error("Sign in failed:", error.message);
146
- },
147
- }
148
- );
149
- } catch (error) {
150
- setIsSigningIn(false);
151
- console.error("Authentication error:", error);
152
- }
153
- };
145
+ console.log("Tx hash:", result.txHash);
154
146
 
155
- return (
156
- <div>
157
- <button onClick={handleSignIn} disabled={isSigningIn}>
158
- {isSigningIn ? "Signing in..." : "Sign in with NEAR"}
159
- </button>
160
- </div>
161
- );
162
- }
147
+ // 3. Check status
148
+ const status = await authClient.near.getRelayStatus(result.txHash);
163
149
  ```
164
150
 
165
151
  ### Profile Access
166
152
 
167
- Access user profiles from NEAR Social automatically:
168
-
169
- ```ts title="profile-usage.ts"
170
- // Get current user's profile (requires authentication)
153
+ ```ts
171
154
  const myProfile = await authClient.near.getProfile();
172
- console.log("My profile:", myProfile);
173
-
174
- // Get specific user's profile (no auth required)
175
155
  const aliceProfile = await authClient.near.getProfile("alice.near");
176
- console.log("Alice's profile:", aliceProfile);
177
156
  ```
178
157
 
179
158
  ### Wallet Management
180
159
 
181
- ```ts title="wallet-management.ts"
182
- // Check if wallet is connected
160
+ ```ts
183
161
  const accountId = authClient.near.getAccountId();
184
- console.log("Connected account:", accountId);
185
-
186
- // Get the embedded NEAR client
187
- const nearClient = authClient.near.getNearClient();
188
-
189
- // Disconnect wallet and clear cached data
190
162
  await authClient.near.disconnect();
191
163
  ```
192
164
 
@@ -194,79 +166,98 @@ await authClient.near.disconnect();
194
166
 
195
167
  ### Server Options
196
168
 
197
- The SIWN plugin accepts the following configuration options:
169
+ | Option | Type | Default | Description |
170
+ |---|---|---|---|
171
+ | `recipient` | `string` | — | NEP-413 recipient identifier (required) |
172
+ | `requireFullAccessKey` | `boolean` | `false` | Require full access keys |
173
+ | `getNonce` | `() => Promise<Uint8Array>` | — | Custom nonce generation |
174
+ | `getProfile` | `(accountId: string) => Promise<Profile \| null>` | — | Custom profile lookup |
175
+ | `validateLimitedAccessKey` | `(args) => Promise<boolean>` | — | Validate FAK when `requireFullAccessKey` is false |
176
+ | `apiKey` | `string` | `process.env.FASTNEAR_API_KEY` | API key for RPC |
177
+ | `relayer` | `RelayerConfig` | — | Relayer configuration (see below) |
198
178
 
199
- * **recipient**: The recipient identifier for NEP-413 messages (required)
200
- * **anonymous**: Whether to allow anonymous sign-ins without requiring an email. Default is `true`
201
- * **emailDomainName**: The email domain name for creating user accounts when not using anonymous mode. Defaults to the recipient value
202
- * **requireFullAccessKey**: Whether to require full access keys. Default is `true`
203
- * **getNonce**: Function to generate a unique nonce for each sign-in attempt. Optional, uses secure defaults
204
- * **validateNonce**: Function to validate nonces. Optional, uses time-based validation by default
205
- * **validateRecipient**: Function to validate recipients. Optional, uses exact match by default
206
- * **validateMessage**: Function to validate messages. Optional, no validation by default
207
- * **getProfile**: Function to fetch user profiles. Optional, uses NEAR Social by default
208
- * **validateLimitedAccessKey**: Function to validate function call access keys when `requireFullAccessKey` is false
179
+ #### Relayer Configuration
209
180
 
210
- ### Client Options
181
+ | Option | Type | Default | Description |
182
+ |---|---|---|---|
183
+ | `accountId` | `string` | — | Named relayer account (explicit mode) |
184
+ | `privateKey` | `string` | — | Base64 private key (explicit mode) |
185
+ | `whitelistedContracts` | `string[]` | — | Restrict relay to these contracts |
186
+ | `maxGasPerTransaction` | `string` | — | Max gas per relayed tx |
187
+ | `maxDepositPerTransaction` | `string` | — | Max deposit per relayed tx |
211
188
 
212
- The SIWN client plugin accepts the following configuration options:
189
+ 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.
213
190
 
214
- * **recipient**: The recipient identifier for NEP-413 messages (must match server config)
215
- * **networkId**: NEAR network to use ("mainnet" or "testnet"). Default is "mainnet"
191
+ ### Client Options
216
192
 
217
- ```ts title="auth-client.ts"
218
- import { createAuthClient } from "better-auth/client";
219
- import { siwnClient } from "better-near-auth/client";
220
-
221
- export const authClient = createAuthClient({
222
- plugins: [
223
- siwnClient({
224
- recipient: "myapp.com",
225
- networkId: "testnet", // Use testnet
226
- }),
227
- ],
228
- });
229
- ```
193
+ | Option | Type | Default | Description |
194
+ |---|---|---|---|
195
+ | `recipient` | `string` | — | NEP-413 recipient (must match server) |
196
+ | `networkId` | `"mainnet" \| "testnet"` | `"mainnet"` | NEAR network |
230
197
 
231
198
  ## Schema
232
199
 
233
- The SIWN plugin adds a `nearAccount` table to store user NEAR account associations:
234
-
235
- | Field | Type | Description |
236
- | --------- | ------- | ----------------------------------------- |
237
- | id | string | Primary key |
238
- | userId | string | Reference to user.id |
239
- | accountId | string | NEAR account ID |
240
- | network | string | Network (mainnet or testnet) |
241
- | publicKey | string | Associated public key |
242
- | isPrimary | boolean | Whether this is the user's primary account|
243
- | createdAt | date | Creation timestamp |
200
+ ### nearAccount
201
+
202
+ | Field | Type | Description |
203
+ |---|---|---|
204
+ | id | string | Primary key |
205
+ | userId | string | user.id |
206
+ | accountId | string | NEAR account ID |
207
+ | network | string | mainnet/testnet |
208
+ | publicKey | string | Associated public key |
209
+ | isPrimary | boolean | User's primary account |
210
+ | createdAt | date | |
211
+
212
+ ### relayedTransaction
213
+
214
+ | Field | Type | Description |
215
+ |---|---|---|
216
+ | userId | string | → user.id |
217
+ | txHash | string | On-chain tx hash |
218
+ | senderId | string | Delegate action sender |
219
+ | receiverId | string | Contract called |
220
+ | status | string | pending/completed/failed |
221
+ | gasUsed | string | Gas consumed |
222
+ | createdAt | date | |
223
+
224
+ ### relayerKey
225
+
226
+ | Field | Type | Description |
227
+ |---|---|---|
228
+ | id | string | Singleton per network |
229
+ | accountId | string | Implicit NEAR account ID |
230
+ | encryptedPrivateKey | string | AES-256-GCM encrypted, base64 |
231
+ | iv | string | Initialization vector, base64 |
232
+ | publicKey | string | ed25519:base64 format |
233
+ | network | string | mainnet/testnet |
234
+ | createdAt | date | |
235
+ | lastUsedAt | date | Updated on each relay |
244
236
 
245
237
  ## API Reference
246
238
 
247
- ### Client Actions
248
-
249
- The client plugin provides the following actions:
250
-
251
- #### `authClient.near`
252
-
253
- - `nonce(params)` - Request a nonce from the server
254
- - `verify(params)` - Verify an auth token with the server
255
- - `getProfile(accountId?)` - Get user profile from NEAR Social
256
- - `getNearClient()` - Get the near-kit client instance
257
- - `getAccountId()` - Get the currently connected account ID
258
- - `disconnect()` - Disconnect wallet and clear cached data
259
- - `link(callbacks?)` - Link a NEAR account to the current session
260
- - `unlink(params)` - Unlink a NEAR account from the current session
261
- - `listAccounts()` - List all linked NEAR accounts
262
-
263
- #### `authClient.requestSignIn`
264
-
265
- - `near(callbacks?)` - Connect wallet and cache nonce (for two-step flow)
266
-
267
- #### `authClient.signIn`
268
-
269
- - `near(callbacks?)` - Sign message and authenticate (single-step or two-step)
239
+ ### Client Actions — `authClient.near`
240
+
241
+ **SIWN**
242
+ - `nonce(params)` — Request a nonce from the server
243
+ - `verify(params)` — Verify an auth token with the server
244
+ - `getProfile(accountId?)` — Get user profile (near-kit profile lookup → NEAR Social fallback)
245
+ - `getAccountId()` Currently connected account ID
246
+ - `getState()` Current wallet state
247
+ - `disconnect()` Disconnect wallet and clear cached data
248
+ - `link(callbacks?)` Link a NEAR account to the current session
249
+ - `unlink(params)` Unlink a NEAR account
250
+ - `listAccounts()` List all linked NEAR accounts
251
+
252
+ **Relay**
253
+ - `buildSignedDelegateAction(receiverId, buildActions)` Build + sign a delegate action via wallet FAK
254
+ - `relayTransaction({ payload })` — Submit a signed delegate action to the relayer
255
+ - `getRelayStatus(txHash)` — Check relayed transaction status
256
+ - `getRelayerInfo()` — Get relayer account info, mode, and balance
257
+ - `relayHistory()` List relayed transactions for current user
258
+
259
+ ### `authClient.signIn`
260
+ - `near(callbacks?)` — Connect wallet, sign message, and authenticate (single popup)
270
261
 
271
262
  ### Callback Interface
272
263
 
@@ -279,22 +270,33 @@ interface AuthCallbacks {
279
270
 
280
271
  ### Error Codes
281
272
 
282
- Common error codes you may encounter:
283
-
284
- - `SIGNER_NOT_AVAILABLE` - NEAR wallet not available
285
- - `WALLET_NOT_CONNECTED` - Wallet not connected before signing (two-step fallback)
286
- - `ACCOUNT_MISMATCH` - Cached nonce doesn't match current account (two-step fallback)
287
- - `UNAUTHORIZED_NONCE_REPLAY` - Nonce already used (replay attack detected)
288
- - `UNAUTHORIZED_INVALID_SIGNATURE` - Invalid signature verification
273
+ | Code | Description |
274
+ |---|---|
275
+ | `UNAUTHORIZED_NONCE_REPLAY` | Nonce already used (replay attack detected) |
276
+ | `UNAUTHORIZED` | Generic auth failure (invalid signature, account mismatch, etc.) |
277
+
278
+ ### Server Endpoints
279
+
280
+ | Method | Path | Description |
281
+ |---|---|---|
282
+ | POST | `/near/nonce` | Generate nonce for signing |
283
+ | POST | `/near/verify` | Verify NEP-413 signature, create session |
284
+ | POST | `/near/profile` | Get NEAR profile |
285
+ | POST | `/near/link-account` | Link NEAR account to session |
286
+ | POST | `/near/unlink-account` | Unlink NEAR account |
287
+ | GET | `/near/list-accounts` | List linked NEAR accounts |
288
+ | POST | `/near/relay` | Relay a signed delegate action on-chain |
289
+ | GET | `/near/relay-status/:txHash` | Check relayed transaction status |
290
+ | GET | `/near/relayer-info` | Get relayer accountId, mode, balance |
291
+ | GET | `/near/relay-history` | List relayed transactions for current user |
292
+ | POST | `/near/view` | Server-side read-only contract call (authenticated) |
289
293
 
290
294
  ## Advanced Configuration
291
295
 
292
- For advanced use cases, you can customize the validation functions:
293
-
294
296
  ```ts title="advanced-auth.ts"
295
297
  import { betterAuth } from "better-auth";
296
298
  import { siwn } from "better-near-auth";
297
- import { generateNonce } from "near-sign-verify";
299
+ import { generateNonce } from "near-kit";
298
300
 
299
301
  const usedNonces = new Set<string>();
300
302
 
@@ -302,60 +304,34 @@ export const auth = betterAuth({
302
304
  plugins: [
303
305
  siwn({
304
306
  recipient: "myapp.com",
305
- anonymous: false, // Require email for users
306
- emailDomainName: "myapp.com",
307
- requireFullAccessKey: false, // Allow function call keys
308
-
309
- // Custom nonce generation
310
- getNonce: async () => {
311
- return generateNonce();
312
- },
313
-
314
- // Custom nonce validation (prevents replay attacks)
315
- validateNonce: (nonce: Uint8Array) => {
316
- const nonceHex = Array.from(nonce).map(b => b.toString(16).padStart(2, '0')).join('');
317
- if (usedNonces.has(nonceHex)) {
318
- return false; // Prevent replay attacks
319
- }
320
- usedNonces.add(nonceHex);
321
- return true;
322
- },
323
-
324
- // Custom recipient validation (allow multiple domains)
325
- validateRecipient: (recipient: string) => {
326
- const allowedRecipients = ["myapp.com", "staging.myapp.com", "localhost:3000"];
327
- return allowedRecipients.includes(recipient);
328
- },
329
-
330
- // Custom message validation
331
- validateMessage: (message: string) => {
332
- // Add custom message format validation
333
- return message.includes("Sign in to") && message.length > 10;
334
- },
335
-
336
- // Custom profile lookup
307
+ requireFullAccessKey: false,
308
+
309
+ getNonce: async () => generateNonce(),
310
+
337
311
  getProfile: async (accountId) => {
338
- // Custom profile logic, falls back to NEAR Social
339
312
  try {
340
- const response = await fetch(`https://api.myapp.com/profiles/${accountId}`);
341
- if (response.ok) {
342
- const customProfile = await response.json();
343
- return {
344
- name: customProfile.displayName,
345
- description: customProfile.bio,
346
- image: { url: customProfile.avatar },
347
- };
313
+ const res = await fetch(`https://api.myapp.com/profiles/${accountId}`);
314
+ if (res.ok) {
315
+ const p = await res.json();
316
+ return { name: p.displayName, description: p.bio, image: { url: p.avatar } };
348
317
  }
349
- } catch (error) {
350
- console.error("Custom profile fetch failed:", error);
351
- }
352
- return null; // Use default NEAR Social lookup
318
+ } catch {}
319
+ return null;
353
320
  },
354
-
355
- // Validate function call keys against allowed contracts
321
+
356
322
  validateLimitedAccessKey: async ({ accountId, publicKey, recipient }) => {
357
- const allowedContracts = ["myapp.near", "social.near"];
358
- return recipient ? allowedContracts.includes(recipient) : true;
323
+ const allowed = ["myapp.near", "social.near"];
324
+ return recipient ? allowed.includes(recipient) : true;
325
+ },
326
+
327
+ apiKey: process.env.FASTNEAR_API_KEY,
328
+
329
+ relayer: {
330
+ accountId: "relayer.myapp.near",
331
+ privateKey: process.env.RELAYER_PRIVATE_KEY,
332
+ whitelistedContracts: ["myapp.near"],
333
+ maxGasPerTransaction: "300000000000000",
334
+ maxDepositPerTransaction: "0",
359
335
  },
360
336
  }),
361
337
  ],
@@ -364,80 +340,49 @@ export const auth = betterAuth({
364
340
 
365
341
  ## Network Support
366
342
 
367
- The plugin automatically detects the network from the account ID:
343
+ The plugin detects the network from the account ID:
368
344
 
369
- - Accounts ending with `.testnet` use the testnet network
370
- - All other accounts use the mainnet network
345
+ - Accounts ending with `.testnet` testnet
346
+ - All other accounts mainnet
371
347
 
372
- You can configure the client to use a specific network:
373
-
374
- ```ts title="testnet-config.ts"
375
- export const authClient = createAuthClient({
376
- plugins: [
377
- siwnClient({
378
- domain: "myapp.com",
379
- networkId: "testnet", // Use testnet
380
- }),
381
- ],
382
- });
383
- ```
384
-
385
- ## Security Features
348
+ ## Security
386
349
 
387
350
  ### NEP-413 Compliance
388
- - Follows NEAR Enhancement Proposal 413 for secure message signing
389
- - Implements proper nonce handling to prevent replay attacks
390
- - Validates message format and recipient information
351
+ - Proper nonce handling prevents replay attacks
352
+ - Message format and recipient validation
353
+ - 15-minute server-side nonce expiration with DB replay detection
391
354
 
392
- ### Nonce Management
393
- - Unique nonce storage per account/network/publicKey combination
394
- - 15-minute server-side expiration for nonces
395
- - 5-minute client-side cache expiration
396
- - Automatic cleanup after successful authentication
355
+ ### Relayer Key Security
356
+ - Ephemeral private key encrypted at rest with AES-256-GCM
357
+ - KEK derived from `BETTER_AUTH_SECRET` via HKDF-SHA256
358
+ - Private key held only in process memory — never in env vars or config files
359
+ - Trust model matches Better Auth session tokens: DB access + secret = full access
397
360
 
398
361
  ### Access Key Support
399
- - Supports both full access keys and function call access keys
362
+ - Full access keys and function-call access keys (FAK)
363
+ - FAK scoped to recipient contract for delegate actions
400
364
  - Configurable validation for limited access keys
401
- - Contract-specific access control when using function call keys
402
365
 
403
366
  ## Troubleshooting
404
367
 
405
- ### Common Issues
406
-
407
- 1. **"Wallet not connected"**
408
- - You must call `requestSignIn.near()` before `signIn.near()`
409
- - Check that the near-kit client is properly initialized
410
-
411
- 2. **"No valid nonce found"**
412
- - Ensure `requestSignIn.near()` completed successfully before calling `signIn.near()`
413
- - Client nonces expire after 5 minutes
414
-
415
- 3. **"Invalid or expired nonce"**
416
- - Server nonces expire after 15 minutes
417
- - Ensure client and server clocks are synchronized
418
-
419
- 4. **"Account ID mismatch"**
420
- - Verify the signed message contains the correct account ID
421
- - Check for wallet switching between the two authentication steps
422
-
423
- 5. **"Network ID mismatch"**
424
- - Ensure the networkId sent to the server matches the account's network
425
- - Testnet accounts must use "testnet", mainnet accounts use "mainnet"
426
-
368
+ | Issue | Solution |
369
+ |---|---|
370
+ | "Invalid or expired nonce" | Server nonces expire after 15 min; check clock sync |
371
+ | "Account ID mismatch" | Verify signed message account ID matches wallet |
372
+ | "Network ID mismatch" | Ensure networkId matches the account's network |
373
+ | Relay fails with "insufficient balance" | Fund the relayer account with NEAR |
374
+ | Relay fails with "contract not whitelisted" | Add `receiverId` to `whitelistedContracts` |
427
375
 
428
376
  ## Examples
429
377
 
430
- This repository includes example applications demonstrating how to use better-near-auth:
431
-
432
378
  ### Browser to Server Example
433
379
 
434
- A full-stack example showing NEAR authentication in a browser app with a server backend.
380
+ A full-stack example showing NEAR authentication + gasless relay.
435
381
 
436
382
  - **Location**: `examples/browser-2-server/`
437
383
  - **Live Demo**: [better-near-auth.near.page](https://better-near-auth.near.page)
438
384
  - **Tech Stack**: Hono, Drizzle ORM, React, TanStack Router
439
385
 
440
- **Running locally:**
441
386
  ```bash
442
387
  # From repo root
443
388
  pnpm install
@@ -445,54 +390,30 @@ cd examples/browser-2-server
445
390
  pnpm dev
446
391
  ```
447
392
 
448
- **Deployment:**
449
- Each example can be deployed independently to Railway or other platforms. See the example's README for deployment instructions.
450
-
451
393
  ## Development
452
394
 
453
- Interested in contributing? See [CONTRIBUTING.md](./CONTRIBUTING.md) for:
454
- - Development setup
455
- - How to add changesets
456
- - Pull request guidelines
457
- - Release process
395
+ Interested in contributing? See [CONTRIBUTING.md](./CONTRIBUTING.md).
458
396
 
459
397
  **Quick start:**
460
398
  ```bash
461
- # Install dependencies
462
399
  pnpm install
463
-
464
- # Build the package
465
400
  pnpm build
466
-
467
- # Run type checking
468
401
  pnpm typecheck
469
-
470
- # Run tests
471
402
  pnpm test
472
-
473
- # Run example locally
474
- cd examples/browser-2-server && pnpm dev
475
403
  ```
476
404
 
477
405
  **Build output:**
478
- - `dist/index.js` - Server plugin (ESM)
479
- - `dist/client.js` - Client plugin (ESM)
480
- - `dist/*.d.ts` - TypeScript declarations
481
-
482
- **Deployment workflow:**
483
- 1. Make changes and create changeset
484
- 2. Merge PR → Changesets publishes to npm
485
- 3. Example auto-updates to use published version
486
- 4. Railway auto-deploys
487
- 5. Example reverts to `workspace:*` for next development cycle
406
+ - `dist/index.js` Server plugin (ESM)
407
+ - `dist/client.js` Client plugin (ESM)
408
+ - `dist/*.d.ts` TypeScript declarations
488
409
 
489
410
  ## Links
490
411
 
491
- * [Better Auth Documentation](https://better-auth.com)
492
- * [NEAR Protocol](https://near.org)
493
- * [NEP-413 Specification](https://github.com/near/NEPs/blob/master/neps/nep-0413.md)
494
- * [near-sign-verify](https://github.com/elliotBraem/near-sign-verify)
495
- * [near-kit](https://kit.near.tools/)
496
- * [NEAR Connect](https://github.com/azbang/near-connect)
497
- * [Example Implementation](https://better-near-auth.near.page) - Live demo
498
- * [Contributing Guide](./CONTRIBUTING.md) - Development and contribution guidelines
412
+ - [Better Auth Documentation](https://better-auth.com)
413
+ - [NEAR Protocol](https://near.org)
414
+ - [NEP-413 Specification](https://github.com/near/NEPs/blob/master/neps/nep-0413.md)
415
+ - [NEP-366 Delegate Actions](https://github.com/near/NEPs/blob/master/neps/nep-0366.md)
416
+ - [near-kit](https://github.com/elliotBraem/near-kit)
417
+ - [@hot-labs/near-connect](https://github.com/azbang/near-connect)
418
+ - [Example Implementation](https://better-near-auth.near.page)
419
+ - [Contributing Guide](./CONTRIBUTING.md)