better-near-auth 0.6.0 → 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 +33 -74
- package/dist/client.d.ts +11 -22
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +117 -147
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +3 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +229 -140
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +75 -66
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +43 -30
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +0 -20
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +2 -47
- package/dist/utils.js.map +1 -1
- package/package.json +5 -11
- package/dist/rpc.d.ts +0 -64
- package/dist/rpc.d.ts.map +0 -1
- package/dist/rpc.js +0 -70
- package/dist/rpc.js.map +0 -1
package/README.md
CHANGED
|
@@ -13,14 +13,14 @@
|
|
|
13
13
|
|
|
14
14
|
</div>
|
|
15
15
|
|
|
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 [
|
|
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
17
|
|
|
18
18
|
## Features
|
|
19
19
|
|
|
20
20
|
- **SIWN authentication** — wallet-based sign-in with automatic single-step/two-step flow detection
|
|
21
21
|
- **Gasless relay** — server relays signed delegate actions on-chain, paying gas from a relayer account
|
|
22
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** —
|
|
23
|
+
- **Profile integration** — near-kit profile lookup primary, NEAR Social fallback
|
|
24
24
|
|
|
25
25
|
## Installation
|
|
26
26
|
|
|
@@ -43,7 +43,6 @@ npm install better-near-auth
|
|
|
43
43
|
plugins: [
|
|
44
44
|
siwn({
|
|
45
45
|
recipient: "myapp.com",
|
|
46
|
-
anonymous: true,
|
|
47
46
|
|
|
48
47
|
// Optional: enable gasless relay
|
|
49
48
|
relayer: {
|
|
@@ -120,19 +119,7 @@ export function LoginButton() {
|
|
|
120
119
|
}
|
|
121
120
|
```
|
|
122
121
|
|
|
123
|
-
**Supported wallets
|
|
124
|
-
|
|
125
|
-
### Manual Two-Step Flow (Optional)
|
|
126
|
-
|
|
127
|
-
```ts
|
|
128
|
-
await authClient.requestSignIn.near({
|
|
129
|
-
onSuccess: () => console.log("Wallet connected"),
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
await authClient.signIn.near({
|
|
133
|
-
onSuccess: () => console.log("Signed in!"),
|
|
134
|
-
});
|
|
135
|
-
```
|
|
122
|
+
**Supported wallets:** HOT Wallet, Meteor Wallet, Intear Wallet, MyNearWallet, and more.
|
|
136
123
|
|
|
137
124
|
### Gasless Relay
|
|
138
125
|
|
|
@@ -140,20 +127,19 @@ Once the relayer is configured on the server, authenticated users can call on-ch
|
|
|
140
127
|
|
|
141
128
|
```ts
|
|
142
129
|
// 1. Build a signed delegate action using the wallet's FAK
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
});
|
|
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
|
+
);
|
|
153
139
|
|
|
154
140
|
// 2. Relay it — the server pays gas
|
|
155
141
|
const result = await authClient.near.relayTransaction({
|
|
156
|
-
|
|
142
|
+
payload: signedAction,
|
|
157
143
|
});
|
|
158
144
|
|
|
159
145
|
console.log("Tx hash:", result.txHash);
|
|
@@ -183,16 +169,11 @@ await authClient.near.disconnect();
|
|
|
183
169
|
| Option | Type | Default | Description |
|
|
184
170
|
|---|---|---|---|
|
|
185
171
|
| `recipient` | `string` | — | NEP-413 recipient identifier (required) |
|
|
186
|
-
| `
|
|
187
|
-
| `emailDomainName` | `string` | recipient | Email domain for non-anonymous accounts |
|
|
188
|
-
| `requireFullAccessKey` | `boolean` | `true` | Require full access keys |
|
|
172
|
+
| `requireFullAccessKey` | `boolean` | `false` | Require full access keys |
|
|
189
173
|
| `getNonce` | `() => Promise<Uint8Array>` | — | Custom nonce generation |
|
|
190
|
-
| `validateNonce` | `(nonce: Uint8Array) => boolean` | — | Custom nonce validation |
|
|
191
|
-
| `validateRecipient` | `(recipient: string) => boolean` | — | Custom recipient validation |
|
|
192
|
-
| `validateMessage` | `(message: string) => boolean` | — | Custom message validation |
|
|
193
174
|
| `getProfile` | `(accountId: string) => Promise<Profile \| null>` | — | Custom profile lookup |
|
|
194
175
|
| `validateLimitedAccessKey` | `(args) => Promise<boolean>` | — | Validate FAK when `requireFullAccessKey` is false |
|
|
195
|
-
| `
|
|
176
|
+
| `apiKey` | `string` | `process.env.FASTNEAR_API_KEY` | API key for RPC |
|
|
196
177
|
| `relayer` | `RelayerConfig` | — | Relayer configuration (see below) |
|
|
197
178
|
|
|
198
179
|
#### Relayer Configuration
|
|
@@ -201,10 +182,9 @@ await authClient.near.disconnect();
|
|
|
201
182
|
|---|---|---|---|
|
|
202
183
|
| `accountId` | `string` | — | Named relayer account (explicit mode) |
|
|
203
184
|
| `privateKey` | `string` | — | Base64 private key (explicit mode) |
|
|
204
|
-
| `relayTarget` | `string` | FastNear RPC | RPC URL for broadcasting |
|
|
205
185
|
| `whitelistedContracts` | `string[]` | — | Restrict relay to these contracts |
|
|
206
|
-
| `maxGasPerTransaction` | `string` |
|
|
207
|
-
| `maxDepositPerTransaction` | `string` |
|
|
186
|
+
| `maxGasPerTransaction` | `string` | — | Max gas per relayed tx |
|
|
187
|
+
| `maxDepositPerTransaction` | `string` | — | Max deposit per relayed tx |
|
|
208
188
|
|
|
209
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.
|
|
210
190
|
|
|
@@ -213,7 +193,7 @@ When `accountId` and `privateKey` are omitted, the relayer starts in **ephemeral
|
|
|
213
193
|
| Option | Type | Default | Description |
|
|
214
194
|
|---|---|---|---|
|
|
215
195
|
| `recipient` | `string` | — | NEP-413 recipient (must match server) |
|
|
216
|
-
| `networkId` | `
|
|
196
|
+
| `networkId` | `"mainnet" \| "testnet"` | `"mainnet"` | NEAR network |
|
|
217
197
|
|
|
218
198
|
## Schema
|
|
219
199
|
|
|
@@ -261,7 +241,7 @@ When `accountId` and `privateKey` are omitted, the relayer starts in **ephemeral
|
|
|
261
241
|
**SIWN**
|
|
262
242
|
- `nonce(params)` — Request a nonce from the server
|
|
263
243
|
- `verify(params)` — Verify an auth token with the server
|
|
264
|
-
- `getProfile(accountId?)` — Get user profile (
|
|
244
|
+
- `getProfile(accountId?)` — Get user profile (near-kit profile lookup → NEAR Social fallback)
|
|
265
245
|
- `getAccountId()` — Currently connected account ID
|
|
266
246
|
- `getState()` — Current wallet state
|
|
267
247
|
- `disconnect()` — Disconnect wallet and clear cached data
|
|
@@ -270,15 +250,14 @@ When `accountId` and `privateKey` are omitted, the relayer starts in **ephemeral
|
|
|
270
250
|
- `listAccounts()` — List all linked NEAR accounts
|
|
271
251
|
|
|
272
252
|
**Relay**
|
|
273
|
-
- `buildSignedDelegateAction(
|
|
274
|
-
- `relayTransaction({
|
|
253
|
+
- `buildSignedDelegateAction(receiverId, buildActions)` — Build + sign a delegate action via wallet FAK
|
|
254
|
+
- `relayTransaction({ payload })` — Submit a signed delegate action to the relayer
|
|
275
255
|
- `getRelayStatus(txHash)` — Check relayed transaction status
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
- `near(callbacks?)` — Connect wallet and cache nonce (two-step flow)
|
|
256
|
+
- `getRelayerInfo()` — Get relayer account info, mode, and balance
|
|
257
|
+
- `relayHistory()` — List relayed transactions for current user
|
|
279
258
|
|
|
280
259
|
### `authClient.signIn`
|
|
281
|
-
- `near(callbacks?)` —
|
|
260
|
+
- `near(callbacks?)` — Connect wallet, sign message, and authenticate (single popup)
|
|
282
261
|
|
|
283
262
|
### Callback Interface
|
|
284
263
|
|
|
@@ -293,11 +272,8 @@ interface AuthCallbacks {
|
|
|
293
272
|
|
|
294
273
|
| Code | Description |
|
|
295
274
|
|---|---|
|
|
296
|
-
| `
|
|
297
|
-
| `
|
|
298
|
-
| `ACCOUNT_MISMATCH` | Cached nonce doesn't match current account |
|
|
299
|
-
| `UNAUTHORIZED_NONCE_REPLAY` | Nonce already used |
|
|
300
|
-
| `UNAUTHORIZED_INVALID_SIGNATURE` | Invalid signature verification |
|
|
275
|
+
| `UNAUTHORIZED_NONCE_REPLAY` | Nonce already used (replay attack detected) |
|
|
276
|
+
| `UNAUTHORIZED` | Generic auth failure (invalid signature, account mismatch, etc.) |
|
|
301
277
|
|
|
302
278
|
### Server Endpoints
|
|
303
279
|
|
|
@@ -312,13 +288,15 @@ interface AuthCallbacks {
|
|
|
312
288
|
| POST | `/near/relay` | Relay a signed delegate action on-chain |
|
|
313
289
|
| GET | `/near/relay-status/:txHash` | Check relayed transaction status |
|
|
314
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) |
|
|
315
293
|
|
|
316
294
|
## Advanced Configuration
|
|
317
295
|
|
|
318
296
|
```ts title="advanced-auth.ts"
|
|
319
297
|
import { betterAuth } from "better-auth";
|
|
320
298
|
import { siwn } from "better-near-auth";
|
|
321
|
-
import { generateNonce } from "near-
|
|
299
|
+
import { generateNonce } from "near-kit";
|
|
322
300
|
|
|
323
301
|
const usedNonces = new Set<string>();
|
|
324
302
|
|
|
@@ -326,27 +304,10 @@ export const auth = betterAuth({
|
|
|
326
304
|
plugins: [
|
|
327
305
|
siwn({
|
|
328
306
|
recipient: "myapp.com",
|
|
329
|
-
anonymous: false,
|
|
330
|
-
emailDomainName: "myapp.com",
|
|
331
307
|
requireFullAccessKey: false,
|
|
332
308
|
|
|
333
309
|
getNonce: async () => generateNonce(),
|
|
334
310
|
|
|
335
|
-
validateNonce: (nonce: Uint8Array) => {
|
|
336
|
-
const nonceHex = Array.from(nonce).map(b => b.toString(16).padStart(2, '0')).join('');
|
|
337
|
-
if (usedNonces.has(nonceHex)) return false;
|
|
338
|
-
usedNonces.add(nonceHex);
|
|
339
|
-
return true;
|
|
340
|
-
},
|
|
341
|
-
|
|
342
|
-
validateRecipient: (recipient: string) => {
|
|
343
|
-
return ["myapp.com", "staging.myapp.com"].includes(recipient);
|
|
344
|
-
},
|
|
345
|
-
|
|
346
|
-
validateMessage: (message: string) => {
|
|
347
|
-
return message.includes("Sign in to") && message.length > 10;
|
|
348
|
-
},
|
|
349
|
-
|
|
350
311
|
getProfile: async (accountId) => {
|
|
351
312
|
try {
|
|
352
313
|
const res = await fetch(`https://api.myapp.com/profiles/${accountId}`);
|
|
@@ -363,7 +324,7 @@ export const auth = betterAuth({
|
|
|
363
324
|
return recipient ? allowed.includes(recipient) : true;
|
|
364
325
|
},
|
|
365
326
|
|
|
366
|
-
|
|
327
|
+
apiKey: process.env.FASTNEAR_API_KEY,
|
|
367
328
|
|
|
368
329
|
relayer: {
|
|
369
330
|
accountId: "relayer.myapp.near",
|
|
@@ -389,7 +350,7 @@ The plugin detects the network from the account ID:
|
|
|
389
350
|
### NEP-413 Compliance
|
|
390
351
|
- Proper nonce handling prevents replay attacks
|
|
391
352
|
- Message format and recipient validation
|
|
392
|
-
- 15-minute server-side nonce expiration
|
|
353
|
+
- 15-minute server-side nonce expiration with DB replay detection
|
|
393
354
|
|
|
394
355
|
### Relayer Key Security
|
|
395
356
|
- Ephemeral private key encrypted at rest with AES-256-GCM
|
|
@@ -406,8 +367,6 @@ The plugin detects the network from the account ID:
|
|
|
406
367
|
|
|
407
368
|
| Issue | Solution |
|
|
408
369
|
|---|---|
|
|
409
|
-
| "Wallet not connected" | Call `requestSignIn.near()` before `signIn.near()` |
|
|
410
|
-
| "No valid nonce found" | Ensure `requestSignIn.near()` completed; client nonces expire after 5 min |
|
|
411
370
|
| "Invalid or expired nonce" | Server nonces expire after 15 min; check clock sync |
|
|
412
371
|
| "Account ID mismatch" | Verify signed message account ID matches wallet |
|
|
413
372
|
| "Network ID mismatch" | Ensure networkId matches the account's network |
|
|
@@ -454,7 +413,7 @@ pnpm test
|
|
|
454
413
|
- [NEAR Protocol](https://near.org)
|
|
455
414
|
- [NEP-413 Specification](https://github.com/near/NEPs/blob/master/neps/nep-0413.md)
|
|
456
415
|
- [NEP-366 Delegate Actions](https://github.com/near/NEPs/blob/master/neps/nep-0366.md)
|
|
457
|
-
- [
|
|
458
|
-
- [near-
|
|
416
|
+
- [near-kit](https://github.com/elliotBraem/near-kit)
|
|
417
|
+
- [@hot-labs/near-connect](https://github.com/azbang/near-connect)
|
|
459
418
|
- [Example Implementation](https://better-near-auth.near.page)
|
|
460
419
|
- [Contributing Guide](./CONTRIBUTING.md)
|
package/dist/client.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
type
|
|
1
|
+
import { TransactionBuilder } from "near-kit";
|
|
2
|
+
import type { Near as NearType } from "near-kit";
|
|
3
3
|
import type { BetterAuthClientPlugin, BetterAuthClientOptions, BetterFetch, BetterFetchResponse, ClientStore } from "better-auth/client";
|
|
4
4
|
import { atom } from "nanostores";
|
|
5
5
|
import type { siwn } from "./index.js";
|
|
6
|
-
import { type AccountId, type NonceRequestT, type NonceResponseT, type ProfileResponseT, type VerifyRequestT, type VerifyResponseT, type
|
|
6
|
+
import { type AccountId, type NonceRequestT, type NonceResponseT, type ProfileResponseT, type VerifyRequestT, type VerifyResponseT, type RelayResponseT, type RelayStatusResponseT, type NearAccount, type ViewContractRequestT, type ViewContractResponseT, type RelayerInfo, type RelayHistoryResponseT } from "./types.js";
|
|
7
7
|
export interface AuthCallbacks {
|
|
8
8
|
onSuccess?: () => void;
|
|
9
9
|
onError?: (error: Error & {
|
|
@@ -14,20 +14,13 @@ export interface AuthCallbacks {
|
|
|
14
14
|
export interface SIWNClientConfig {
|
|
15
15
|
recipient: string;
|
|
16
16
|
networkId?: "mainnet" | "testnet";
|
|
17
|
-
fastnearApiKey?: string;
|
|
18
|
-
}
|
|
19
|
-
export interface CachedNonceData {
|
|
20
|
-
nonce: string;
|
|
21
|
-
accountId: string;
|
|
22
|
-
publicKey?: string | null;
|
|
23
|
-
networkId: string;
|
|
24
|
-
timestamp: number;
|
|
25
17
|
}
|
|
26
18
|
export interface SIWNClientActions {
|
|
27
19
|
near: {
|
|
28
20
|
nonce: (params: NonceRequestT) => Promise<BetterFetchResponse<NonceResponseT>>;
|
|
29
21
|
verify: (params: VerifyRequestT) => Promise<BetterFetchResponse<VerifyResponseT>>;
|
|
30
22
|
getProfile: (accountId?: AccountId) => Promise<BetterFetchResponse<ProfileResponseT>>;
|
|
23
|
+
view: (params: ViewContractRequestT) => Promise<BetterFetchResponse<ViewContractResponseT>>;
|
|
31
24
|
getAccountId: () => string | null;
|
|
32
25
|
getState: () => {
|
|
33
26
|
accountId: string | null;
|
|
@@ -46,18 +39,16 @@ export interface SIWNClientActions {
|
|
|
46
39
|
listAccounts: () => Promise<BetterFetchResponse<{
|
|
47
40
|
accounts: NearAccount[];
|
|
48
41
|
}>>;
|
|
49
|
-
buildSignedDelegateAction: (
|
|
50
|
-
receiverId: string;
|
|
51
|
-
actions: NearActionInput[];
|
|
52
|
-
}) => Promise<string>;
|
|
42
|
+
buildSignedDelegateAction: (receiverId: string, buildActions: (builder: TransactionBuilder, receiverId: string) => TransactionBuilder) => Promise<string>;
|
|
53
43
|
relayTransaction: (params: {
|
|
54
|
-
|
|
44
|
+
payload: string;
|
|
55
45
|
}) => Promise<BetterFetchResponse<RelayResponseT>>;
|
|
56
46
|
getRelayStatus: (txHash: string) => Promise<BetterFetchResponse<RelayStatusResponseT>>;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
47
|
+
getRelayerInfo: () => Promise<BetterFetchResponse<RelayerInfo & {
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
}>>;
|
|
50
|
+
relayHistory: () => Promise<BetterFetchResponse<RelayHistoryResponseT>>;
|
|
51
|
+
client: NearType;
|
|
61
52
|
};
|
|
62
53
|
signIn: {
|
|
63
54
|
near: (callbacks?: AuthCallbacks) => Promise<void>;
|
|
@@ -72,10 +63,8 @@ export interface SIWNClientPlugin extends BetterAuthClientPlugin {
|
|
|
72
63
|
publicKey: string | null;
|
|
73
64
|
networkId: string;
|
|
74
65
|
} | null>>;
|
|
75
|
-
cachedNonce: ReturnType<typeof atom<CachedNonceData | null>>;
|
|
76
66
|
};
|
|
77
67
|
getActions: ($fetch: BetterFetch, $store: ClientStore, options: BetterAuthClientOptions | undefined) => SIWNClientActions;
|
|
78
68
|
}
|
|
79
69
|
export declare const siwnClient: (config: SIWNClientConfig) => SIWNClientPlugin;
|
|
80
|
-
export {};
|
|
81
70
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|