better-near-auth 0.1.1 → 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/README.md +2 -3
- package/package.json +1 -1
- package/src/client.ts +14 -11
- package/src/index.ts +9 -8
- package/src/near.test.ts +1 -1
package/README.md
CHANGED
|
@@ -111,7 +111,7 @@ The SIWN plugin accepts the following configuration options:
|
|
|
111
111
|
* **validateRecipient**: Function to validate recipients. Optional, uses exact match by default
|
|
112
112
|
* **validateMessage**: Function to validate messages. Optional, no validation by default
|
|
113
113
|
* **getProfile**: Function to fetch user profiles. Optional, uses NEAR Social by default
|
|
114
|
-
* **
|
|
114
|
+
* **validateLimitedAccessKey**: Function to validate function call access keys when `requireFullAccessKey` is false
|
|
115
115
|
|
|
116
116
|
### Client Options
|
|
117
117
|
|
|
@@ -166,7 +166,6 @@ export const auth = betterAuth({
|
|
|
166
166
|
// Optional: Custom profile lookup
|
|
167
167
|
getProfile: async (accountId) => {
|
|
168
168
|
// Custom profile logic, falls back to NEAR Social
|
|
169
|
-
return null; // Use default NEAR Social lookup
|
|
170
169
|
},
|
|
171
170
|
}),
|
|
172
171
|
],
|
|
@@ -296,7 +295,7 @@ export const auth = betterAuth({
|
|
|
296
295
|
},
|
|
297
296
|
|
|
298
297
|
// Validate function call keys against allowed contracts
|
|
299
|
-
|
|
298
|
+
validateLimitedAccessKey: async ({ accountId, publicKey, contractId }) => {
|
|
300
299
|
const allowedContracts = ["myapp.near", "social.near"];
|
|
301
300
|
return contractId ? allowedContracts.includes(contractId) : true;
|
|
302
301
|
},
|
package/package.json
CHANGED
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<
|
|
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 (
|
|
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
|
-
|
|
41
|
+
validateLimitedAccessKey?: (args: {
|
|
42
42
|
accountId: AccountId;
|
|
43
43
|
publicKey: string;
|
|
44
|
-
|
|
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
|
-
|
|
57
|
+
validateLimitedAccessKey?: (args: {
|
|
58
58
|
accountId: AccountId;
|
|
59
59
|
publicKey: string;
|
|
60
|
-
|
|
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.
|
|
198
|
-
const
|
|
197
|
+
if (!options.requireFullAccessKey && options.validateLimitedAccessKey) {
|
|
198
|
+
const isValidKey = await options.validateLimitedAccessKey({
|
|
199
199
|
accountId: result.accountId,
|
|
200
200
|
publicKey: result.publicKey,
|
|
201
|
-
|
|
201
|
+
recipient: options.recipient
|
|
202
|
+
}); // we could validate against some access control contract
|
|
202
203
|
|
|
203
|
-
if (!
|
|
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
|
|
159
|
+
// async validateLimitedAccessKey({ accountId, publicKey }) {
|
|
160
160
|
// return accountId === "test.near" && publicKey !== "";
|
|
161
161
|
// },
|
|
162
162
|
// }),
|