better-near-auth 0.1.0 → 0.1.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Elliot Braem
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
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 [fastintear](https://github.com/elliotBraem/fastintear), and provides a complete drop-in solution with secure defaults and automatic profile integration.
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 [fastintear](https://github.com/elliotBraem/fastintear), and provides a complete drop-in solution with session management, secure defaults, and automatic profile integration.
17
17
 
18
18
  ## Installation
19
19
 
@@ -30,6 +30,9 @@ npm install better-near-auth
30
30
  import { siwn } from "better-near-auth";
31
31
 
32
32
  export const auth = betterAuth({
33
+ database: drizzleAdapter(db, {
34
+ // db configuration
35
+ }),
33
36
  plugins: [
34
37
  siwn({
35
38
  recipient: "myapp.com",
@@ -39,7 +42,7 @@ npm install better-near-auth
39
42
  });
40
43
  ```
41
44
 
42
- 3. Migrate the database. Run the migration or generate the schema to add the necessary fields and tables to the database.
45
+ 3. Generate the schema to add the necessary fields and tables to the database.
43
46
 
44
47
  ```bash
45
48
  npx @better-auth/cli generate
@@ -85,11 +88,11 @@ Access user profiles from NEAR Social automatically:
85
88
 
86
89
  ```ts title="profile-usage.ts"
87
90
  // Get current user's profile (requires authentication)
88
- const { data: myProfile } = await authClient.near.getProfile();
91
+ const myProfile = await authClient.near.getProfile();
89
92
  console.log("My profile:", myProfile);
90
93
 
91
94
  // Get specific user's profile (no auth required)
92
- const { data: aliceProfile } = await authClient.near.getProfile("alice.near");
95
+ const aliceProfile = await authClient.near.getProfile("alice.near");
93
96
  console.log("Alice's profile:", aliceProfile);
94
97
  ```
95
98
 
@@ -108,7 +111,7 @@ The SIWN plugin accepts the following configuration options:
108
111
  * **validateRecipient**: Function to validate recipients. Optional, uses exact match by default
109
112
  * **validateMessage**: Function to validate messages. Optional, no validation by default
110
113
  * **getProfile**: Function to fetch user profiles. Optional, uses NEAR Social by default
111
- * **validateFunctionCallKey**: Function to validate function call access keys when `requireFullAccessKey` is false
114
+ * **validateLimitedAccessKey**: Function to validate function call access keys when `requireFullAccessKey` is false
112
115
 
113
116
  ### Client Options
114
117
 
@@ -163,7 +166,6 @@ export const auth = betterAuth({
163
166
  // Optional: Custom profile lookup
164
167
  getProfile: async (accountId) => {
165
168
  // Custom profile logic, falls back to NEAR Social
166
- return null; // Use default NEAR Social lookup
167
169
  },
168
170
  }),
169
171
  ],
@@ -293,7 +295,7 @@ export const auth = betterAuth({
293
295
  },
294
296
 
295
297
  // Validate function call keys against allowed contracts
296
- validateFunctionCallKey: async ({ accountId, publicKey, contractId }) => {
298
+ validateLimitedAccessKey: async ({ accountId, publicKey, contractId }) => {
297
299
  const allowedContracts = ["myapp.near", "social.near"];
298
300
  return contractId ? allowedContracts.includes(contractId) : true;
299
301
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-near-auth",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Sign in with NEAR (SIWN) plugin for Better Auth",
5
5
  "main": "index.ts",
6
6
  "module": "index.ts",
@@ -45,7 +45,7 @@
45
45
  "dependencies": {
46
46
  "@better-auth/utils": "^0.2.6",
47
47
  "@fastnear/utils": "^0.9.7",
48
- "fastintear": "link:fastintear",
48
+ "fastintear": "^0.2.3",
49
49
  "near-sign-verify": "^0.4.3",
50
50
  "zod": "^4.0.17"
51
51
  },
package/src/client.ts CHANGED
@@ -3,6 +3,7 @@ import type { BetterAuthClientPlugin, BetterFetchOption, BetterFetchResponse } f
3
3
  import { sign, type WalletInterface } from "near-sign-verify";
4
4
  import type { siwn } from ".";
5
5
  import { type AccountId, type NonceRequestT, type NonceResponseT, type ProfileResponseT, type VerifyRequestT, type VerifyResponseT } from "./types";
6
+ import type { User } from "better-auth";
6
7
 
7
8
  export interface Signer {
8
9
  accountId(): string | null;
@@ -25,7 +26,7 @@ export interface SIWNClientActions {
25
26
  getProfile: (accountId?: AccountId) => Promise<BetterFetchResponse<ProfileResponseT>>;
26
27
  };
27
28
  signIn: {
28
- near: (params: { recipient: string, signer: Signer }, callbacks?: AuthCallbacks) => Promise<VerifyResponseT>;
29
+ near: (params: { recipient: string, signer: Signer }, callbacks?: AuthCallbacks) => Promise<void>;
29
30
  };
30
31
  }
31
32
 
@@ -66,7 +67,10 @@ export const siwnClient = (config: SIWNClientConfig): SIWNClientPlugin => {
66
67
  },
67
68
  },
68
69
  signIn: {
69
- near: async (params: { recipient: string, signer: Signer }, callbacks?: AuthCallbacks): Promise<VerifyResponseT> => {
70
+ near: async (
71
+ params: { recipient: string, signer: Signer },
72
+ callbacks?: AuthCallbacks
73
+ ): Promise<void> => {
70
74
  try {
71
75
  const { signer, recipient } = params;
72
76
 
@@ -74,32 +78,30 @@ export const siwnClient = (config: SIWNClientConfig): SIWNClientPlugin => {
74
78
  throw new Error("NEAR signer not available");
75
79
  }
76
80
 
77
- // Must be already connected
78
81
  const accountId = signer.accountId();
79
82
  if (!accountId) {
80
83
  throw new Error("Wallet not connected. Please connect your wallet first.");
81
84
  }
82
85
 
83
- // Get nonce for signature
84
86
  const nonceResponse: BetterFetchResponse<NonceResponseT> = await $fetch("/near/nonce", {
85
87
  method: "POST",
86
88
  body: { accountId }
87
89
  });
88
90
 
91
+ if (nonceResponse.error) {
92
+ throw new Error(nonceResponse.error.message || "Failed to get nonce");
93
+ }
94
+
89
95
  const nonce = nonceResponse?.data?.nonce;
90
96
  const message = `Sign in to ${recipient}\n\nAccount ID: ${accountId}\nNonce: ${nonce}`;
91
-
92
- // Convert base64 nonce to Uint8Array for signing
93
97
  const nonceBytes = base64ToBytes(nonce!);
94
98
 
95
- // Sign message
96
99
  const authToken = await sign(message, {
97
100
  signer,
98
101
  recipient,
99
102
  nonce: nonceBytes,
100
103
  });
101
104
 
102
- // Verify signature with backend
103
105
  const verifyResponse: BetterFetchResponse<VerifyResponseT> = await $fetch("/near/verify", {
104
106
  method: "POST",
105
107
  body: {
@@ -108,17 +110,18 @@ export const siwnClient = (config: SIWNClientConfig): SIWNClientPlugin => {
108
110
  }
109
111
  });
110
112
 
113
+ if (verifyResponse.error) {
114
+ throw new Error(verifyResponse.error.message || "Failed to verify signature");
115
+ }
116
+
111
117
  if (!verifyResponse?.data?.success) {
112
118
  throw new Error("Authentication verification failed");
113
119
  }
114
120
 
115
121
  callbacks?.onSuccess?.();
116
- return verifyResponse.data;
117
-
118
122
  } catch (error) {
119
123
  const err = error instanceof Error ? error : new Error(String(error));
120
124
  callbacks?.onError?.(err);
121
- throw err;
122
125
  }
123
126
  }
124
127
  }
package/src/index.ts CHANGED
@@ -38,10 +38,10 @@ export type SIWNPluginOptions =
38
38
  validateRecipient?: (recipient: string) => boolean;
39
39
  validateMessage?: (message: string) => boolean;
40
40
  getProfile?: (accountId: AccountId) => Promise<Profile | null>;
41
- validateFunctionCallKey?: (args: {
41
+ validateLimitedAccessKey?: (args: {
42
42
  accountId: AccountId;
43
43
  publicKey: string;
44
- contractId?: string;
44
+ recipient?: string;
45
45
  }) => Promise<boolean>;
46
46
  }
47
47
  | {
@@ -54,10 +54,10 @@ export type SIWNPluginOptions =
54
54
  validateRecipient?: (recipient: string) => boolean;
55
55
  validateMessage?: (message: string) => boolean;
56
56
  getProfile?: (accountId: AccountId) => Promise<Profile | null>;
57
- validateFunctionCallKey?: (args: {
57
+ validateLimitedAccessKey?: (args: {
58
58
  accountId: AccountId;
59
59
  publicKey: string;
60
- contractId?: string;
60
+ recipient: string;
61
61
  }) => Promise<boolean>;
62
62
  };
63
63
 
@@ -194,13 +194,14 @@ export const siwn = (options: SIWNPluginOptions) =>
194
194
  });
195
195
  }
196
196
 
197
- if (!options.requireFullAccessKey && options.validateFunctionCallKey) {
198
- const isValidFunctionKey = await options.validateFunctionCallKey({
197
+ if (!options.requireFullAccessKey && options.validateLimitedAccessKey) {
198
+ const isValidKey = await options.validateLimitedAccessKey({
199
199
  accountId: result.accountId,
200
200
  publicKey: result.publicKey,
201
- }); // we can validate against an access control contract
201
+ recipient: options.recipient
202
+ }); // we could validate against some access control contract
202
203
 
203
- if (!isValidFunctionKey) {
204
+ if (!isValidKey) {
204
205
  throw new APIError("UNAUTHORIZED", {
205
206
  message: "Unauthorized: Invalid function call access key",
206
207
  status: 401,
package/src/near.test.ts CHANGED
@@ -156,7 +156,7 @@
156
156
  // async verifyMessage({ authToken, expectedRecipient, accountId }) {
157
157
  // return authToken === "valid_token" && expectedRecipient === domain;
158
158
  // },
159
- // async validateFunctionCallKey({ accountId, publicKey }) {
159
+ // async validateLimitedAccessKey({ accountId, publicKey }) {
160
160
  // return accountId === "test.near" && publicKey !== "";
161
161
  // },
162
162
  // }),