better-near-auth 0.2.4 → 0.3.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 +6 -5
- package/package.json +4 -3
- package/src/client.ts +174 -86
- package/src/index.ts +7 -4
- package/src/utils.ts +13 -0
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
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) and [
|
|
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 [Hot Connect](https://github.com/azbang/hot-connector) to provide a complete drop-in solution with session management, secure defaults, and automatic profile integration.
|
|
17
17
|
|
|
18
18
|
## Installation
|
|
19
19
|
|
|
@@ -112,7 +112,7 @@ export function LoginButton() {
|
|
|
112
112
|
const [isConnectingWallet, setIsConnectingWallet] = useState(false);
|
|
113
113
|
const [isSigningIn, setIsSigningIn] = useState(false);
|
|
114
114
|
|
|
115
|
-
// Get account ID from
|
|
115
|
+
// Get account ID from near-kit client
|
|
116
116
|
const accountId = authClient.near.getAccountId();
|
|
117
117
|
|
|
118
118
|
if (session) {
|
|
@@ -277,7 +277,7 @@ The client plugin provides the following actions:
|
|
|
277
277
|
- `nonce(params)` - Request a nonce from the server
|
|
278
278
|
- `verify(params)` - Verify an auth token with the server
|
|
279
279
|
- `getProfile(accountId?)` - Get user profile from NEAR Social
|
|
280
|
-
- `getNearClient()` - Get the
|
|
280
|
+
- `getNearClient()` - Get the near-kit client instance
|
|
281
281
|
- `getAccountId()` - Get the currently connected account ID
|
|
282
282
|
- `disconnect()` - Disconnect wallet and clear cached data
|
|
283
283
|
|
|
@@ -427,7 +427,7 @@ export const authClient = createAuthClient({
|
|
|
427
427
|
|
|
428
428
|
1. **"Wallet not connected"**
|
|
429
429
|
- You must call `requestSignIn.near()` before `signIn.near()`
|
|
430
|
-
- Check that the
|
|
430
|
+
- Check that the near-kit client is properly initialized
|
|
431
431
|
|
|
432
432
|
2. **"No valid nonce found"**
|
|
433
433
|
- Ensure `requestSignIn.near()` completed successfully before calling `signIn.near()`
|
|
@@ -452,5 +452,6 @@ export const authClient = createAuthClient({
|
|
|
452
452
|
* [NEAR Protocol](https://near.org)
|
|
453
453
|
* [NEP-413 Specification](https://github.com/near/NEPs/blob/master/neps/nep-0413.md)
|
|
454
454
|
* [near-sign-verify](https://github.com/elliotBraem/near-sign-verify)
|
|
455
|
-
* [
|
|
455
|
+
* [near-kit](https://kit.near.tools/)
|
|
456
|
+
* [Hot Connect](https://github.com/azbang/hot-connector)
|
|
456
457
|
* [Example Implementation](https://better-near-auth.near.page)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-near-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Sign in with NEAR (SIWN) plugin for Better Auth",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"module": "src/index.ts",
|
|
@@ -47,15 +47,16 @@
|
|
|
47
47
|
"typescript": "^5.7.0"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"
|
|
50
|
+
"@hot-labs/near-connect": "^latest",
|
|
51
51
|
"nanostores": "^1.0.1",
|
|
52
|
+
"near-kit": "^latest",
|
|
52
53
|
"near-sign-verify": "^0.4.5",
|
|
53
54
|
"zod": "^4.1.12"
|
|
54
55
|
},
|
|
55
56
|
"devDependencies": {
|
|
56
57
|
"@types/bun": "latest",
|
|
57
58
|
"@types/node": "^24.9.2",
|
|
58
|
-
"better-auth": "^1.
|
|
59
|
+
"better-auth": "^1.4.4",
|
|
59
60
|
"typescript": "^5.9.3",
|
|
60
61
|
"vitest": "^3.2.4"
|
|
61
62
|
},
|
package/src/client.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { NearConnector } from "@hot-labs/near-connect";
|
|
2
|
+
import type { Account, NearWalletBase } from "@hot-labs/near-connect/build/types/wallet";
|
|
1
3
|
import type { BetterAuthClientPlugin, BetterFetch, BetterFetchOption, BetterFetchResponse } from "better-auth/client";
|
|
2
|
-
import { createNearClient } from "fastintear";
|
|
3
|
-
import { base64ToBytes } from "fastintear/utils";
|
|
4
4
|
import { atom } from "nanostores";
|
|
5
|
-
import {
|
|
5
|
+
import { Near, fromHotConnect } from "near-kit";
|
|
6
|
+
import { sign } from "near-sign-verify";
|
|
6
7
|
import type { siwn } from ".";
|
|
7
8
|
import { type AccountId, type NonceRequestT, type NonceResponseT, type ProfileResponseT, type VerifyRequestT, type VerifyResponseT } from "./types";
|
|
9
|
+
import { base64ToBytes } from "./utils";
|
|
8
10
|
|
|
9
11
|
export interface AuthCallbacks {
|
|
10
12
|
onSuccess?: () => void;
|
|
@@ -30,7 +32,7 @@ export interface SIWNClientActions {
|
|
|
30
32
|
nonce: (params: NonceRequestT) => Promise<BetterFetchResponse<NonceResponseT>>;
|
|
31
33
|
verify: (params: VerifyRequestT) => Promise<BetterFetchResponse<VerifyResponseT>>;
|
|
32
34
|
getProfile: (accountId?: AccountId) => Promise<BetterFetchResponse<ProfileResponseT>>;
|
|
33
|
-
getNearClient: () =>
|
|
35
|
+
getNearClient: () => Near;
|
|
34
36
|
getAccountId: () => string | null;
|
|
35
37
|
getState: () => { accountId: string | null; publicKey: string | null; networkId: string } | null;
|
|
36
38
|
disconnect: () => Promise<void>;
|
|
@@ -56,10 +58,24 @@ export interface SIWNClientPlugin extends BetterAuthClientPlugin {
|
|
|
56
58
|
getActions: ($fetch: BetterFetch) => SIWNClientActions;
|
|
57
59
|
}
|
|
58
60
|
|
|
61
|
+
|
|
59
62
|
export const siwnClient = (config: SIWNClientConfig): SIWNClientPlugin => {
|
|
60
63
|
const cachedNonce = atom<CachedNonceData | null>(null);
|
|
61
64
|
const nearState = atom<{ accountId: string | null; publicKey: string | null; networkId: string } | null>(null);
|
|
62
65
|
|
|
66
|
+
// Initialize Hot Connect connector
|
|
67
|
+
const network = config.networkId || "mainnet";
|
|
68
|
+
const connector = new NearConnector({ network });
|
|
69
|
+
|
|
70
|
+
// Public Near instance for read-only access
|
|
71
|
+
const publicNear = new Near({ network });
|
|
72
|
+
|
|
73
|
+
// Near instance will be created after wallet connection
|
|
74
|
+
let nearInstance: Near | null = null;
|
|
75
|
+
let connectionPromise: Promise<void> | null = null;
|
|
76
|
+
let connectionResolve: (() => void) | null = null;
|
|
77
|
+
let connectionReject: ((error: Error) => void) | null = null;
|
|
78
|
+
|
|
63
79
|
const clearNonce = () => {
|
|
64
80
|
cachedNonce.set(null);
|
|
65
81
|
};
|
|
@@ -71,6 +87,66 @@ export const siwnClient = (config: SIWNClientConfig): SIWNClientPlugin => {
|
|
|
71
87
|
return (now - nonceData.timestamp) < fiveMinutes;
|
|
72
88
|
};
|
|
73
89
|
|
|
90
|
+
const handleAccountConnection = async (accounts: Account[]) => {
|
|
91
|
+
try {
|
|
92
|
+
const accountId = accounts?.[0]?.accountId;
|
|
93
|
+
if (!accountId) return;
|
|
94
|
+
|
|
95
|
+
// Create Near instance with Hot Connect wallet adapter
|
|
96
|
+
nearInstance = new Near({
|
|
97
|
+
network,
|
|
98
|
+
wallet: fromHotConnect(connector),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Update state with account info
|
|
102
|
+
nearState.set({
|
|
103
|
+
accountId,
|
|
104
|
+
publicKey: accounts?.[0]?.publicKey || null,
|
|
105
|
+
networkId: network
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Resolve connection promise if it exists
|
|
109
|
+
if (connectionResolve) {
|
|
110
|
+
connectionResolve();
|
|
111
|
+
connectionResolve = null;
|
|
112
|
+
connectionReject = null;
|
|
113
|
+
}
|
|
114
|
+
} catch (error) {
|
|
115
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
116
|
+
if (connectionReject) {
|
|
117
|
+
connectionReject(err);
|
|
118
|
+
connectionResolve = null;
|
|
119
|
+
connectionReject = null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Check for existing connection immediately
|
|
125
|
+
connector.getConnectedWallet().then((result: {
|
|
126
|
+
wallet: NearWalletBase;
|
|
127
|
+
accounts: Account[];
|
|
128
|
+
}) => {
|
|
129
|
+
if (result && result.accounts && result.accounts.length > 0) {
|
|
130
|
+
handleAccountConnection(result.accounts);
|
|
131
|
+
}
|
|
132
|
+
}).catch(() => {
|
|
133
|
+
// Ignore errors on initial check
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Set up event listeners for Hot Connect
|
|
137
|
+
// Per documentation: connector.on("wallet:signIn", async (t) => { const address = t.accounts[0].accountId; })
|
|
138
|
+
connector.on("wallet:signIn", async (data) => {
|
|
139
|
+
if (data?.accounts) {
|
|
140
|
+
await handleAccountConnection(data.accounts);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
connector.on("wallet:signOut", () => {
|
|
145
|
+
nearInstance = null;
|
|
146
|
+
nearState.set(null);
|
|
147
|
+
clearNonce();
|
|
148
|
+
});
|
|
149
|
+
|
|
74
150
|
return {
|
|
75
151
|
id: "siwn",
|
|
76
152
|
$InferServerPlugin: {} as ReturnType<typeof siwn>,
|
|
@@ -81,25 +157,6 @@ export const siwnClient = (config: SIWNClientConfig): SIWNClientPlugin => {
|
|
|
81
157
|
}),
|
|
82
158
|
|
|
83
159
|
getActions: ($fetch): SIWNClientActions => {
|
|
84
|
-
const nearClient = createNearClient({
|
|
85
|
-
networkId: config.networkId || "mainnet",
|
|
86
|
-
callbacks: {
|
|
87
|
-
onConnect: (accountData) => {
|
|
88
|
-
// Update nearState atom when wallet connects
|
|
89
|
-
nearState.set({
|
|
90
|
-
accountId: accountData.accountId,
|
|
91
|
-
publicKey: accountData.publicKey,
|
|
92
|
-
networkId: config.networkId || "mainnet"
|
|
93
|
-
});
|
|
94
|
-
},
|
|
95
|
-
onDisconnect: () => {
|
|
96
|
-
// Clear nearState atom when wallet disconnects
|
|
97
|
-
nearState.set(null);
|
|
98
|
-
clearNonce();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
160
|
return {
|
|
104
161
|
near: {
|
|
105
162
|
nonce: async (params: NonceRequestT, fetchOptions?: BetterFetchOption): Promise<BetterFetchResponse<NonceResponseT>> => {
|
|
@@ -123,11 +180,19 @@ export const siwnClient = (config: SIWNClientConfig): SIWNClientPlugin => {
|
|
|
123
180
|
...fetchOptions
|
|
124
181
|
});
|
|
125
182
|
},
|
|
126
|
-
getNearClient: () =>
|
|
127
|
-
|
|
183
|
+
getNearClient: () => {
|
|
184
|
+
return nearInstance || publicNear;
|
|
185
|
+
},
|
|
186
|
+
getAccountId: () => {
|
|
187
|
+
const state = nearState.get();
|
|
188
|
+
return state?.accountId || null;
|
|
189
|
+
},
|
|
128
190
|
getState: () => nearState.get(),
|
|
129
191
|
disconnect: async () => {
|
|
130
|
-
|
|
192
|
+
if (connector) {
|
|
193
|
+
await connector.disconnect();
|
|
194
|
+
}
|
|
195
|
+
nearInstance = null;
|
|
131
196
|
clearNonce();
|
|
132
197
|
nearState.set(null);
|
|
133
198
|
},
|
|
@@ -138,13 +203,14 @@ export const siwnClient = (config: SIWNClientConfig): SIWNClientPlugin => {
|
|
|
138
203
|
try {
|
|
139
204
|
const { recipient } = params;
|
|
140
205
|
|
|
141
|
-
if (!
|
|
206
|
+
if (!nearInstance) {
|
|
142
207
|
const error = new Error("NEAR client not available") as Error & { code?: string };
|
|
143
208
|
error.code = "SIGNER_NOT_AVAILABLE";
|
|
144
209
|
throw error;
|
|
145
210
|
}
|
|
146
211
|
|
|
147
|
-
const
|
|
212
|
+
const state = nearState.get();
|
|
213
|
+
const accountId = state?.accountId;
|
|
148
214
|
if (!accountId) {
|
|
149
215
|
const error = new Error("Wallet not connected. Please connect your wallet first.") as Error & { code?: string };
|
|
150
216
|
error.code = "WALLET_NOT_CONNECTED";
|
|
@@ -152,11 +218,10 @@ export const siwnClient = (config: SIWNClientConfig): SIWNClientPlugin => {
|
|
|
152
218
|
}
|
|
153
219
|
|
|
154
220
|
// Get nonce first
|
|
155
|
-
const state = nearState.get();
|
|
156
221
|
const nonceRequest: NonceRequestT = {
|
|
157
222
|
accountId,
|
|
158
|
-
publicKey: state
|
|
159
|
-
networkId: (state
|
|
223
|
+
publicKey: state.publicKey || "",
|
|
224
|
+
networkId: (state.networkId || network) as "mainnet" | "testnet"
|
|
160
225
|
};
|
|
161
226
|
|
|
162
227
|
const nonceResponse: BetterFetchResponse<NonceResponseT> = await $fetch("/near/nonce", {
|
|
@@ -177,13 +242,28 @@ export const siwnClient = (config: SIWNClientConfig): SIWNClientPlugin => {
|
|
|
177
242
|
const message = `Sign in to ${recipient}\n\nAccount ID: ${accountId}\nNonce: ${nonce}`;
|
|
178
243
|
const nonceBytes = base64ToBytes(nonce);
|
|
179
244
|
|
|
180
|
-
// Sign the message
|
|
245
|
+
// Sign the message using near-sign-verify
|
|
181
246
|
const authToken = await sign(message, {
|
|
182
|
-
signer:
|
|
247
|
+
signer: nearInstance,
|
|
183
248
|
recipient,
|
|
184
249
|
nonce: nonceBytes,
|
|
185
250
|
});
|
|
186
251
|
|
|
252
|
+
// Update state with publicKey from signed message if not already set
|
|
253
|
+
if (!state.publicKey) {
|
|
254
|
+
try {
|
|
255
|
+
const parsedToken = JSON.parse(authToken);
|
|
256
|
+
if (parsedToken.publicKey) {
|
|
257
|
+
nearState.set({
|
|
258
|
+
...state,
|
|
259
|
+
publicKey: parsedToken.publicKey,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
} catch (e) {
|
|
263
|
+
// Ignore
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
187
267
|
// Link the account (instead of verify)
|
|
188
268
|
const linkResponse: BetterFetchResponse<any> = await $fetch("/near/link-account", {
|
|
189
269
|
method: "POST",
|
|
@@ -229,60 +309,68 @@ export const siwnClient = (config: SIWNClientConfig): SIWNClientPlugin => {
|
|
|
229
309
|
try {
|
|
230
310
|
const { recipient } = params;
|
|
231
311
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
312
|
+
clearNonce();
|
|
313
|
+
|
|
314
|
+
// Create a promise to wait for wallet connection
|
|
315
|
+
connectionPromise = new Promise<void>((resolve, reject) => {
|
|
316
|
+
connectionResolve = resolve;
|
|
317
|
+
connectionReject = reject;
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Trigger Hot Connect modal
|
|
321
|
+
await connector.connect();
|
|
322
|
+
|
|
323
|
+
// Wait for wallet:signIn event
|
|
324
|
+
await connectionPromise;
|
|
325
|
+
|
|
326
|
+
// After connection, get account info and request nonce
|
|
327
|
+
const state = nearState.get();
|
|
328
|
+
if (!state || !state.accountId) {
|
|
329
|
+
throw new Error("Failed to get account information after wallet connection");
|
|
236
330
|
}
|
|
237
331
|
|
|
238
|
-
|
|
332
|
+
const { accountId, networkId, publicKey } = state;
|
|
239
333
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const nonceRequest: NonceRequestT = {
|
|
244
|
-
accountId,
|
|
245
|
-
publicKey,
|
|
246
|
-
networkId: networkId as "mainnet" | "testnet"
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
const nonceResponse: BetterFetchResponse<NonceResponseT> = await $fetch("/near/nonce", {
|
|
250
|
-
method: "POST",
|
|
251
|
-
body: nonceRequest
|
|
252
|
-
});
|
|
334
|
+
if (!publicKey) {
|
|
335
|
+
throw new Error("Failed to get public key from wallet");
|
|
336
|
+
}
|
|
253
337
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const nonce = nonceResponse?.data?.nonce;
|
|
259
|
-
if (!nonce) {
|
|
260
|
-
throw new Error("No nonce received from server");
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Cache nonce with all wallet data
|
|
264
|
-
const cachedData: CachedNonceData = {
|
|
265
|
-
nonce,
|
|
266
|
-
accountId,
|
|
267
|
-
publicKey,
|
|
268
|
-
networkId,
|
|
269
|
-
timestamp: Date.now()
|
|
270
|
-
};
|
|
271
|
-
cachedNonce.set(cachedData);
|
|
272
|
-
|
|
273
|
-
callbacks?.onSuccess?.();
|
|
274
|
-
} catch (error) {
|
|
275
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
276
|
-
clearNonce();
|
|
277
|
-
callbacks?.onError?.(err);
|
|
278
|
-
}
|
|
279
|
-
},
|
|
280
|
-
onError: (error: any) => {
|
|
281
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
282
|
-
clearNonce();
|
|
283
|
-
callbacks?.onError?.(err);
|
|
284
|
-
}
|
|
338
|
+
nearState.set({
|
|
339
|
+
...state,
|
|
340
|
+
publicKey,
|
|
285
341
|
});
|
|
342
|
+
|
|
343
|
+
const nonceRequest: NonceRequestT = {
|
|
344
|
+
accountId,
|
|
345
|
+
publicKey: publicKey,
|
|
346
|
+
networkId: networkId as "mainnet" | "testnet"
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const nonceResponse: BetterFetchResponse<NonceResponseT> = await $fetch("/near/nonce", {
|
|
350
|
+
method: "POST",
|
|
351
|
+
body: nonceRequest
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
if (nonceResponse.error) {
|
|
355
|
+
throw new Error(nonceResponse.error.message || "Failed to get nonce");
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const nonce = nonceResponse?.data?.nonce;
|
|
359
|
+
if (!nonce) {
|
|
360
|
+
throw new Error("No nonce received from server");
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Cache nonce with all wallet data
|
|
364
|
+
const cachedData: CachedNonceData = {
|
|
365
|
+
nonce,
|
|
366
|
+
accountId,
|
|
367
|
+
publicKey,
|
|
368
|
+
networkId,
|
|
369
|
+
timestamp: Date.now()
|
|
370
|
+
};
|
|
371
|
+
cachedNonce.set(cachedData);
|
|
372
|
+
|
|
373
|
+
callbacks?.onSuccess?.();
|
|
286
374
|
} catch (error) {
|
|
287
375
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
288
376
|
clearNonce();
|
|
@@ -298,13 +386,14 @@ export const siwnClient = (config: SIWNClientConfig): SIWNClientPlugin => {
|
|
|
298
386
|
try {
|
|
299
387
|
const { recipient } = params;
|
|
300
388
|
|
|
301
|
-
if (!
|
|
389
|
+
if (!nearInstance) {
|
|
302
390
|
const error = new Error("NEAR client not available") as Error & { code?: string };
|
|
303
391
|
error.code = "SIGNER_NOT_AVAILABLE";
|
|
304
392
|
throw error;
|
|
305
393
|
}
|
|
306
394
|
|
|
307
|
-
const
|
|
395
|
+
const state = nearState.get();
|
|
396
|
+
const accountId = state?.accountId;
|
|
308
397
|
if (!accountId) {
|
|
309
398
|
const error = new Error("Wallet not connected. Please connect your wallet first.") as Error & { code?: string };
|
|
310
399
|
error.code = "WALLET_NOT_CONNECTED";
|
|
@@ -335,12 +424,11 @@ export const siwnClient = (config: SIWNClientConfig): SIWNClientPlugin => {
|
|
|
335
424
|
|
|
336
425
|
// Sign the message
|
|
337
426
|
const authToken = await sign(message, {
|
|
338
|
-
signer:
|
|
427
|
+
signer: nearInstance,
|
|
339
428
|
recipient,
|
|
340
429
|
nonce: nonceBytes,
|
|
341
430
|
});
|
|
342
431
|
|
|
343
|
-
// Verify the signature with the server
|
|
344
432
|
const verifyResponse: BetterFetchResponse<VerifyResponseT> = await $fetch("/near/verify", {
|
|
345
433
|
method: "POST",
|
|
346
434
|
body: {
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { APIError, createAuthEndpoint, createAuthMiddleware, sessionMiddleware } from "better-auth/api";
|
|
2
2
|
import { setSessionCookie } from "better-auth/cookies";
|
|
3
3
|
import type { Account, BetterAuthPlugin, User } from "better-auth/types";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { generateNonce, parseAuthToken, verify, type VerificationResult, type VerifyOptions } from "near-sign-verify";
|
|
5
|
+
import { bytesToBase64 } from "./utils";
|
|
6
6
|
import z from "zod";
|
|
7
7
|
import { defaultGetProfile, getImageUrl, getNetworkFromAccountId } from "./profile";
|
|
8
8
|
import { schema } from "./schema";
|
|
@@ -464,6 +464,7 @@ export const siwn = (options: SIWNPluginOptions) =>
|
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
try {
|
|
467
|
+
console.log("in server authToken", parseAuthToken(authToken));
|
|
467
468
|
const verification =
|
|
468
469
|
await ctx.context.internalAdapter.findVerificationValue(
|
|
469
470
|
`siwn:${accountId}:${network}`,
|
|
@@ -622,8 +623,8 @@ export const siwn = (options: SIWNPluginOptions) =>
|
|
|
622
623
|
}
|
|
623
624
|
|
|
624
625
|
const session = await ctx.context.internalAdapter.createSession(
|
|
625
|
-
user.id
|
|
626
|
-
ctx,
|
|
626
|
+
user.id
|
|
627
|
+
// ctx,
|
|
627
628
|
);
|
|
628
629
|
|
|
629
630
|
if (!session) {
|
|
@@ -645,6 +646,8 @@ export const siwn = (options: SIWNPluginOptions) =>
|
|
|
645
646
|
},
|
|
646
647
|
}));
|
|
647
648
|
} catch (error: unknown) {
|
|
649
|
+
console.log("server authToken", authToken);
|
|
650
|
+
console.log("server parsed authToken", parseAuthToken(authToken));
|
|
648
651
|
if (error instanceof APIError) throw error;
|
|
649
652
|
throw new APIError("UNAUTHORIZED", {
|
|
650
653
|
message: "Something went wrong. Please try again later.",
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for base64 encoding/decoding
|
|
3
|
+
* Replaces fastintear/utils functions (now using standard browser APIs)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export function bytesToBase64(bytes: Uint8Array): string {
|
|
7
|
+
return btoa(String.fromCharCode(...bytes));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function base64ToBytes(base64: string): Uint8Array {
|
|
11
|
+
return Uint8Array.from(atob(base64), c => c.charCodeAt(0));
|
|
12
|
+
}
|
|
13
|
+
|