@pinklemon8/better-auth-siws 0.1.1 → 0.1.4
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 +1 -1
- package/README.md +8 -8
- package/dist/{chunk-Q7TSFSYW.js → chunk-Q7OWFXGY.js} +2 -3
- package/dist/{chunk-Q7TSFSYW.js.map → chunk-Q7OWFXGY.js.map} +1 -1
- package/dist/index.cjs +5 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -7
- package/dist/index.js.map +1 -1
- package/dist/link.cjs +12 -2
- package/dist/link.cjs.map +1 -1
- package/dist/link.js +12 -1
- package/dist/link.js.map +1 -1
- package/dist/react/index.cjs +13 -9
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +13 -9
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Sign In With Solana (SIWS) plugin for [Better Auth](https://www.better-auth.com/
|
|
|
14
14
|
## Installation
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
npm install better-auth-siws @solana/wallet-adapter-react @solana/wallet-adapter-react-ui @solana/wallet-standard-features @solana/wallet-standard-util @solana/web3.js
|
|
17
|
+
npm install @pinklemon8/better-auth-siws @solana/wallet-adapter-react @solana/wallet-adapter-react-ui @solana/wallet-standard-features @solana/wallet-standard-util @solana/web3.js
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
## Quick Start
|
|
@@ -24,8 +24,8 @@ npm install better-auth-siws @solana/wallet-adapter-react @solana/wallet-adapter
|
|
|
24
24
|
```ts
|
|
25
25
|
// auth.ts
|
|
26
26
|
import { betterAuth } from "better-auth";
|
|
27
|
-
import { siws } from "better-auth-siws";
|
|
28
|
-
import { siwsLink } from "better-auth-siws/link";
|
|
27
|
+
import { siws } from "@pinklemon8/better-auth-siws";
|
|
28
|
+
import { siwsLink } from "@pinklemon8/better-auth-siws/link";
|
|
29
29
|
|
|
30
30
|
export const auth = betterAuth({
|
|
31
31
|
// ...your config
|
|
@@ -47,7 +47,7 @@ export const auth = betterAuth({
|
|
|
47
47
|
```ts
|
|
48
48
|
// auth-client.ts
|
|
49
49
|
import { createAuthClient } from "better-auth/react";
|
|
50
|
-
import { siwsClient } from "better-auth-siws/client";
|
|
50
|
+
import { siwsClient } from "@pinklemon8/better-auth-siws/client";
|
|
51
51
|
|
|
52
52
|
export const authClient = createAuthClient({
|
|
53
53
|
plugins: [siwsClient()],
|
|
@@ -59,7 +59,7 @@ export const authClient = createAuthClient({
|
|
|
59
59
|
Drop-in sign-in button:
|
|
60
60
|
|
|
61
61
|
```tsx
|
|
62
|
-
import { SolanaSignInButton } from "better-auth-siws/react";
|
|
62
|
+
import { SolanaSignInButton } from "@pinklemon8/better-auth-siws/react";
|
|
63
63
|
|
|
64
64
|
function LoginPage() {
|
|
65
65
|
return (
|
|
@@ -78,7 +78,7 @@ function LoginPage() {
|
|
|
78
78
|
Wallet linking for settings pages:
|
|
79
79
|
|
|
80
80
|
```tsx
|
|
81
|
-
import { SolanaLinkWallet } from "better-auth-siws/react";
|
|
81
|
+
import { SolanaLinkWallet } from "@pinklemon8/better-auth-siws/react";
|
|
82
82
|
|
|
83
83
|
function WalletSettings() {
|
|
84
84
|
return (
|
|
@@ -158,8 +158,8 @@ authClient.solana.getLinkedWallets();
|
|
|
158
158
|
| Component | Props | Description |
|
|
159
159
|
|---|---|---|
|
|
160
160
|
| `SolanaProvider` | `cluster`, `endpoint`, `autoConnect` | Wraps Solana wallet adapter providers |
|
|
161
|
-
| `SolanaSignInButton` | `baseURL`, `onSuccess`, `onError`, `className`, `cluster`, `endpoint` | Complete sign-in button |
|
|
162
|
-
| `SolanaLinkWallet` | `baseURL`, `onLink`, `onUnlink`, `onError`, `className`, `cluster`, `endpoint`, `renderLinked` | Wallet link/unlink component |
|
|
161
|
+
| `SolanaSignInButton` | `baseURL`, `basePath`, `onSuccess`, `onError`, `className`, `disabled`, `cluster`, `endpoint` | Complete sign-in button |
|
|
162
|
+
| `SolanaLinkWallet` | `baseURL`, `basePath`, `onLink`, `onUnlink`, `onError`, `className`, `disabled`, `cluster`, `endpoint`, `renderLinked` | Wallet link/unlink component |
|
|
163
163
|
|
|
164
164
|
## How It Works
|
|
165
165
|
|
|
@@ -28,8 +28,7 @@ function verifySIWS(input, output) {
|
|
|
28
28
|
signedMessage: output.signedMessage instanceof Uint8Array ? output.signedMessage : new Uint8Array(output.signedMessage)
|
|
29
29
|
};
|
|
30
30
|
return verifySignIn(input, reconstructed);
|
|
31
|
-
} catch
|
|
32
|
-
console.error("SIWS verification error:", error);
|
|
31
|
+
} catch {
|
|
33
32
|
return false;
|
|
34
33
|
}
|
|
35
34
|
}
|
|
@@ -39,4 +38,4 @@ export {
|
|
|
39
38
|
createSignInInput,
|
|
40
39
|
verifySIWS
|
|
41
40
|
};
|
|
42
|
-
//# sourceMappingURL=chunk-
|
|
41
|
+
//# sourceMappingURL=chunk-Q7OWFXGY.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/crypto.ts"],"sourcesContent":["import type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\nimport { verifySignIn } from \"@solana/wallet-standard-util\";\n\nexport function generateNonce(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n return Array.from(array, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function createSignInInput(\n domain: string,\n nonce: string,\n options?: { statement?: string; chainId?: string }\n): SolanaSignInInput {\n return {\n domain,\n statement:\n options?.statement ??\n \"Sign in with your Solana wallet. This will not trigger a blockchain transaction or cost any gas fee.\",\n version: \"1\",\n nonce,\n chainId: options?.chainId ?? \"mainnet\",\n issuedAt: new Date().toISOString(),\n };\n}\n\nexport function verifySIWS(\n input: SolanaSignInInput,\n output: SolanaSignInOutput | { account: { publicKey: number[] | Uint8Array; address: string }; signature: number[] | Uint8Array; signedMessage: number[] | Uint8Array }\n): boolean {\n try {\n const reconstructed: SolanaSignInOutput = {\n account: {\n ...output.account,\n publicKey:\n output.account.publicKey instanceof Uint8Array\n ? output.account.publicKey\n : new Uint8Array(output.account.publicKey),\n chains: (output.account as any).chains ?? [],\n features: (output.account as any).features ?? [],\n },\n signature:\n output.signature instanceof Uint8Array\n ? output.signature\n : new Uint8Array(output.signature),\n signedMessage:\n output.signedMessage instanceof Uint8Array\n ? output.signedMessage\n : new Uint8Array(output.signedMessage),\n };\n return verifySignIn(input, reconstructed);\n } catch
|
|
1
|
+
{"version":3,"sources":["../src/crypto.ts"],"sourcesContent":["import type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\nimport { verifySignIn } from \"@solana/wallet-standard-util\";\n\nexport function generateNonce(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n return Array.from(array, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function createSignInInput(\n domain: string,\n nonce: string,\n options?: { statement?: string; chainId?: string }\n): SolanaSignInInput {\n return {\n domain,\n statement:\n options?.statement ??\n \"Sign in with your Solana wallet. This will not trigger a blockchain transaction or cost any gas fee.\",\n version: \"1\",\n nonce,\n chainId: options?.chainId ?? \"mainnet\",\n issuedAt: new Date().toISOString(),\n };\n}\n\nexport function verifySIWS(\n input: SolanaSignInInput,\n output: SolanaSignInOutput | { account: { publicKey: number[] | Uint8Array; address: string }; signature: number[] | Uint8Array; signedMessage: number[] | Uint8Array }\n): boolean {\n try {\n const reconstructed: SolanaSignInOutput = {\n account: {\n ...output.account,\n publicKey:\n output.account.publicKey instanceof Uint8Array\n ? output.account.publicKey\n : new Uint8Array(output.account.publicKey),\n chains: (output.account as any).chains ?? [],\n features: (output.account as any).features ?? [],\n },\n signature:\n output.signature instanceof Uint8Array\n ? output.signature\n : new Uint8Array(output.signature),\n signedMessage:\n output.signedMessage instanceof Uint8Array\n ? output.signedMessage\n : new Uint8Array(output.signedMessage),\n };\n return verifySignIn(input, reconstructed);\n } catch {\n return false;\n }\n}\n"],"mappings":";AACA,SAAS,oBAAoB;AAEtB,SAAS,gBAAwB;AACtC,QAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,SAAO,gBAAgB,KAAK;AAC5B,SAAO,MAAM,KAAK,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAChF;AAEO,SAAS,kBACd,QACA,OACA,SACmB;AACnB,SAAO;AAAA,IACL;AAAA,IACA,WACE,SAAS,aACT;AAAA,IACF,SAAS;AAAA,IACT;AAAA,IACA,SAAS,SAAS,WAAW;AAAA,IAC7B,WAAU,oBAAI,KAAK,GAAE,YAAY;AAAA,EACnC;AACF;AAEO,SAAS,WACd,OACA,QACS;AACT,MAAI;AACF,UAAM,gBAAoC;AAAA,MACxC,SAAS;AAAA,QACP,GAAG,OAAO;AAAA,QACV,WACE,OAAO,QAAQ,qBAAqB,aAChC,OAAO,QAAQ,YACf,IAAI,WAAW,OAAO,QAAQ,SAAS;AAAA,QAC7C,QAAS,OAAO,QAAgB,UAAU,CAAC;AAAA,QAC3C,UAAW,OAAO,QAAgB,YAAY,CAAC;AAAA,MACjD;AAAA,MACA,WACE,OAAO,qBAAqB,aACxB,OAAO,YACP,IAAI,WAAW,OAAO,SAAS;AAAA,MACrC,eACE,OAAO,yBAAyB,aAC5B,OAAO,gBACP,IAAI,WAAW,OAAO,aAAa;AAAA,IAC3C;AACA,WAAO,aAAa,OAAO,aAAa;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -51,8 +51,7 @@ function verifySIWS(input, output) {
|
|
|
51
51
|
signedMessage: output.signedMessage instanceof Uint8Array ? output.signedMessage : new Uint8Array(output.signedMessage)
|
|
52
52
|
};
|
|
53
53
|
return (0, import_wallet_standard_util.verifySignIn)(input, reconstructed);
|
|
54
|
-
} catch
|
|
55
|
-
console.error("SIWS verification error:", error);
|
|
54
|
+
} catch {
|
|
56
55
|
return false;
|
|
57
56
|
}
|
|
58
57
|
}
|
|
@@ -165,8 +164,8 @@ var siws = (options) => ({
|
|
|
165
164
|
message: "Invalid signature"
|
|
166
165
|
});
|
|
167
166
|
}
|
|
168
|
-
await ctx.context.internalAdapter.
|
|
169
|
-
|
|
167
|
+
await ctx.context.internalAdapter.deleteVerificationByIdentifier(
|
|
168
|
+
`siws:${walletAddress}`
|
|
170
169
|
);
|
|
171
170
|
const linkedAccount = await ctx.context.adapter.findOne({
|
|
172
171
|
model: "account",
|
|
@@ -190,8 +189,7 @@ var siws = (options) => ({
|
|
|
190
189
|
updatedAt: /* @__PURE__ */ new Date()
|
|
191
190
|
});
|
|
192
191
|
const session2 = await ctx.context.internalAdapter.createSession(
|
|
193
|
-
user2.id
|
|
194
|
-
ctx
|
|
192
|
+
user2.id
|
|
195
193
|
);
|
|
196
194
|
if (!session2) {
|
|
197
195
|
throw new import_api.APIError("INTERNAL_SERVER_ERROR", {
|
|
@@ -230,8 +228,7 @@ var siws = (options) => ({
|
|
|
230
228
|
});
|
|
231
229
|
}
|
|
232
230
|
const session = await ctx.context.internalAdapter.createSession(
|
|
233
|
-
user.id
|
|
234
|
-
ctx
|
|
231
|
+
user.id
|
|
235
232
|
);
|
|
236
233
|
if (!session) {
|
|
237
234
|
throw new import_api.APIError("INTERNAL_SERVER_ERROR", {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/crypto.ts","../src/types.ts"],"sourcesContent":["import { createAuthEndpoint, APIError } from \"better-auth/api\";\nimport { setSessionCookie } from \"better-auth/cookies\";\nimport { z } from \"zod\";\nimport { generateNonce, verifySIWS } from \"./crypto\";\nimport { deserializeOutput } from \"./types\";\nimport type { SiwsPluginOptions } from \"./types\";\n\nexport type { SiwsPluginOptions };\nexport { generateNonce, verifySIWS } from \"./crypto\";\nexport { serializeOutput, deserializeOutput } from \"./types\";\nexport type {\n SiwsNonceResponse,\n SiwsVerifyRequest,\n SiwsVerifyResponse,\n SiwsLinkedWallet,\n SerializedSolanaOutput,\n} from \"./types\";\n\nexport const siws = (options?: SiwsPluginOptions) => ({\n id: \"siws\" as const,\n endpoints: {\n getSiwsNonce: createAuthEndpoint(\n \"/siws/nonce\",\n {\n method: \"POST\",\n body: z.object({\n walletAddress: z.string().min(32).max(44),\n }),\n },\n async (ctx) => {\n const { walletAddress } = ctx.body;\n const nonce = generateNonce();\n const expiryMs = options?.nonceExpiryMs ?? 5 * 60 * 1000;\n\n await ctx.context.internalAdapter.createVerificationValue({\n identifier: `siws:${walletAddress}`,\n value: nonce,\n expiresAt: new Date(Date.now() + expiryMs),\n });\n\n const host = ctx.headers?.get(\"host\") || \"localhost:3000\";\n const protocol = host.includes(\"localhost\") ? \"http\" : \"https\";\n const domain = host;\n const uri = `${protocol}://${host}`;\n\n return ctx.json({\n nonce,\n domain,\n uri,\n statement:\n options?.statement ??\n \"Sign in with your Solana wallet. This will not trigger a blockchain transaction or cost any gas fee.\",\n chainId: options?.chainId ?? \"mainnet\",\n issuedAt: new Date().toISOString(),\n });\n }\n ),\n\n verifySiwsSignature: createAuthEndpoint(\n \"/siws/verify\",\n {\n method: \"POST\",\n body: z.object({\n output: z.object({\n account: z.object({\n address: z.string(),\n publicKey: z.array(z.number()),\n }),\n signature: z.array(z.number()),\n signedMessage: z.array(z.number()),\n }),\n input: z.object({\n domain: z.string(),\n address: z.string().optional(),\n statement: z.string().optional(),\n uri: z.string().optional(),\n version: z.string().optional(),\n chainId: z.string().optional(),\n nonce: z.string().optional(),\n issuedAt: z.string().optional(),\n expirationTime: z.string().optional(),\n notBefore: z.string().optional(),\n requestId: z.string().optional(),\n resources: z.array(z.string()).optional(),\n }),\n }),\n requireRequest: true,\n },\n async (ctx: any) => {\n const { output, input } = ctx.body;\n const walletAddress = output.account.address;\n\n const verification =\n await ctx.context.internalAdapter.findVerificationValue(\n `siws:${walletAddress}`\n );\n\n if (!verification || new Date() > verification.expiresAt) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"Invalid or expired nonce\",\n });\n }\n\n const solanaOutput = deserializeOutput(output);\n const isValid = verifySIWS(input, solanaOutput);\n\n if (!isValid) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"Invalid signature\",\n });\n }\n\n await ctx.context.internalAdapter.deleteVerificationValue(\n verification.id\n );\n\n const linkedAccount = (await ctx.context.adapter.findOne({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"accountId\", operator: \"eq\", value: walletAddress },\n ],\n })) as { userId: string } | null;\n\n if (!linkedAccount) {\n if (options?.anonymous) {\n const user = await ctx.context.internalAdapter.createUser({\n name: walletAddress,\n email: `${walletAddress}@solana.wallet`,\n image: \"\",\n });\n\n await ctx.context.internalAdapter.createAccount({\n userId: user.id,\n providerId: \"solana\",\n accountId: walletAddress,\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n\n const session = await ctx.context.internalAdapter.createSession(\n user.id,\n ctx\n );\n\n if (!session) {\n throw new APIError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Failed to create session\",\n });\n }\n\n await setSessionCookie(ctx, { session, user });\n\n return ctx.json({\n success: true,\n user: {\n id: user.id,\n name: user.name,\n email: user.email,\n },\n });\n }\n\n throw new APIError(\"UNAUTHORIZED\", {\n message:\n \"No account linked to this wallet. Please sign in with email first and link your wallet.\",\n code: \"WALLET_NOT_LINKED\",\n });\n }\n\n const user = (await ctx.context.adapter.findOne({\n model: \"user\",\n where: [\n { field: \"id\", operator: \"eq\", value: linkedAccount.userId },\n ],\n })) as {\n id: string;\n name: string;\n email: string;\n emailVerified: boolean;\n image?: string | null;\n banned?: boolean;\n createdAt: Date;\n updatedAt: Date;\n } | null;\n\n if (!user) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"User not found\",\n });\n }\n\n if (user.banned) {\n throw new APIError(\"FORBIDDEN\", {\n message: \"Your account has been banned\",\n });\n }\n\n const session = await ctx.context.internalAdapter.createSession(\n user.id,\n ctx\n );\n\n if (!session) {\n throw new APIError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Failed to create session\",\n });\n }\n\n await setSessionCookie(ctx, { session, user });\n\n return ctx.json({\n success: true,\n user: {\n id: user.id,\n name: user.name,\n email: user.email,\n },\n });\n }\n ),\n },\n});\n","import type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\nimport { verifySignIn } from \"@solana/wallet-standard-util\";\n\nexport function generateNonce(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n return Array.from(array, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function createSignInInput(\n domain: string,\n nonce: string,\n options?: { statement?: string; chainId?: string }\n): SolanaSignInInput {\n return {\n domain,\n statement:\n options?.statement ??\n \"Sign in with your Solana wallet. This will not trigger a blockchain transaction or cost any gas fee.\",\n version: \"1\",\n nonce,\n chainId: options?.chainId ?? \"mainnet\",\n issuedAt: new Date().toISOString(),\n };\n}\n\nexport function verifySIWS(\n input: SolanaSignInInput,\n output: SolanaSignInOutput | { account: { publicKey: number[] | Uint8Array; address: string }; signature: number[] | Uint8Array; signedMessage: number[] | Uint8Array }\n): boolean {\n try {\n const reconstructed: SolanaSignInOutput = {\n account: {\n ...output.account,\n publicKey:\n output.account.publicKey instanceof Uint8Array\n ? output.account.publicKey\n : new Uint8Array(output.account.publicKey),\n chains: (output.account as any).chains ?? [],\n features: (output.account as any).features ?? [],\n },\n signature:\n output.signature instanceof Uint8Array\n ? output.signature\n : new Uint8Array(output.signature),\n signedMessage:\n output.signedMessage instanceof Uint8Array\n ? output.signedMessage\n : new Uint8Array(output.signedMessage),\n };\n return verifySignIn(input, reconstructed);\n } catch (error) {\n console.error(\"SIWS verification error:\", error);\n return false;\n }\n}\n","import type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\n\nexport type { SolanaSignInInput, SolanaSignInOutput };\n\nexport interface SiwsPluginOptions {\n statement?: string;\n chainId?: string;\n nonceExpiryMs?: number;\n anonymous?: boolean;\n}\n\nexport interface SiwsNonceResponse {\n nonce: string;\n domain: string;\n uri: string;\n statement: string;\n chainId: string;\n issuedAt: string;\n}\n\nexport interface SiwsVerifyRequest {\n output: {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n };\n input: {\n domain: string;\n address?: string;\n statement?: string;\n uri?: string;\n version?: string;\n chainId?: string;\n nonce?: string;\n issuedAt?: string;\n expirationTime?: string;\n notBefore?: string;\n requestId?: string;\n resources?: string[];\n };\n}\n\nexport interface SiwsVerifyResponse {\n success: boolean;\n user: {\n id: string;\n name: string;\n email: string;\n };\n}\n\nexport interface SiwsLinkRequest {\n input: SolanaSignInInput;\n output: {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n };\n}\n\nexport interface SiwsLinkedWallet {\n walletAddress: string;\n createdAt: Date;\n}\n\nexport interface SerializedSolanaOutput {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n signatureType?: string;\n}\n\nexport function serializeOutput(output: SolanaSignInOutput): SerializedSolanaOutput {\n return {\n account: {\n address: output.account.address,\n publicKey: Array.from(output.account.publicKey),\n },\n signature: Array.from(output.signature),\n signedMessage: Array.from(output.signedMessage),\n signatureType: (output as any).signatureType,\n };\n}\n\nexport function deserializeOutput(data: SerializedSolanaOutput): SolanaSignInOutput {\n return {\n account: {\n address: data.account.address,\n publicKey: new Uint8Array(data.account.publicKey),\n chains: [],\n features: [],\n },\n signature: new Uint8Array(data.signature),\n signedMessage: new Uint8Array(data.signedMessage),\n };\n}\n"],"mappings":"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","names":["user","session"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/crypto.ts","../src/types.ts"],"sourcesContent":["import { createAuthEndpoint, APIError } from \"better-auth/api\";\nimport { setSessionCookie } from \"better-auth/cookies\";\nimport { z } from \"zod\";\nimport { generateNonce, verifySIWS } from \"./crypto\";\nimport { deserializeOutput } from \"./types\";\nimport type { SiwsPluginOptions } from \"./types\";\n\nexport type { SiwsPluginOptions };\nexport { generateNonce, verifySIWS } from \"./crypto\";\nexport { serializeOutput, deserializeOutput } from \"./types\";\nexport type {\n SiwsNonceResponse,\n SiwsVerifyRequest,\n SiwsVerifyResponse,\n SiwsLinkedWallet,\n SerializedSolanaOutput,\n} from \"./types\";\n\nexport const siws = (options?: SiwsPluginOptions) => ({\n id: \"siws\" as const,\n endpoints: {\n getSiwsNonce: createAuthEndpoint(\n \"/siws/nonce\",\n {\n method: \"POST\",\n body: z.object({\n walletAddress: z.string().min(32).max(44),\n }),\n },\n async (ctx) => {\n const { walletAddress } = ctx.body;\n const nonce = generateNonce();\n const expiryMs = options?.nonceExpiryMs ?? 5 * 60 * 1000;\n\n await ctx.context.internalAdapter.createVerificationValue({\n identifier: `siws:${walletAddress}`,\n value: nonce,\n expiresAt: new Date(Date.now() + expiryMs),\n });\n\n const host = ctx.headers?.get(\"host\") || \"localhost:3000\";\n const protocol = host.includes(\"localhost\") ? \"http\" : \"https\";\n const domain = host;\n const uri = `${protocol}://${host}`;\n\n return ctx.json({\n nonce,\n domain,\n uri,\n statement:\n options?.statement ??\n \"Sign in with your Solana wallet. This will not trigger a blockchain transaction or cost any gas fee.\",\n chainId: options?.chainId ?? \"mainnet\",\n issuedAt: new Date().toISOString(),\n });\n }\n ),\n\n verifySiwsSignature: createAuthEndpoint(\n \"/siws/verify\",\n {\n method: \"POST\",\n body: z.object({\n output: z.object({\n account: z.object({\n address: z.string(),\n publicKey: z.array(z.number()),\n }),\n signature: z.array(z.number()),\n signedMessage: z.array(z.number()),\n }),\n input: z.object({\n domain: z.string(),\n address: z.string().optional(),\n statement: z.string().optional(),\n uri: z.string().optional(),\n version: z.string().optional(),\n chainId: z.string().optional(),\n nonce: z.string().optional(),\n issuedAt: z.string().optional(),\n expirationTime: z.string().optional(),\n notBefore: z.string().optional(),\n requestId: z.string().optional(),\n resources: z.array(z.string()).optional(),\n }),\n }),\n requireRequest: true,\n },\n async (ctx: any) => {\n const { output, input } = ctx.body;\n const walletAddress = output.account.address;\n\n const verification =\n await ctx.context.internalAdapter.findVerificationValue(\n `siws:${walletAddress}`\n );\n\n if (!verification || new Date() > verification.expiresAt) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"Invalid or expired nonce\",\n });\n }\n\n const solanaOutput = deserializeOutput(output);\n const isValid = verifySIWS(input, solanaOutput);\n\n if (!isValid) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"Invalid signature\",\n });\n }\n\n await ctx.context.internalAdapter.deleteVerificationByIdentifier(\n `siws:${walletAddress}`\n );\n\n const linkedAccount = (await ctx.context.adapter.findOne({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"accountId\", operator: \"eq\", value: walletAddress },\n ],\n })) as { userId: string } | null;\n\n if (!linkedAccount) {\n if (options?.anonymous) {\n const user = await ctx.context.internalAdapter.createUser({\n name: walletAddress,\n email: `${walletAddress}@solana.wallet`,\n image: \"\",\n });\n\n await ctx.context.internalAdapter.createAccount({\n userId: user.id,\n providerId: \"solana\",\n accountId: walletAddress,\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n\n const session = await ctx.context.internalAdapter.createSession(\n user.id\n );\n\n if (!session) {\n throw new APIError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Failed to create session\",\n });\n }\n\n await setSessionCookie(ctx, { session, user });\n\n return ctx.json({\n success: true,\n user: {\n id: user.id,\n name: user.name,\n email: user.email,\n },\n });\n }\n\n throw new APIError(\"UNAUTHORIZED\", {\n message:\n \"No account linked to this wallet. Please sign in with email first and link your wallet.\",\n code: \"WALLET_NOT_LINKED\",\n });\n }\n\n const user = (await ctx.context.adapter.findOne({\n model: \"user\",\n where: [\n { field: \"id\", operator: \"eq\", value: linkedAccount.userId },\n ],\n })) as {\n id: string;\n name: string;\n email: string;\n emailVerified: boolean;\n image?: string | null;\n banned?: boolean;\n createdAt: Date;\n updatedAt: Date;\n } | null;\n\n if (!user) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"User not found\",\n });\n }\n\n if (user.banned) {\n throw new APIError(\"FORBIDDEN\", {\n message: \"Your account has been banned\",\n });\n }\n\n const session = await ctx.context.internalAdapter.createSession(\n user.id\n );\n\n if (!session) {\n throw new APIError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Failed to create session\",\n });\n }\n\n await setSessionCookie(ctx, { session, user });\n\n return ctx.json({\n success: true,\n user: {\n id: user.id,\n name: user.name,\n email: user.email,\n },\n });\n }\n ),\n },\n});\n","import type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\nimport { verifySignIn } from \"@solana/wallet-standard-util\";\n\nexport function generateNonce(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n return Array.from(array, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function createSignInInput(\n domain: string,\n nonce: string,\n options?: { statement?: string; chainId?: string }\n): SolanaSignInInput {\n return {\n domain,\n statement:\n options?.statement ??\n \"Sign in with your Solana wallet. This will not trigger a blockchain transaction or cost any gas fee.\",\n version: \"1\",\n nonce,\n chainId: options?.chainId ?? \"mainnet\",\n issuedAt: new Date().toISOString(),\n };\n}\n\nexport function verifySIWS(\n input: SolanaSignInInput,\n output: SolanaSignInOutput | { account: { publicKey: number[] | Uint8Array; address: string }; signature: number[] | Uint8Array; signedMessage: number[] | Uint8Array }\n): boolean {\n try {\n const reconstructed: SolanaSignInOutput = {\n account: {\n ...output.account,\n publicKey:\n output.account.publicKey instanceof Uint8Array\n ? output.account.publicKey\n : new Uint8Array(output.account.publicKey),\n chains: (output.account as any).chains ?? [],\n features: (output.account as any).features ?? [],\n },\n signature:\n output.signature instanceof Uint8Array\n ? output.signature\n : new Uint8Array(output.signature),\n signedMessage:\n output.signedMessage instanceof Uint8Array\n ? output.signedMessage\n : new Uint8Array(output.signedMessage),\n };\n return verifySignIn(input, reconstructed);\n } catch {\n return false;\n }\n}\n","import type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\n\nexport type { SolanaSignInInput, SolanaSignInOutput };\n\nexport interface SiwsPluginOptions {\n statement?: string;\n chainId?: string;\n nonceExpiryMs?: number;\n anonymous?: boolean;\n}\n\nexport interface SiwsNonceResponse {\n nonce: string;\n domain: string;\n uri: string;\n statement: string;\n chainId: string;\n issuedAt: string;\n}\n\nexport interface SiwsVerifyRequest {\n output: {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n };\n input: {\n domain: string;\n address?: string;\n statement?: string;\n uri?: string;\n version?: string;\n chainId?: string;\n nonce?: string;\n issuedAt?: string;\n expirationTime?: string;\n notBefore?: string;\n requestId?: string;\n resources?: string[];\n };\n}\n\nexport interface SiwsVerifyResponse {\n success: boolean;\n user: {\n id: string;\n name: string;\n email: string;\n };\n}\n\nexport interface SiwsLinkRequest {\n input: SolanaSignInInput;\n output: {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n };\n}\n\nexport interface SiwsLinkedWallet {\n walletAddress: string;\n createdAt: Date;\n}\n\nexport interface SerializedSolanaOutput {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n signatureType?: string;\n}\n\nexport function serializeOutput(output: SolanaSignInOutput): SerializedSolanaOutput {\n return {\n account: {\n address: output.account.address,\n publicKey: Array.from(output.account.publicKey),\n },\n signature: Array.from(output.signature),\n signedMessage: Array.from(output.signedMessage),\n signatureType: (output as any).signatureType,\n };\n}\n\nexport function deserializeOutput(data: SerializedSolanaOutput): SolanaSignInOutput {\n return {\n account: {\n address: data.account.address,\n publicKey: new Uint8Array(data.account.publicKey),\n chains: [],\n features: [],\n },\n signature: new Uint8Array(data.signature),\n signedMessage: new Uint8Array(data.signedMessage),\n };\n}\n"],"mappings":"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","names":["user","session"]}
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
generateNonce,
|
|
7
7
|
verifySIWS
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-Q7OWFXGY.js";
|
|
9
9
|
|
|
10
10
|
// src/index.ts
|
|
11
11
|
import { createAuthEndpoint, APIError } from "better-auth/api";
|
|
@@ -93,8 +93,8 @@ var siws = (options) => ({
|
|
|
93
93
|
message: "Invalid signature"
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
|
-
await ctx.context.internalAdapter.
|
|
97
|
-
|
|
96
|
+
await ctx.context.internalAdapter.deleteVerificationByIdentifier(
|
|
97
|
+
`siws:${walletAddress}`
|
|
98
98
|
);
|
|
99
99
|
const linkedAccount = await ctx.context.adapter.findOne({
|
|
100
100
|
model: "account",
|
|
@@ -118,8 +118,7 @@ var siws = (options) => ({
|
|
|
118
118
|
updatedAt: /* @__PURE__ */ new Date()
|
|
119
119
|
});
|
|
120
120
|
const session2 = await ctx.context.internalAdapter.createSession(
|
|
121
|
-
user2.id
|
|
122
|
-
ctx
|
|
121
|
+
user2.id
|
|
123
122
|
);
|
|
124
123
|
if (!session2) {
|
|
125
124
|
throw new APIError("INTERNAL_SERVER_ERROR", {
|
|
@@ -158,8 +157,7 @@ var siws = (options) => ({
|
|
|
158
157
|
});
|
|
159
158
|
}
|
|
160
159
|
const session = await ctx.context.internalAdapter.createSession(
|
|
161
|
-
user.id
|
|
162
|
-
ctx
|
|
160
|
+
user.id
|
|
163
161
|
);
|
|
164
162
|
if (!session) {
|
|
165
163
|
throw new APIError("INTERNAL_SERVER_ERROR", {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { createAuthEndpoint, APIError } from \"better-auth/api\";\nimport { setSessionCookie } from \"better-auth/cookies\";\nimport { z } from \"zod\";\nimport { generateNonce, verifySIWS } from \"./crypto\";\nimport { deserializeOutput } from \"./types\";\nimport type { SiwsPluginOptions } from \"./types\";\n\nexport type { SiwsPluginOptions };\nexport { generateNonce, verifySIWS } from \"./crypto\";\nexport { serializeOutput, deserializeOutput } from \"./types\";\nexport type {\n SiwsNonceResponse,\n SiwsVerifyRequest,\n SiwsVerifyResponse,\n SiwsLinkedWallet,\n SerializedSolanaOutput,\n} from \"./types\";\n\nexport const siws = (options?: SiwsPluginOptions) => ({\n id: \"siws\" as const,\n endpoints: {\n getSiwsNonce: createAuthEndpoint(\n \"/siws/nonce\",\n {\n method: \"POST\",\n body: z.object({\n walletAddress: z.string().min(32).max(44),\n }),\n },\n async (ctx) => {\n const { walletAddress } = ctx.body;\n const nonce = generateNonce();\n const expiryMs = options?.nonceExpiryMs ?? 5 * 60 * 1000;\n\n await ctx.context.internalAdapter.createVerificationValue({\n identifier: `siws:${walletAddress}`,\n value: nonce,\n expiresAt: new Date(Date.now() + expiryMs),\n });\n\n const host = ctx.headers?.get(\"host\") || \"localhost:3000\";\n const protocol = host.includes(\"localhost\") ? \"http\" : \"https\";\n const domain = host;\n const uri = `${protocol}://${host}`;\n\n return ctx.json({\n nonce,\n domain,\n uri,\n statement:\n options?.statement ??\n \"Sign in with your Solana wallet. This will not trigger a blockchain transaction or cost any gas fee.\",\n chainId: options?.chainId ?? \"mainnet\",\n issuedAt: new Date().toISOString(),\n });\n }\n ),\n\n verifySiwsSignature: createAuthEndpoint(\n \"/siws/verify\",\n {\n method: \"POST\",\n body: z.object({\n output: z.object({\n account: z.object({\n address: z.string(),\n publicKey: z.array(z.number()),\n }),\n signature: z.array(z.number()),\n signedMessage: z.array(z.number()),\n }),\n input: z.object({\n domain: z.string(),\n address: z.string().optional(),\n statement: z.string().optional(),\n uri: z.string().optional(),\n version: z.string().optional(),\n chainId: z.string().optional(),\n nonce: z.string().optional(),\n issuedAt: z.string().optional(),\n expirationTime: z.string().optional(),\n notBefore: z.string().optional(),\n requestId: z.string().optional(),\n resources: z.array(z.string()).optional(),\n }),\n }),\n requireRequest: true,\n },\n async (ctx: any) => {\n const { output, input } = ctx.body;\n const walletAddress = output.account.address;\n\n const verification =\n await ctx.context.internalAdapter.findVerificationValue(\n `siws:${walletAddress}`\n );\n\n if (!verification || new Date() > verification.expiresAt) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"Invalid or expired nonce\",\n });\n }\n\n const solanaOutput = deserializeOutput(output);\n const isValid = verifySIWS(input, solanaOutput);\n\n if (!isValid) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"Invalid signature\",\n });\n }\n\n await ctx.context.internalAdapter.deleteVerificationValue(\n verification.id\n );\n\n const linkedAccount = (await ctx.context.adapter.findOne({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"accountId\", operator: \"eq\", value: walletAddress },\n ],\n })) as { userId: string } | null;\n\n if (!linkedAccount) {\n if (options?.anonymous) {\n const user = await ctx.context.internalAdapter.createUser({\n name: walletAddress,\n email: `${walletAddress}@solana.wallet`,\n image: \"\",\n });\n\n await ctx.context.internalAdapter.createAccount({\n userId: user.id,\n providerId: \"solana\",\n accountId: walletAddress,\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n\n const session = await ctx.context.internalAdapter.createSession(\n user.id,\n ctx\n );\n\n if (!session) {\n throw new APIError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Failed to create session\",\n });\n }\n\n await setSessionCookie(ctx, { session, user });\n\n return ctx.json({\n success: true,\n user: {\n id: user.id,\n name: user.name,\n email: user.email,\n },\n });\n }\n\n throw new APIError(\"UNAUTHORIZED\", {\n message:\n \"No account linked to this wallet. Please sign in with email first and link your wallet.\",\n code: \"WALLET_NOT_LINKED\",\n });\n }\n\n const user = (await ctx.context.adapter.findOne({\n model: \"user\",\n where: [\n { field: \"id\", operator: \"eq\", value: linkedAccount.userId },\n ],\n })) as {\n id: string;\n name: string;\n email: string;\n emailVerified: boolean;\n image?: string | null;\n banned?: boolean;\n createdAt: Date;\n updatedAt: Date;\n } | null;\n\n if (!user) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"User not found\",\n });\n }\n\n if (user.banned) {\n throw new APIError(\"FORBIDDEN\", {\n message: \"Your account has been banned\",\n });\n }\n\n const session = await ctx.context.internalAdapter.createSession(\n user.id,\n ctx\n );\n\n if (!session) {\n throw new APIError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Failed to create session\",\n });\n }\n\n await setSessionCookie(ctx, { session, user });\n\n return ctx.json({\n success: true,\n user: {\n id: user.id,\n name: user.name,\n email: user.email,\n },\n });\n }\n ),\n },\n});\n"],"mappings":"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","names":["user","session"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { createAuthEndpoint, APIError } from \"better-auth/api\";\nimport { setSessionCookie } from \"better-auth/cookies\";\nimport { z } from \"zod\";\nimport { generateNonce, verifySIWS } from \"./crypto\";\nimport { deserializeOutput } from \"./types\";\nimport type { SiwsPluginOptions } from \"./types\";\n\nexport type { SiwsPluginOptions };\nexport { generateNonce, verifySIWS } from \"./crypto\";\nexport { serializeOutput, deserializeOutput } from \"./types\";\nexport type {\n SiwsNonceResponse,\n SiwsVerifyRequest,\n SiwsVerifyResponse,\n SiwsLinkedWallet,\n SerializedSolanaOutput,\n} from \"./types\";\n\nexport const siws = (options?: SiwsPluginOptions) => ({\n id: \"siws\" as const,\n endpoints: {\n getSiwsNonce: createAuthEndpoint(\n \"/siws/nonce\",\n {\n method: \"POST\",\n body: z.object({\n walletAddress: z.string().min(32).max(44),\n }),\n },\n async (ctx) => {\n const { walletAddress } = ctx.body;\n const nonce = generateNonce();\n const expiryMs = options?.nonceExpiryMs ?? 5 * 60 * 1000;\n\n await ctx.context.internalAdapter.createVerificationValue({\n identifier: `siws:${walletAddress}`,\n value: nonce,\n expiresAt: new Date(Date.now() + expiryMs),\n });\n\n const host = ctx.headers?.get(\"host\") || \"localhost:3000\";\n const protocol = host.includes(\"localhost\") ? \"http\" : \"https\";\n const domain = host;\n const uri = `${protocol}://${host}`;\n\n return ctx.json({\n nonce,\n domain,\n uri,\n statement:\n options?.statement ??\n \"Sign in with your Solana wallet. This will not trigger a blockchain transaction or cost any gas fee.\",\n chainId: options?.chainId ?? \"mainnet\",\n issuedAt: new Date().toISOString(),\n });\n }\n ),\n\n verifySiwsSignature: createAuthEndpoint(\n \"/siws/verify\",\n {\n method: \"POST\",\n body: z.object({\n output: z.object({\n account: z.object({\n address: z.string(),\n publicKey: z.array(z.number()),\n }),\n signature: z.array(z.number()),\n signedMessage: z.array(z.number()),\n }),\n input: z.object({\n domain: z.string(),\n address: z.string().optional(),\n statement: z.string().optional(),\n uri: z.string().optional(),\n version: z.string().optional(),\n chainId: z.string().optional(),\n nonce: z.string().optional(),\n issuedAt: z.string().optional(),\n expirationTime: z.string().optional(),\n notBefore: z.string().optional(),\n requestId: z.string().optional(),\n resources: z.array(z.string()).optional(),\n }),\n }),\n requireRequest: true,\n },\n async (ctx: any) => {\n const { output, input } = ctx.body;\n const walletAddress = output.account.address;\n\n const verification =\n await ctx.context.internalAdapter.findVerificationValue(\n `siws:${walletAddress}`\n );\n\n if (!verification || new Date() > verification.expiresAt) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"Invalid or expired nonce\",\n });\n }\n\n const solanaOutput = deserializeOutput(output);\n const isValid = verifySIWS(input, solanaOutput);\n\n if (!isValid) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"Invalid signature\",\n });\n }\n\n await ctx.context.internalAdapter.deleteVerificationByIdentifier(\n `siws:${walletAddress}`\n );\n\n const linkedAccount = (await ctx.context.adapter.findOne({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"accountId\", operator: \"eq\", value: walletAddress },\n ],\n })) as { userId: string } | null;\n\n if (!linkedAccount) {\n if (options?.anonymous) {\n const user = await ctx.context.internalAdapter.createUser({\n name: walletAddress,\n email: `${walletAddress}@solana.wallet`,\n image: \"\",\n });\n\n await ctx.context.internalAdapter.createAccount({\n userId: user.id,\n providerId: \"solana\",\n accountId: walletAddress,\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n\n const session = await ctx.context.internalAdapter.createSession(\n user.id\n );\n\n if (!session) {\n throw new APIError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Failed to create session\",\n });\n }\n\n await setSessionCookie(ctx, { session, user });\n\n return ctx.json({\n success: true,\n user: {\n id: user.id,\n name: user.name,\n email: user.email,\n },\n });\n }\n\n throw new APIError(\"UNAUTHORIZED\", {\n message:\n \"No account linked to this wallet. Please sign in with email first and link your wallet.\",\n code: \"WALLET_NOT_LINKED\",\n });\n }\n\n const user = (await ctx.context.adapter.findOne({\n model: \"user\",\n where: [\n { field: \"id\", operator: \"eq\", value: linkedAccount.userId },\n ],\n })) as {\n id: string;\n name: string;\n email: string;\n emailVerified: boolean;\n image?: string | null;\n banned?: boolean;\n createdAt: Date;\n updatedAt: Date;\n } | null;\n\n if (!user) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"User not found\",\n });\n }\n\n if (user.banned) {\n throw new APIError(\"FORBIDDEN\", {\n message: \"Your account has been banned\",\n });\n }\n\n const session = await ctx.context.internalAdapter.createSession(\n user.id\n );\n\n if (!session) {\n throw new APIError(\"INTERNAL_SERVER_ERROR\", {\n message: \"Failed to create session\",\n });\n }\n\n await setSessionCookie(ctx, { session, user });\n\n return ctx.json({\n success: true,\n user: {\n id: user.id,\n name: user.name,\n email: user.email,\n },\n });\n }\n ),\n },\n});\n"],"mappings":"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","names":["user","session"]}
|
package/dist/link.cjs
CHANGED
|
@@ -60,8 +60,7 @@ function verifySIWS(input, output) {
|
|
|
60
60
|
signedMessage: output.signedMessage instanceof Uint8Array ? output.signedMessage : new Uint8Array(output.signedMessage)
|
|
61
61
|
};
|
|
62
62
|
return (0, import_wallet_standard_util.verifySignIn)(input, reconstructed);
|
|
63
|
-
} catch
|
|
64
|
-
console.error("SIWS verification error:", error);
|
|
63
|
+
} catch {
|
|
65
64
|
return false;
|
|
66
65
|
}
|
|
67
66
|
}
|
|
@@ -101,12 +100,23 @@ var siwsLink = (_options) => ({
|
|
|
101
100
|
}
|
|
102
101
|
const { input, output } = ctx.body;
|
|
103
102
|
const walletAddress = output.account.address;
|
|
103
|
+
const verification = await ctx.context.internalAdapter.findVerificationValue(
|
|
104
|
+
`siws:${walletAddress}`
|
|
105
|
+
);
|
|
106
|
+
if (!verification || /* @__PURE__ */ new Date() > verification.expiresAt) {
|
|
107
|
+
throw new import_api.APIError("UNAUTHORIZED", {
|
|
108
|
+
message: "Invalid or expired nonce. Please request a new one."
|
|
109
|
+
});
|
|
110
|
+
}
|
|
104
111
|
const isValid = verifySIWS(input, output);
|
|
105
112
|
if (!isValid) {
|
|
106
113
|
throw new import_api.APIError("BAD_REQUEST", {
|
|
107
114
|
message: "Invalid signature"
|
|
108
115
|
});
|
|
109
116
|
}
|
|
117
|
+
await ctx.context.internalAdapter.deleteVerificationByIdentifier(
|
|
118
|
+
`siws:${walletAddress}`
|
|
119
|
+
);
|
|
110
120
|
const existing = await ctx.context.adapter.findOne({
|
|
111
121
|
model: "account",
|
|
112
122
|
where: [
|
package/dist/link.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/link.ts","../src/crypto.ts"],"sourcesContent":["import { createAuthEndpoint, APIError } from \"better-auth/api\";\nimport { getSessionFromCtx } from \"better-auth/api\";\nimport { z } from \"zod\";\nimport { verifySIWS } from \"./crypto\";\nimport type { SiwsPluginOptions } from \"./types\";\n\nexport { verifySIWS } from \"./crypto\";\nexport { generateNonce, createSignInInput } from \"./crypto\";\nexport type { SiwsLinkedWallet, SiwsLinkRequest } from \"./types\";\n\nexport const siwsLink = (_options?: Pick<SiwsPluginOptions, \"chainId\">) => ({\n id: \"siws-link\" as const,\n endpoints: {\n linkSolanaWallet: createAuthEndpoint(\n \"/siws/link\",\n {\n method: \"POST\",\n body: z.object({\n input: z.object({\n domain: z.string(),\n statement: z.string().optional(),\n version: z.string().optional(),\n nonce: z.string().optional(),\n chainId: z.string().optional(),\n issuedAt: z.string().optional(),\n }),\n output: z.object({\n account: z.object({\n address: z.string(),\n publicKey: z.array(z.number()),\n }),\n signature: z.array(z.number()),\n signedMessage: z.array(z.number()),\n }),\n }),\n requireRequest: true,\n },\n async (ctx: any) => {\n const session = await getSessionFromCtx(ctx);\n if (!session?.user) {\n throw new APIError(\"UNAUTHORIZED\", { message: \"Not authenticated\" });\n }\n\n const { input, output } = ctx.body;\n const walletAddress = output.account.address;\n\n const isValid = verifySIWS(input, output);\n if (!isValid) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Invalid signature\",\n });\n }\n\n const existing = await ctx.context.adapter.findOne({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"accountId\", operator: \"eq\", value: walletAddress },\n ],\n });\n\n if (existing) {\n if ((existing as any).userId === session.user.id) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"This wallet is already linked to your account\",\n });\n }\n throw new APIError(\"BAD_REQUEST\", {\n message: \"This wallet is already linked to another account\",\n });\n }\n\n await ctx.context.internalAdapter.createAccount({\n userId: session.user.id,\n providerId: \"solana\",\n accountId: walletAddress,\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n\n return ctx.json({ success: true, walletAddress });\n }\n ),\n\n unlinkSolanaWallet: createAuthEndpoint(\n \"/siws/unlink\",\n {\n method: \"POST\",\n body: z.object({\n walletAddress: z.string().min(32).max(44),\n }),\n requireRequest: true,\n },\n async (ctx: any) => {\n const session = await getSessionFromCtx(ctx);\n if (!session?.user) {\n throw new APIError(\"UNAUTHORIZED\", { message: \"Not authenticated\" });\n }\n\n const { walletAddress } = ctx.body;\n\n const account = await ctx.context.adapter.findOne({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"accountId\", operator: \"eq\", value: walletAddress },\n { field: \"userId\", operator: \"eq\", value: session.user.id },\n ],\n });\n\n if (!account) {\n throw new APIError(\"NOT_FOUND\", {\n message: \"Wallet not found on your account\",\n });\n }\n\n await ctx.context.adapter.delete({\n model: \"account\",\n where: [\n { field: \"id\", operator: \"eq\", value: (account as any).id },\n ],\n });\n\n return ctx.json({ success: true });\n }\n ),\n\n getLinkedSolanaWallets: createAuthEndpoint(\n \"/siws/wallets\",\n {\n method: \"GET\",\n requireRequest: true,\n },\n async (ctx: any) => {\n const session = await getSessionFromCtx(ctx);\n if (!session?.user) {\n throw new APIError(\"UNAUTHORIZED\", { message: \"Not authenticated\" });\n }\n\n const accounts = await ctx.context.adapter.findMany({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"userId\", operator: \"eq\", value: session.user.id },\n ],\n });\n\n const wallets = (accounts as any[]).map((a) => ({\n walletAddress: a.accountId,\n createdAt: a.createdAt,\n }));\n\n return ctx.json({ wallets });\n }\n ),\n },\n});\n","import type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\nimport { verifySignIn } from \"@solana/wallet-standard-util\";\n\nexport function generateNonce(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n return Array.from(array, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function createSignInInput(\n domain: string,\n nonce: string,\n options?: { statement?: string; chainId?: string }\n): SolanaSignInInput {\n return {\n domain,\n statement:\n options?.statement ??\n \"Sign in with your Solana wallet. This will not trigger a blockchain transaction or cost any gas fee.\",\n version: \"1\",\n nonce,\n chainId: options?.chainId ?? \"mainnet\",\n issuedAt: new Date().toISOString(),\n };\n}\n\nexport function verifySIWS(\n input: SolanaSignInInput,\n output: SolanaSignInOutput | { account: { publicKey: number[] | Uint8Array; address: string }; signature: number[] | Uint8Array; signedMessage: number[] | Uint8Array }\n): boolean {\n try {\n const reconstructed: SolanaSignInOutput = {\n account: {\n ...output.account,\n publicKey:\n output.account.publicKey instanceof Uint8Array\n ? output.account.publicKey\n : new Uint8Array(output.account.publicKey),\n chains: (output.account as any).chains ?? [],\n features: (output.account as any).features ?? [],\n },\n signature:\n output.signature instanceof Uint8Array\n ? output.signature\n : new Uint8Array(output.signature),\n signedMessage:\n output.signedMessage instanceof Uint8Array\n ? output.signedMessage\n : new Uint8Array(output.signedMessage),\n };\n return verifySignIn(input, reconstructed);\n } catch (error) {\n console.error(\"SIWS verification error:\", error);\n return false;\n }\n}\n"],"mappings":"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","names":["import_api"]}
|
|
1
|
+
{"version":3,"sources":["../src/link.ts","../src/crypto.ts"],"sourcesContent":["import { createAuthEndpoint, APIError } from \"better-auth/api\";\nimport { getSessionFromCtx } from \"better-auth/api\";\nimport { z } from \"zod\";\nimport { verifySIWS } from \"./crypto\";\nimport type { SiwsPluginOptions } from \"./types\";\n\nexport { verifySIWS } from \"./crypto\";\nexport { generateNonce, createSignInInput } from \"./crypto\";\nexport type { SiwsLinkedWallet, SiwsLinkRequest } from \"./types\";\n\nexport const siwsLink = (_options?: Pick<SiwsPluginOptions, \"chainId\">) => ({\n id: \"siws-link\" as const,\n endpoints: {\n linkSolanaWallet: createAuthEndpoint(\n \"/siws/link\",\n {\n method: \"POST\",\n body: z.object({\n input: z.object({\n domain: z.string(),\n statement: z.string().optional(),\n version: z.string().optional(),\n nonce: z.string().optional(),\n chainId: z.string().optional(),\n issuedAt: z.string().optional(),\n }),\n output: z.object({\n account: z.object({\n address: z.string(),\n publicKey: z.array(z.number()),\n }),\n signature: z.array(z.number()),\n signedMessage: z.array(z.number()),\n }),\n }),\n requireRequest: true,\n },\n async (ctx: any) => {\n const session = await getSessionFromCtx(ctx);\n if (!session?.user) {\n throw new APIError(\"UNAUTHORIZED\", { message: \"Not authenticated\" });\n }\n\n const { input, output } = ctx.body;\n const walletAddress = output.account.address;\n\n // Verify nonce from verification table to prevent replay attacks\n const verification =\n await ctx.context.internalAdapter.findVerificationValue(\n `siws:${walletAddress}`\n );\n\n if (!verification || new Date() > verification.expiresAt) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"Invalid or expired nonce. Please request a new one.\",\n });\n }\n\n const isValid = verifySIWS(input, output);\n if (!isValid) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Invalid signature\",\n });\n }\n\n // Delete used nonce to prevent reuse\n await ctx.context.internalAdapter.deleteVerificationByIdentifier(\n `siws:${walletAddress}`\n );\n\n const existing = await ctx.context.adapter.findOne({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"accountId\", operator: \"eq\", value: walletAddress },\n ],\n });\n\n if (existing) {\n if ((existing as any).userId === session.user.id) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"This wallet is already linked to your account\",\n });\n }\n throw new APIError(\"BAD_REQUEST\", {\n message: \"This wallet is already linked to another account\",\n });\n }\n\n await ctx.context.internalAdapter.createAccount({\n userId: session.user.id,\n providerId: \"solana\",\n accountId: walletAddress,\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n\n return ctx.json({ success: true, walletAddress });\n }\n ),\n\n unlinkSolanaWallet: createAuthEndpoint(\n \"/siws/unlink\",\n {\n method: \"POST\",\n body: z.object({\n walletAddress: z.string().min(32).max(44),\n }),\n requireRequest: true,\n },\n async (ctx: any) => {\n const session = await getSessionFromCtx(ctx);\n if (!session?.user) {\n throw new APIError(\"UNAUTHORIZED\", { message: \"Not authenticated\" });\n }\n\n const { walletAddress } = ctx.body;\n\n const account = await ctx.context.adapter.findOne({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"accountId\", operator: \"eq\", value: walletAddress },\n { field: \"userId\", operator: \"eq\", value: session.user.id },\n ],\n });\n\n if (!account) {\n throw new APIError(\"NOT_FOUND\", {\n message: \"Wallet not found on your account\",\n });\n }\n\n await ctx.context.adapter.delete({\n model: \"account\",\n where: [\n { field: \"id\", operator: \"eq\", value: (account as any).id },\n ],\n });\n\n return ctx.json({ success: true });\n }\n ),\n\n getLinkedSolanaWallets: createAuthEndpoint(\n \"/siws/wallets\",\n {\n method: \"GET\",\n requireRequest: true,\n },\n async (ctx: any) => {\n const session = await getSessionFromCtx(ctx);\n if (!session?.user) {\n throw new APIError(\"UNAUTHORIZED\", { message: \"Not authenticated\" });\n }\n\n const accounts = await ctx.context.adapter.findMany({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"userId\", operator: \"eq\", value: session.user.id },\n ],\n });\n\n const wallets = (accounts as any[]).map((a) => ({\n walletAddress: a.accountId,\n createdAt: a.createdAt,\n }));\n\n return ctx.json({ wallets });\n }\n ),\n },\n});\n","import type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\nimport { verifySignIn } from \"@solana/wallet-standard-util\";\n\nexport function generateNonce(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n return Array.from(array, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\nexport function createSignInInput(\n domain: string,\n nonce: string,\n options?: { statement?: string; chainId?: string }\n): SolanaSignInInput {\n return {\n domain,\n statement:\n options?.statement ??\n \"Sign in with your Solana wallet. This will not trigger a blockchain transaction or cost any gas fee.\",\n version: \"1\",\n nonce,\n chainId: options?.chainId ?? \"mainnet\",\n issuedAt: new Date().toISOString(),\n };\n}\n\nexport function verifySIWS(\n input: SolanaSignInInput,\n output: SolanaSignInOutput | { account: { publicKey: number[] | Uint8Array; address: string }; signature: number[] | Uint8Array; signedMessage: number[] | Uint8Array }\n): boolean {\n try {\n const reconstructed: SolanaSignInOutput = {\n account: {\n ...output.account,\n publicKey:\n output.account.publicKey instanceof Uint8Array\n ? output.account.publicKey\n : new Uint8Array(output.account.publicKey),\n chains: (output.account as any).chains ?? [],\n features: (output.account as any).features ?? [],\n },\n signature:\n output.signature instanceof Uint8Array\n ? output.signature\n : new Uint8Array(output.signature),\n signedMessage:\n output.signedMessage instanceof Uint8Array\n ? output.signedMessage\n : new Uint8Array(output.signedMessage),\n };\n return verifySignIn(input, reconstructed);\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":["import_api"]}
|
package/dist/link.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
createSignInInput,
|
|
3
3
|
generateNonce,
|
|
4
4
|
verifySIWS
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-Q7OWFXGY.js";
|
|
6
6
|
|
|
7
7
|
// src/link.ts
|
|
8
8
|
import { createAuthEndpoint, APIError } from "better-auth/api";
|
|
@@ -42,12 +42,23 @@ var siwsLink = (_options) => ({
|
|
|
42
42
|
}
|
|
43
43
|
const { input, output } = ctx.body;
|
|
44
44
|
const walletAddress = output.account.address;
|
|
45
|
+
const verification = await ctx.context.internalAdapter.findVerificationValue(
|
|
46
|
+
`siws:${walletAddress}`
|
|
47
|
+
);
|
|
48
|
+
if (!verification || /* @__PURE__ */ new Date() > verification.expiresAt) {
|
|
49
|
+
throw new APIError("UNAUTHORIZED", {
|
|
50
|
+
message: "Invalid or expired nonce. Please request a new one."
|
|
51
|
+
});
|
|
52
|
+
}
|
|
45
53
|
const isValid = verifySIWS(input, output);
|
|
46
54
|
if (!isValid) {
|
|
47
55
|
throw new APIError("BAD_REQUEST", {
|
|
48
56
|
message: "Invalid signature"
|
|
49
57
|
});
|
|
50
58
|
}
|
|
59
|
+
await ctx.context.internalAdapter.deleteVerificationByIdentifier(
|
|
60
|
+
`siws:${walletAddress}`
|
|
61
|
+
);
|
|
51
62
|
const existing = await ctx.context.adapter.findOne({
|
|
52
63
|
model: "account",
|
|
53
64
|
where: [
|
package/dist/link.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/link.ts"],"sourcesContent":["import { createAuthEndpoint, APIError } from \"better-auth/api\";\nimport { getSessionFromCtx } from \"better-auth/api\";\nimport { z } from \"zod\";\nimport { verifySIWS } from \"./crypto\";\nimport type { SiwsPluginOptions } from \"./types\";\n\nexport { verifySIWS } from \"./crypto\";\nexport { generateNonce, createSignInInput } from \"./crypto\";\nexport type { SiwsLinkedWallet, SiwsLinkRequest } from \"./types\";\n\nexport const siwsLink = (_options?: Pick<SiwsPluginOptions, \"chainId\">) => ({\n id: \"siws-link\" as const,\n endpoints: {\n linkSolanaWallet: createAuthEndpoint(\n \"/siws/link\",\n {\n method: \"POST\",\n body: z.object({\n input: z.object({\n domain: z.string(),\n statement: z.string().optional(),\n version: z.string().optional(),\n nonce: z.string().optional(),\n chainId: z.string().optional(),\n issuedAt: z.string().optional(),\n }),\n output: z.object({\n account: z.object({\n address: z.string(),\n publicKey: z.array(z.number()),\n }),\n signature: z.array(z.number()),\n signedMessage: z.array(z.number()),\n }),\n }),\n requireRequest: true,\n },\n async (ctx: any) => {\n const session = await getSessionFromCtx(ctx);\n if (!session?.user) {\n throw new APIError(\"UNAUTHORIZED\", { message: \"Not authenticated\" });\n }\n\n const { input, output } = ctx.body;\n const walletAddress = output.account.address;\n\n const isValid = verifySIWS(input, output);\n if (!isValid) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Invalid signature\",\n });\n }\n\n const existing = await ctx.context.adapter.findOne({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"accountId\", operator: \"eq\", value: walletAddress },\n ],\n });\n\n if (existing) {\n if ((existing as any).userId === session.user.id) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"This wallet is already linked to your account\",\n });\n }\n throw new APIError(\"BAD_REQUEST\", {\n message: \"This wallet is already linked to another account\",\n });\n }\n\n await ctx.context.internalAdapter.createAccount({\n userId: session.user.id,\n providerId: \"solana\",\n accountId: walletAddress,\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n\n return ctx.json({ success: true, walletAddress });\n }\n ),\n\n unlinkSolanaWallet: createAuthEndpoint(\n \"/siws/unlink\",\n {\n method: \"POST\",\n body: z.object({\n walletAddress: z.string().min(32).max(44),\n }),\n requireRequest: true,\n },\n async (ctx: any) => {\n const session = await getSessionFromCtx(ctx);\n if (!session?.user) {\n throw new APIError(\"UNAUTHORIZED\", { message: \"Not authenticated\" });\n }\n\n const { walletAddress } = ctx.body;\n\n const account = await ctx.context.adapter.findOne({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"accountId\", operator: \"eq\", value: walletAddress },\n { field: \"userId\", operator: \"eq\", value: session.user.id },\n ],\n });\n\n if (!account) {\n throw new APIError(\"NOT_FOUND\", {\n message: \"Wallet not found on your account\",\n });\n }\n\n await ctx.context.adapter.delete({\n model: \"account\",\n where: [\n { field: \"id\", operator: \"eq\", value: (account as any).id },\n ],\n });\n\n return ctx.json({ success: true });\n }\n ),\n\n getLinkedSolanaWallets: createAuthEndpoint(\n \"/siws/wallets\",\n {\n method: \"GET\",\n requireRequest: true,\n },\n async (ctx: any) => {\n const session = await getSessionFromCtx(ctx);\n if (!session?.user) {\n throw new APIError(\"UNAUTHORIZED\", { message: \"Not authenticated\" });\n }\n\n const accounts = await ctx.context.adapter.findMany({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"userId\", operator: \"eq\", value: session.user.id },\n ],\n });\n\n const wallets = (accounts as any[]).map((a) => ({\n walletAddress: a.accountId,\n createdAt: a.createdAt,\n }));\n\n return ctx.json({ wallets });\n }\n ),\n },\n});\n"],"mappings":";;;;;;;AAAA,SAAS,oBAAoB,gBAAgB;AAC7C,SAAS,yBAAyB;AAClC,SAAS,SAAS;AAQX,IAAM,WAAW,CAAC,cAAmD;AAAA,EAC1E,IAAI;AAAA,EACJ,WAAW;AAAA,IACT,kBAAkB;AAAA,MAChB;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,MAAM,EAAE,OAAO;AAAA,UACb,OAAO,EAAE,OAAO;AAAA,YACd,QAAQ,EAAE,OAAO;AAAA,YACjB,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,YAC/B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,YAC7B,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,YAC3B,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,YAC7B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,UAChC,CAAC;AAAA,UACD,QAAQ,EAAE,OAAO;AAAA,YACf,SAAS,EAAE,OAAO;AAAA,cAChB,SAAS,EAAE,OAAO;AAAA,cAClB,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,YAC/B,CAAC;AAAA,YACD,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,YAC7B,eAAe,EAAE,MAAM,EAAE,OAAO,CAAC;AAAA,UACnC,CAAC;AAAA,QACH,CAAC;AAAA,QACD,gBAAgB;AAAA,MAClB;AAAA,MACA,OAAO,QAAa;AAClB,cAAM,UAAU,MAAM,kBAAkB,GAAG;AAC3C,YAAI,CAAC,SAAS,MAAM;AAClB,gBAAM,IAAI,SAAS,gBAAgB,EAAE,SAAS,oBAAoB,CAAC;AAAA,QACrE;AAEA,cAAM,EAAE,OAAO,OAAO,IAAI,IAAI;AAC9B,cAAM,gBAAgB,OAAO,QAAQ;
|
|
1
|
+
{"version":3,"sources":["../src/link.ts"],"sourcesContent":["import { createAuthEndpoint, APIError } from \"better-auth/api\";\nimport { getSessionFromCtx } from \"better-auth/api\";\nimport { z } from \"zod\";\nimport { verifySIWS } from \"./crypto\";\nimport type { SiwsPluginOptions } from \"./types\";\n\nexport { verifySIWS } from \"./crypto\";\nexport { generateNonce, createSignInInput } from \"./crypto\";\nexport type { SiwsLinkedWallet, SiwsLinkRequest } from \"./types\";\n\nexport const siwsLink = (_options?: Pick<SiwsPluginOptions, \"chainId\">) => ({\n id: \"siws-link\" as const,\n endpoints: {\n linkSolanaWallet: createAuthEndpoint(\n \"/siws/link\",\n {\n method: \"POST\",\n body: z.object({\n input: z.object({\n domain: z.string(),\n statement: z.string().optional(),\n version: z.string().optional(),\n nonce: z.string().optional(),\n chainId: z.string().optional(),\n issuedAt: z.string().optional(),\n }),\n output: z.object({\n account: z.object({\n address: z.string(),\n publicKey: z.array(z.number()),\n }),\n signature: z.array(z.number()),\n signedMessage: z.array(z.number()),\n }),\n }),\n requireRequest: true,\n },\n async (ctx: any) => {\n const session = await getSessionFromCtx(ctx);\n if (!session?.user) {\n throw new APIError(\"UNAUTHORIZED\", { message: \"Not authenticated\" });\n }\n\n const { input, output } = ctx.body;\n const walletAddress = output.account.address;\n\n // Verify nonce from verification table to prevent replay attacks\n const verification =\n await ctx.context.internalAdapter.findVerificationValue(\n `siws:${walletAddress}`\n );\n\n if (!verification || new Date() > verification.expiresAt) {\n throw new APIError(\"UNAUTHORIZED\", {\n message: \"Invalid or expired nonce. Please request a new one.\",\n });\n }\n\n const isValid = verifySIWS(input, output);\n if (!isValid) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"Invalid signature\",\n });\n }\n\n // Delete used nonce to prevent reuse\n await ctx.context.internalAdapter.deleteVerificationByIdentifier(\n `siws:${walletAddress}`\n );\n\n const existing = await ctx.context.adapter.findOne({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"accountId\", operator: \"eq\", value: walletAddress },\n ],\n });\n\n if (existing) {\n if ((existing as any).userId === session.user.id) {\n throw new APIError(\"BAD_REQUEST\", {\n message: \"This wallet is already linked to your account\",\n });\n }\n throw new APIError(\"BAD_REQUEST\", {\n message: \"This wallet is already linked to another account\",\n });\n }\n\n await ctx.context.internalAdapter.createAccount({\n userId: session.user.id,\n providerId: \"solana\",\n accountId: walletAddress,\n createdAt: new Date(),\n updatedAt: new Date(),\n });\n\n return ctx.json({ success: true, walletAddress });\n }\n ),\n\n unlinkSolanaWallet: createAuthEndpoint(\n \"/siws/unlink\",\n {\n method: \"POST\",\n body: z.object({\n walletAddress: z.string().min(32).max(44),\n }),\n requireRequest: true,\n },\n async (ctx: any) => {\n const session = await getSessionFromCtx(ctx);\n if (!session?.user) {\n throw new APIError(\"UNAUTHORIZED\", { message: \"Not authenticated\" });\n }\n\n const { walletAddress } = ctx.body;\n\n const account = await ctx.context.adapter.findOne({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"accountId\", operator: \"eq\", value: walletAddress },\n { field: \"userId\", operator: \"eq\", value: session.user.id },\n ],\n });\n\n if (!account) {\n throw new APIError(\"NOT_FOUND\", {\n message: \"Wallet not found on your account\",\n });\n }\n\n await ctx.context.adapter.delete({\n model: \"account\",\n where: [\n { field: \"id\", operator: \"eq\", value: (account as any).id },\n ],\n });\n\n return ctx.json({ success: true });\n }\n ),\n\n getLinkedSolanaWallets: createAuthEndpoint(\n \"/siws/wallets\",\n {\n method: \"GET\",\n requireRequest: true,\n },\n async (ctx: any) => {\n const session = await getSessionFromCtx(ctx);\n if (!session?.user) {\n throw new APIError(\"UNAUTHORIZED\", { message: \"Not authenticated\" });\n }\n\n const accounts = await ctx.context.adapter.findMany({\n model: \"account\",\n where: [\n { field: \"providerId\", operator: \"eq\", value: \"solana\" },\n { field: \"userId\", operator: \"eq\", value: session.user.id },\n ],\n });\n\n const wallets = (accounts as any[]).map((a) => ({\n walletAddress: a.accountId,\n createdAt: a.createdAt,\n }));\n\n return ctx.json({ wallets });\n }\n ),\n },\n});\n"],"mappings":"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","names":[]}
|
package/dist/react/index.cjs
CHANGED
|
@@ -69,9 +69,11 @@ function serializeOutput(output) {
|
|
|
69
69
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
70
70
|
function SolanaSignInInner({
|
|
71
71
|
baseURL = "",
|
|
72
|
+
basePath = "/api/auth",
|
|
72
73
|
onSuccess,
|
|
73
74
|
onError,
|
|
74
75
|
className,
|
|
76
|
+
disabled,
|
|
75
77
|
children
|
|
76
78
|
}) {
|
|
77
79
|
const { publicKey, connected, disconnect, wallet, connecting, connect } = (0, import_wallet_adapter_react2.useWallet)();
|
|
@@ -125,7 +127,7 @@ function SolanaSignInInner({
|
|
|
125
127
|
}
|
|
126
128
|
setLoading(true);
|
|
127
129
|
try {
|
|
128
|
-
const nonceRes = await fetch(`${baseURL}/
|
|
130
|
+
const nonceRes = await fetch(`${baseURL}${basePath}/siws/nonce`, {
|
|
129
131
|
method: "POST",
|
|
130
132
|
headers: { "Content-Type": "application/json" },
|
|
131
133
|
body: JSON.stringify({ walletAddress: publicKey.toBase58() })
|
|
@@ -141,7 +143,7 @@ function SolanaSignInInner({
|
|
|
141
143
|
};
|
|
142
144
|
const output = await adapter.signIn(input);
|
|
143
145
|
const serialized = serializeOutput(output);
|
|
144
|
-
const verifyRes = await fetch(`${baseURL}/
|
|
146
|
+
const verifyRes = await fetch(`${baseURL}${basePath}/siws/verify`, {
|
|
145
147
|
method: "POST",
|
|
146
148
|
headers: { "Content-Type": "application/json" },
|
|
147
149
|
body: JSON.stringify({ input, output: serialized })
|
|
@@ -167,7 +169,7 @@ function SolanaSignInInner({
|
|
|
167
169
|
"button",
|
|
168
170
|
{
|
|
169
171
|
onClick: handleClick,
|
|
170
|
-
disabled: loading,
|
|
172
|
+
disabled: loading || disabled,
|
|
171
173
|
className,
|
|
172
174
|
type: "button",
|
|
173
175
|
children: children ?? (loading ? "Authenticating..." : "Sign In with Solana")
|
|
@@ -189,11 +191,13 @@ var import_wallet_adapter_react_ui3 = require("@solana/wallet-adapter-react-ui")
|
|
|
189
191
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
190
192
|
function LinkWalletInner({
|
|
191
193
|
baseURL = "",
|
|
194
|
+
basePath = "/api/auth",
|
|
192
195
|
onLink,
|
|
193
196
|
onUnlink,
|
|
194
197
|
onError,
|
|
195
198
|
className,
|
|
196
199
|
unlinkClassName,
|
|
200
|
+
disabled,
|
|
197
201
|
renderLinked,
|
|
198
202
|
children
|
|
199
203
|
}) {
|
|
@@ -205,7 +209,7 @@ function LinkWalletInner({
|
|
|
205
209
|
const [hasTriggered, setHasTriggered] = (0, import_react3.useState)(false);
|
|
206
210
|
const fetchWallets = async () => {
|
|
207
211
|
try {
|
|
208
|
-
const res = await fetch(`${baseURL}/
|
|
212
|
+
const res = await fetch(`${baseURL}${basePath}/siws/wallets`);
|
|
209
213
|
const data = await res.json();
|
|
210
214
|
if (data.wallets?.[0]) {
|
|
211
215
|
setLinkedWallet(data.wallets[0].walletAddress);
|
|
@@ -257,7 +261,7 @@ function LinkWalletInner({
|
|
|
257
261
|
return;
|
|
258
262
|
}
|
|
259
263
|
try {
|
|
260
|
-
const createRes = await fetch(`${baseURL}/
|
|
264
|
+
const createRes = await fetch(`${baseURL}${basePath}/siws/nonce`, {
|
|
261
265
|
method: "POST",
|
|
262
266
|
headers: { "Content-Type": "application/json" },
|
|
263
267
|
body: JSON.stringify({
|
|
@@ -282,7 +286,7 @@ function LinkWalletInner({
|
|
|
282
286
|
signature: Array.from(output.signature),
|
|
283
287
|
signedMessage: Array.from(output.signedMessage)
|
|
284
288
|
};
|
|
285
|
-
const linkRes = await fetch(`${baseURL}/
|
|
289
|
+
const linkRes = await fetch(`${baseURL}${basePath}/siws/link`, {
|
|
286
290
|
method: "POST",
|
|
287
291
|
headers: { "Content-Type": "application/json" },
|
|
288
292
|
body: JSON.stringify({ input, output: serializedOutput })
|
|
@@ -308,7 +312,7 @@ function LinkWalletInner({
|
|
|
308
312
|
const handleUnlink = async () => {
|
|
309
313
|
if (!linkedWallet) return;
|
|
310
314
|
try {
|
|
311
|
-
const res = await fetch(`${baseURL}/
|
|
315
|
+
const res = await fetch(`${baseURL}${basePath}/siws/unlink`, {
|
|
312
316
|
method: "POST",
|
|
313
317
|
headers: { "Content-Type": "application/json" },
|
|
314
318
|
body: JSON.stringify({ walletAddress: linkedWallet })
|
|
@@ -331,14 +335,14 @@ function LinkWalletInner({
|
|
|
331
335
|
const truncated = `${linkedWallet.slice(0, 4)}...${linkedWallet.slice(-4)}`;
|
|
332
336
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
333
337
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontFamily: "monospace", fontSize: "0.875rem" }, children: truncated }),
|
|
334
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: handleUnlink, className: unlinkClassName, type: "button", children: "Unlink" })
|
|
338
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: handleUnlink, disabled, className: unlinkClassName, type: "button", children: "Unlink" })
|
|
335
339
|
] });
|
|
336
340
|
}
|
|
337
341
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
338
342
|
"button",
|
|
339
343
|
{
|
|
340
344
|
onClick: handleLinkClick,
|
|
341
|
-
disabled: linking,
|
|
345
|
+
disabled: linking || disabled,
|
|
342
346
|
className,
|
|
343
347
|
type: "button",
|
|
344
348
|
children: children ?? (linking ? "Linking..." : "Link Solana Wallet")
|
package/dist/react/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/index.ts","../../src/react/provider.tsx","../../src/react/sign-in.tsx","../../src/types.ts","../../src/react/link-wallet.tsx"],"sourcesContent":["export { SolanaProvider } from \"./provider\";\nexport type { SolanaProviderProps } from \"./provider\";\n\nexport { SolanaSignInButton } from \"./sign-in\";\nexport type { SolanaSignInButtonProps } from \"./sign-in\";\n\nexport { SolanaLinkWallet } from \"./link-wallet\";\nexport type { SolanaLinkWalletProps } from \"./link-wallet\";\n","\"use client\";\n\nimport { type FC, type ReactNode, useMemo } from \"react\";\nimport {\n ConnectionProvider,\n WalletProvider,\n} from \"@solana/wallet-adapter-react\";\nimport { WalletModalProvider } from \"@solana/wallet-adapter-react-ui\";\nimport { clusterApiUrl } from \"@solana/web3.js\";\n\nimport \"@solana/wallet-adapter-react-ui/styles.css\";\n\nexport type SolanaProviderProps = {\n children: ReactNode;\n cluster?: \"mainnet-beta\" | \"devnet\" | \"testnet\";\n endpoint?: string;\n autoConnect?: boolean;\n};\n\nexport const SolanaProvider: FC<SolanaProviderProps> = ({\n children,\n cluster = \"mainnet-beta\",\n endpoint,\n autoConnect = false,\n}) => {\n const rpcEndpoint = useMemo(\n () => endpoint ?? clusterApiUrl(cluster),\n [endpoint, cluster]\n );\n const wallets = useMemo(() => [], []);\n\n return (\n <ConnectionProvider endpoint={rpcEndpoint}>\n <WalletProvider wallets={wallets} autoConnect={autoConnect}>\n <WalletModalProvider>{children}</WalletModalProvider>\n </WalletProvider>\n </ConnectionProvider>\n );\n};\n","\"use client\";\n\nimport { useEffect, useState, type FC } from \"react\";\nimport { useWallet } from \"@solana/wallet-adapter-react\";\nimport { useWalletModal } from \"@solana/wallet-adapter-react-ui\";\nimport type { SolanaSignInInput } from \"@solana/wallet-standard-features\";\nimport { serializeOutput } from \"../types\";\nimport { SolanaProvider, type SolanaProviderProps } from \"./provider\";\n\nexport type SolanaSignInButtonProps = {\n baseURL?: string;\n onSuccess?: (user: { id: string; name: string; email: string }) => void;\n onError?: (error: string) => void;\n className?: string;\n children?: React.ReactNode;\n cluster?: SolanaProviderProps[\"cluster\"];\n endpoint?: SolanaProviderProps[\"endpoint\"];\n};\n\nfunction SolanaSignInInner({\n baseURL = \"\",\n onSuccess,\n onError,\n className,\n children,\n}: Omit<SolanaSignInButtonProps, \"cluster\" | \"endpoint\">) {\n const { publicKey, connected, disconnect, wallet, connecting, connect } =\n useWallet();\n const { setVisible, visible } = useWalletModal();\n const [loading, setLoading] = useState(false);\n const [signingIn, setSigningIn] = useState(false);\n const [hasTriggered, setHasTriggered] = useState(false);\n\n useEffect(() => {\n if (connected && publicKey && signingIn && !hasTriggered) {\n setHasTriggered(true);\n setVisible(false);\n handleSignIn();\n }\n }, [connected, publicKey, signingIn, wallet, hasTriggered]);\n\n useEffect(() => {\n if (wallet && !connected && !connecting && signingIn) {\n connect().catch(console.error);\n }\n }, [wallet, connected, connecting, signingIn, connect]);\n\n useEffect(() => {\n if (!visible && signingIn && !connected && !connecting && !wallet) {\n setSigningIn(false);\n setHasTriggered(false);\n }\n }, [visible, signingIn, connected, connecting, wallet]);\n\n const handleClick = async () => {\n setSigningIn(true);\n if (wallet && !connected) {\n try {\n await connect();\n } catch {\n setVisible(true);\n }\n } else if (!wallet) {\n setVisible(true);\n }\n };\n\n const handleSignIn = async () => {\n if (!publicKey || !wallet?.adapter) {\n setSigningIn(false);\n return;\n }\n\n const adapter = wallet.adapter as any;\n if (!(\"signIn\" in adapter) || typeof adapter.signIn !== \"function\") {\n onError?.(\n \"Your wallet does not support Sign In With Solana. Please use Phantom v23.11.0 or later.\"\n );\n setSigningIn(false);\n disconnect();\n return;\n }\n\n setLoading(true);\n\n try {\n const nonceRes = await fetch(`${baseURL}/api/auth/siws/nonce`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ walletAddress: publicKey.toBase58() }),\n });\n const nonceData = await nonceRes.json();\n\n const input: SolanaSignInInput = {\n domain: nonceData.domain,\n statement: nonceData.statement,\n uri: nonceData.uri,\n nonce: nonceData.nonce,\n chainId: nonceData.chainId,\n issuedAt: nonceData.issuedAt,\n };\n\n const output = await adapter.signIn(input);\n const serialized = serializeOutput(output);\n\n const verifyRes = await fetch(`${baseURL}/api/auth/siws/verify`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ input, output: serialized }),\n });\n\n const data = await verifyRes.json();\n\n if (!verifyRes.ok) {\n onError?.(data.message || data.error || \"Failed to sign in with Solana\");\n } else {\n onSuccess?.(data.user);\n }\n } catch (error: any) {\n if (error?.message !== \"User rejected the request.\") {\n onError?.(error?.message || \"Failed to sign in with Solana wallet\");\n }\n } finally {\n setLoading(false);\n setSigningIn(false);\n setHasTriggered(false);\n disconnect();\n }\n };\n\n return (\n <button\n onClick={handleClick}\n disabled={loading}\n className={className}\n type=\"button\"\n >\n {children ?? (loading ? \"Authenticating...\" : \"Sign In with Solana\")}\n </button>\n );\n}\n\nexport const SolanaSignInButton: FC<SolanaSignInButtonProps> = ({\n cluster,\n endpoint,\n ...props\n}) => {\n return (\n <SolanaProvider cluster={cluster} endpoint={endpoint}>\n <SolanaSignInInner {...props} />\n </SolanaProvider>\n );\n};\n","import type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\n\nexport type { SolanaSignInInput, SolanaSignInOutput };\n\nexport interface SiwsPluginOptions {\n statement?: string;\n chainId?: string;\n nonceExpiryMs?: number;\n anonymous?: boolean;\n}\n\nexport interface SiwsNonceResponse {\n nonce: string;\n domain: string;\n uri: string;\n statement: string;\n chainId: string;\n issuedAt: string;\n}\n\nexport interface SiwsVerifyRequest {\n output: {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n };\n input: {\n domain: string;\n address?: string;\n statement?: string;\n uri?: string;\n version?: string;\n chainId?: string;\n nonce?: string;\n issuedAt?: string;\n expirationTime?: string;\n notBefore?: string;\n requestId?: string;\n resources?: string[];\n };\n}\n\nexport interface SiwsVerifyResponse {\n success: boolean;\n user: {\n id: string;\n name: string;\n email: string;\n };\n}\n\nexport interface SiwsLinkRequest {\n input: SolanaSignInInput;\n output: {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n };\n}\n\nexport interface SiwsLinkedWallet {\n walletAddress: string;\n createdAt: Date;\n}\n\nexport interface SerializedSolanaOutput {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n signatureType?: string;\n}\n\nexport function serializeOutput(output: SolanaSignInOutput): SerializedSolanaOutput {\n return {\n account: {\n address: output.account.address,\n publicKey: Array.from(output.account.publicKey),\n },\n signature: Array.from(output.signature),\n signedMessage: Array.from(output.signedMessage),\n signatureType: (output as any).signatureType,\n };\n}\n\nexport function deserializeOutput(data: SerializedSolanaOutput): SolanaSignInOutput {\n return {\n account: {\n address: data.account.address,\n publicKey: new Uint8Array(data.account.publicKey),\n chains: [],\n features: [],\n },\n signature: new Uint8Array(data.signature),\n signedMessage: new Uint8Array(data.signedMessage),\n };\n}\n","\"use client\";\n\nimport { useEffect, useState, type FC } from \"react\";\nimport { useWallet } from \"@solana/wallet-adapter-react\";\nimport { useWalletModal } from \"@solana/wallet-adapter-react-ui\";\nimport type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\nimport { SolanaProvider, type SolanaProviderProps } from \"./provider\";\n\nexport type SolanaLinkWalletProps = {\n baseURL?: string;\n onLink?: (walletAddress: string) => void;\n onUnlink?: () => void;\n onError?: (error: string) => void;\n className?: string;\n unlinkClassName?: string;\n cluster?: SolanaProviderProps[\"cluster\"];\n endpoint?: SolanaProviderProps[\"endpoint\"];\n renderLinked?: (wallet: { address: string; onUnlink: () => void }) => React.ReactNode;\n children?: React.ReactNode;\n};\n\nfunction LinkWalletInner({\n baseURL = \"\",\n onLink,\n onUnlink,\n onError,\n className,\n unlinkClassName,\n renderLinked,\n children,\n}: Omit<SolanaLinkWalletProps, \"cluster\" | \"endpoint\">) {\n const { publicKey, connected, disconnect, wallet, connecting, connect } =\n useWallet();\n const { setVisible, visible } = useWalletModal();\n const [linkedWallet, setLinkedWallet] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [linking, setLinking] = useState(false);\n const [hasTriggered, setHasTriggered] = useState(false);\n\n const fetchWallets = async () => {\n try {\n const res = await fetch(`${baseURL}/api/auth/siws/wallets`);\n const data = await res.json();\n if (data.wallets?.[0]) {\n setLinkedWallet(data.wallets[0].walletAddress);\n }\n } catch {}\n setLoading(false);\n };\n\n useEffect(() => {\n fetchWallets();\n }, []);\n\n useEffect(() => {\n if (connected && publicKey && linking && !hasTriggered) {\n setHasTriggered(true);\n setVisible(false);\n handleLink();\n }\n }, [connected, publicKey, linking, wallet, hasTriggered]);\n\n useEffect(() => {\n if (wallet && !connected && !connecting && linking) {\n connect().catch(console.error);\n }\n }, [wallet, connected, connecting, linking, connect]);\n\n useEffect(() => {\n if (!visible && linking && !connected && !connecting && !wallet) {\n setLinking(false);\n setHasTriggered(false);\n }\n }, [visible, linking, connected, connecting, wallet]);\n\n const handleLinkClick = async () => {\n setLinking(true);\n if (wallet && !connected) {\n try {\n await connect();\n } catch {\n setVisible(true);\n }\n } else if (!wallet) {\n setVisible(true);\n }\n };\n\n const handleLink = async () => {\n if (!wallet?.adapter) return;\n\n const adapter = wallet.adapter as any;\n if (!(\"signIn\" in adapter)) {\n onError?.(\"Wallet doesn't support SIWS\");\n setLinking(false);\n disconnect();\n return;\n }\n\n try {\n const createRes = await fetch(`${baseURL}/api/auth/siws/nonce`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n walletAddress: publicKey!.toBase58(),\n }),\n });\n const nonceData = await createRes.json();\n\n const input: SolanaSignInInput = {\n domain: nonceData.domain,\n statement: nonceData.statement,\n uri: nonceData.uri,\n nonce: nonceData.nonce,\n chainId: nonceData.chainId,\n issuedAt: nonceData.issuedAt,\n };\n\n const output: SolanaSignInOutput = await adapter.signIn(input);\n\n const serializedOutput = {\n account: {\n address: output.account.address,\n publicKey: Array.from(output.account.publicKey),\n },\n signature: Array.from(output.signature),\n signedMessage: Array.from(output.signedMessage),\n };\n\n const linkRes = await fetch(`${baseURL}/api/auth/siws/link`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ input, output: serializedOutput }),\n });\n\n if (linkRes.ok) {\n const data = await linkRes.json();\n setLinkedWallet(data.walletAddress);\n onLink?.(data.walletAddress);\n } else {\n const err = await linkRes.json();\n onError?.(err.message || \"Failed to link wallet\");\n }\n } catch (e: any) {\n if (!e?.message?.includes(\"rejected\")) {\n onError?.(\"Failed to link wallet\");\n }\n } finally {\n setLinking(false);\n setHasTriggered(false);\n disconnect();\n }\n };\n\n const handleUnlink = async () => {\n if (!linkedWallet) return;\n try {\n const res = await fetch(`${baseURL}/api/auth/siws/unlink`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ walletAddress: linkedWallet }),\n });\n if (res.ok) {\n setLinkedWallet(null);\n onUnlink?.();\n }\n } catch {\n onError?.(\"Failed to unlink wallet\");\n }\n };\n\n if (loading) {\n return (\n <button disabled className={className}>\n Loading...\n </button>\n );\n }\n\n if (linkedWallet) {\n if (renderLinked) {\n return <>{renderLinked({ address: linkedWallet, onUnlink: handleUnlink })}</>;\n }\n const truncated = `${linkedWallet.slice(0, 4)}...${linkedWallet.slice(-4)}`;\n return (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: \"0.5rem\" }}>\n <span style={{ fontFamily: \"monospace\", fontSize: \"0.875rem\" }}>\n {truncated}\n </span>\n <button onClick={handleUnlink} className={unlinkClassName} type=\"button\">\n Unlink\n </button>\n </div>\n );\n }\n\n return (\n <button\n onClick={handleLinkClick}\n disabled={linking}\n className={className}\n type=\"button\"\n >\n {children ?? (linking ? \"Linking...\" : \"Link Solana Wallet\")}\n </button>\n );\n}\n\nexport const SolanaLinkWallet: FC<SolanaLinkWalletProps> = ({\n cluster,\n endpoint,\n ...props\n}) => {\n return (\n <SolanaProvider cluster={cluster} endpoint={endpoint}>\n <LinkWalletInner {...props} />\n </SolanaProvider>\n );\n};\n"],"mappings":"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","names":["import_react","import_wallet_adapter_react","import_wallet_adapter_react_ui","import_jsx_runtime","import_react","import_wallet_adapter_react","import_wallet_adapter_react_ui","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../../src/react/index.ts","../../src/react/provider.tsx","../../src/react/sign-in.tsx","../../src/types.ts","../../src/react/link-wallet.tsx"],"sourcesContent":["export { SolanaProvider } from \"./provider\";\nexport type { SolanaProviderProps } from \"./provider\";\n\nexport { SolanaSignInButton } from \"./sign-in\";\nexport type { SolanaSignInButtonProps } from \"./sign-in\";\n\nexport { SolanaLinkWallet } from \"./link-wallet\";\nexport type { SolanaLinkWalletProps } from \"./link-wallet\";\n","\"use client\";\n\nimport { type FC, type ReactNode, useMemo } from \"react\";\nimport {\n ConnectionProvider,\n WalletProvider,\n} from \"@solana/wallet-adapter-react\";\nimport { WalletModalProvider } from \"@solana/wallet-adapter-react-ui\";\nimport { clusterApiUrl } from \"@solana/web3.js\";\n\nimport \"@solana/wallet-adapter-react-ui/styles.css\";\n\nexport type SolanaProviderProps = {\n children: ReactNode;\n cluster?: \"mainnet-beta\" | \"devnet\" | \"testnet\";\n endpoint?: string;\n autoConnect?: boolean;\n};\n\nexport const SolanaProvider: FC<SolanaProviderProps> = ({\n children,\n cluster = \"mainnet-beta\",\n endpoint,\n autoConnect = false,\n}) => {\n const rpcEndpoint = useMemo(\n () => endpoint ?? clusterApiUrl(cluster),\n [endpoint, cluster]\n );\n const wallets = useMemo(() => [], []);\n\n return (\n <ConnectionProvider endpoint={rpcEndpoint}>\n <WalletProvider wallets={wallets} autoConnect={autoConnect}>\n <WalletModalProvider>{children}</WalletModalProvider>\n </WalletProvider>\n </ConnectionProvider>\n );\n};\n","\"use client\";\n\nimport { useEffect, useState, type FC } from \"react\";\nimport { useWallet } from \"@solana/wallet-adapter-react\";\nimport { useWalletModal } from \"@solana/wallet-adapter-react-ui\";\nimport type { SolanaSignInInput } from \"@solana/wallet-standard-features\";\nimport { serializeOutput } from \"../types\";\nimport { SolanaProvider, type SolanaProviderProps } from \"./provider\";\n\nexport type SolanaSignInButtonProps = {\n baseURL?: string;\n basePath?: string;\n onSuccess?: (user: { id: string; name: string; email: string }) => void;\n onError?: (error: string) => void;\n className?: string;\n disabled?: boolean;\n children?: React.ReactNode;\n cluster?: SolanaProviderProps[\"cluster\"];\n endpoint?: SolanaProviderProps[\"endpoint\"];\n};\n\nfunction SolanaSignInInner({\n baseURL = \"\",\n basePath = \"/api/auth\",\n onSuccess,\n onError,\n className,\n disabled,\n children,\n}: Omit<SolanaSignInButtonProps, \"cluster\" | \"endpoint\">) {\n const { publicKey, connected, disconnect, wallet, connecting, connect } =\n useWallet();\n const { setVisible, visible } = useWalletModal();\n const [loading, setLoading] = useState(false);\n const [signingIn, setSigningIn] = useState(false);\n const [hasTriggered, setHasTriggered] = useState(false);\n\n useEffect(() => {\n if (connected && publicKey && signingIn && !hasTriggered) {\n setHasTriggered(true);\n setVisible(false);\n handleSignIn();\n }\n }, [connected, publicKey, signingIn, wallet, hasTriggered]);\n\n useEffect(() => {\n if (wallet && !connected && !connecting && signingIn) {\n connect().catch(console.error);\n }\n }, [wallet, connected, connecting, signingIn, connect]);\n\n useEffect(() => {\n if (!visible && signingIn && !connected && !connecting && !wallet) {\n setSigningIn(false);\n setHasTriggered(false);\n }\n }, [visible, signingIn, connected, connecting, wallet]);\n\n const handleClick = async () => {\n setSigningIn(true);\n if (wallet && !connected) {\n try {\n await connect();\n } catch {\n setVisible(true);\n }\n } else if (!wallet) {\n setVisible(true);\n }\n };\n\n const handleSignIn = async () => {\n if (!publicKey || !wallet?.adapter) {\n setSigningIn(false);\n return;\n }\n\n const adapter = wallet.adapter as any;\n if (!(\"signIn\" in adapter) || typeof adapter.signIn !== \"function\") {\n onError?.(\n \"Your wallet does not support Sign In With Solana. Please use Phantom v23.11.0 or later.\"\n );\n setSigningIn(false);\n disconnect();\n return;\n }\n\n setLoading(true);\n\n try {\n const nonceRes = await fetch(`${baseURL}${basePath}/siws/nonce`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ walletAddress: publicKey.toBase58() }),\n });\n const nonceData = await nonceRes.json();\n\n const input: SolanaSignInInput = {\n domain: nonceData.domain,\n statement: nonceData.statement,\n uri: nonceData.uri,\n nonce: nonceData.nonce,\n chainId: nonceData.chainId,\n issuedAt: nonceData.issuedAt,\n };\n\n const output = await adapter.signIn(input);\n const serialized = serializeOutput(output);\n\n const verifyRes = await fetch(`${baseURL}${basePath}/siws/verify`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ input, output: serialized }),\n });\n\n const data = await verifyRes.json();\n\n if (!verifyRes.ok) {\n onError?.(data.message || data.error || \"Failed to sign in with Solana\");\n } else {\n onSuccess?.(data.user);\n }\n } catch (error: any) {\n if (error?.message !== \"User rejected the request.\") {\n onError?.(error?.message || \"Failed to sign in with Solana wallet\");\n }\n } finally {\n setLoading(false);\n setSigningIn(false);\n setHasTriggered(false);\n disconnect();\n }\n };\n\n return (\n <button\n onClick={handleClick}\n disabled={loading || disabled}\n className={className}\n type=\"button\"\n >\n {children ?? (loading ? \"Authenticating...\" : \"Sign In with Solana\")}\n </button>\n );\n}\n\nexport const SolanaSignInButton: FC<SolanaSignInButtonProps> = ({\n cluster,\n endpoint,\n ...props\n}) => {\n return (\n <SolanaProvider cluster={cluster} endpoint={endpoint}>\n <SolanaSignInInner {...props} />\n </SolanaProvider>\n );\n};\n","import type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\n\nexport type { SolanaSignInInput, SolanaSignInOutput };\n\nexport interface SiwsPluginOptions {\n statement?: string;\n chainId?: string;\n nonceExpiryMs?: number;\n anonymous?: boolean;\n}\n\nexport interface SiwsNonceResponse {\n nonce: string;\n domain: string;\n uri: string;\n statement: string;\n chainId: string;\n issuedAt: string;\n}\n\nexport interface SiwsVerifyRequest {\n output: {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n };\n input: {\n domain: string;\n address?: string;\n statement?: string;\n uri?: string;\n version?: string;\n chainId?: string;\n nonce?: string;\n issuedAt?: string;\n expirationTime?: string;\n notBefore?: string;\n requestId?: string;\n resources?: string[];\n };\n}\n\nexport interface SiwsVerifyResponse {\n success: boolean;\n user: {\n id: string;\n name: string;\n email: string;\n };\n}\n\nexport interface SiwsLinkRequest {\n input: SolanaSignInInput;\n output: {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n };\n}\n\nexport interface SiwsLinkedWallet {\n walletAddress: string;\n createdAt: Date;\n}\n\nexport interface SerializedSolanaOutput {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n signatureType?: string;\n}\n\nexport function serializeOutput(output: SolanaSignInOutput): SerializedSolanaOutput {\n return {\n account: {\n address: output.account.address,\n publicKey: Array.from(output.account.publicKey),\n },\n signature: Array.from(output.signature),\n signedMessage: Array.from(output.signedMessage),\n signatureType: (output as any).signatureType,\n };\n}\n\nexport function deserializeOutput(data: SerializedSolanaOutput): SolanaSignInOutput {\n return {\n account: {\n address: data.account.address,\n publicKey: new Uint8Array(data.account.publicKey),\n chains: [],\n features: [],\n },\n signature: new Uint8Array(data.signature),\n signedMessage: new Uint8Array(data.signedMessage),\n };\n}\n","\"use client\";\n\nimport { useEffect, useState, type FC } from \"react\";\nimport { useWallet } from \"@solana/wallet-adapter-react\";\nimport { useWalletModal } from \"@solana/wallet-adapter-react-ui\";\nimport type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\nimport { SolanaProvider, type SolanaProviderProps } from \"./provider\";\n\nexport type SolanaLinkWalletProps = {\n baseURL?: string;\n basePath?: string;\n onLink?: (walletAddress: string) => void;\n onUnlink?: () => void;\n onError?: (error: string) => void;\n className?: string;\n unlinkClassName?: string;\n disabled?: boolean;\n cluster?: SolanaProviderProps[\"cluster\"];\n endpoint?: SolanaProviderProps[\"endpoint\"];\n renderLinked?: (wallet: { address: string; onUnlink: () => void }) => React.ReactNode;\n children?: React.ReactNode;\n};\n\nfunction LinkWalletInner({\n baseURL = \"\",\n basePath = \"/api/auth\",\n onLink,\n onUnlink,\n onError,\n className,\n unlinkClassName,\n disabled,\n renderLinked,\n children,\n}: Omit<SolanaLinkWalletProps, \"cluster\" | \"endpoint\">) {\n const { publicKey, connected, disconnect, wallet, connecting, connect } =\n useWallet();\n const { setVisible, visible } = useWalletModal();\n const [linkedWallet, setLinkedWallet] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [linking, setLinking] = useState(false);\n const [hasTriggered, setHasTriggered] = useState(false);\n\n const fetchWallets = async () => {\n try {\n const res = await fetch(`${baseURL}${basePath}/siws/wallets`);\n const data = await res.json();\n if (data.wallets?.[0]) {\n setLinkedWallet(data.wallets[0].walletAddress);\n }\n } catch {}\n setLoading(false);\n };\n\n useEffect(() => {\n fetchWallets();\n }, []);\n\n useEffect(() => {\n if (connected && publicKey && linking && !hasTriggered) {\n setHasTriggered(true);\n setVisible(false);\n handleLink();\n }\n }, [connected, publicKey, linking, wallet, hasTriggered]);\n\n useEffect(() => {\n if (wallet && !connected && !connecting && linking) {\n connect().catch(console.error);\n }\n }, [wallet, connected, connecting, linking, connect]);\n\n useEffect(() => {\n if (!visible && linking && !connected && !connecting && !wallet) {\n setLinking(false);\n setHasTriggered(false);\n }\n }, [visible, linking, connected, connecting, wallet]);\n\n const handleLinkClick = async () => {\n setLinking(true);\n if (wallet && !connected) {\n try {\n await connect();\n } catch {\n setVisible(true);\n }\n } else if (!wallet) {\n setVisible(true);\n }\n };\n\n const handleLink = async () => {\n if (!wallet?.adapter) return;\n\n const adapter = wallet.adapter as any;\n if (!(\"signIn\" in adapter)) {\n onError?.(\"Wallet doesn't support SIWS\");\n setLinking(false);\n disconnect();\n return;\n }\n\n try {\n const createRes = await fetch(`${baseURL}${basePath}/siws/nonce`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n walletAddress: publicKey!.toBase58(),\n }),\n });\n const nonceData = await createRes.json();\n\n const input: SolanaSignInInput = {\n domain: nonceData.domain,\n statement: nonceData.statement,\n uri: nonceData.uri,\n nonce: nonceData.nonce,\n chainId: nonceData.chainId,\n issuedAt: nonceData.issuedAt,\n };\n\n const output: SolanaSignInOutput = await adapter.signIn(input);\n\n const serializedOutput = {\n account: {\n address: output.account.address,\n publicKey: Array.from(output.account.publicKey),\n },\n signature: Array.from(output.signature),\n signedMessage: Array.from(output.signedMessage),\n };\n\n const linkRes = await fetch(`${baseURL}${basePath}/siws/link`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ input, output: serializedOutput }),\n });\n\n if (linkRes.ok) {\n const data = await linkRes.json();\n setLinkedWallet(data.walletAddress);\n onLink?.(data.walletAddress);\n } else {\n const err = await linkRes.json();\n onError?.(err.message || \"Failed to link wallet\");\n }\n } catch (e: any) {\n if (!e?.message?.includes(\"rejected\")) {\n onError?.(\"Failed to link wallet\");\n }\n } finally {\n setLinking(false);\n setHasTriggered(false);\n disconnect();\n }\n };\n\n const handleUnlink = async () => {\n if (!linkedWallet) return;\n try {\n const res = await fetch(`${baseURL}${basePath}/siws/unlink`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ walletAddress: linkedWallet }),\n });\n if (res.ok) {\n setLinkedWallet(null);\n onUnlink?.();\n }\n } catch {\n onError?.(\"Failed to unlink wallet\");\n }\n };\n\n if (loading) {\n return (\n <button disabled className={className}>\n Loading...\n </button>\n );\n }\n\n if (linkedWallet) {\n if (renderLinked) {\n return <>{renderLinked({ address: linkedWallet, onUnlink: handleUnlink })}</>;\n }\n const truncated = `${linkedWallet.slice(0, 4)}...${linkedWallet.slice(-4)}`;\n return (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: \"0.5rem\" }}>\n <span style={{ fontFamily: \"monospace\", fontSize: \"0.875rem\" }}>\n {truncated}\n </span>\n <button onClick={handleUnlink} disabled={disabled} className={unlinkClassName} type=\"button\">\n Unlink\n </button>\n </div>\n );\n }\n\n return (\n <button\n onClick={handleLinkClick}\n disabled={linking || disabled}\n className={className}\n type=\"button\"\n >\n {children ?? (linking ? \"Linking...\" : \"Link Solana Wallet\")}\n </button>\n );\n}\n\nexport const SolanaLinkWallet: FC<SolanaLinkWalletProps> = ({\n cluster,\n endpoint,\n ...props\n}) => {\n return (\n <SolanaProvider cluster={cluster} endpoint={endpoint}>\n <LinkWalletInner {...props} />\n </SolanaProvider>\n );\n};\n"],"mappings":"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","names":["import_react","import_wallet_adapter_react","import_wallet_adapter_react_ui","import_jsx_runtime","import_react","import_wallet_adapter_react","import_wallet_adapter_react_ui","import_jsx_runtime"]}
|
package/dist/react/index.js
CHANGED
|
@@ -44,9 +44,11 @@ function serializeOutput(output) {
|
|
|
44
44
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
45
45
|
function SolanaSignInInner({
|
|
46
46
|
baseURL = "",
|
|
47
|
+
basePath = "/api/auth",
|
|
47
48
|
onSuccess,
|
|
48
49
|
onError,
|
|
49
50
|
className,
|
|
51
|
+
disabled,
|
|
50
52
|
children
|
|
51
53
|
}) {
|
|
52
54
|
const { publicKey, connected, disconnect, wallet, connecting, connect } = useWallet();
|
|
@@ -100,7 +102,7 @@ function SolanaSignInInner({
|
|
|
100
102
|
}
|
|
101
103
|
setLoading(true);
|
|
102
104
|
try {
|
|
103
|
-
const nonceRes = await fetch(`${baseURL}/
|
|
105
|
+
const nonceRes = await fetch(`${baseURL}${basePath}/siws/nonce`, {
|
|
104
106
|
method: "POST",
|
|
105
107
|
headers: { "Content-Type": "application/json" },
|
|
106
108
|
body: JSON.stringify({ walletAddress: publicKey.toBase58() })
|
|
@@ -116,7 +118,7 @@ function SolanaSignInInner({
|
|
|
116
118
|
};
|
|
117
119
|
const output = await adapter.signIn(input);
|
|
118
120
|
const serialized = serializeOutput(output);
|
|
119
|
-
const verifyRes = await fetch(`${baseURL}/
|
|
121
|
+
const verifyRes = await fetch(`${baseURL}${basePath}/siws/verify`, {
|
|
120
122
|
method: "POST",
|
|
121
123
|
headers: { "Content-Type": "application/json" },
|
|
122
124
|
body: JSON.stringify({ input, output: serialized })
|
|
@@ -142,7 +144,7 @@ function SolanaSignInInner({
|
|
|
142
144
|
"button",
|
|
143
145
|
{
|
|
144
146
|
onClick: handleClick,
|
|
145
|
-
disabled: loading,
|
|
147
|
+
disabled: loading || disabled,
|
|
146
148
|
className,
|
|
147
149
|
type: "button",
|
|
148
150
|
children: children ?? (loading ? "Authenticating..." : "Sign In with Solana")
|
|
@@ -164,11 +166,13 @@ import { useWalletModal as useWalletModal2 } from "@solana/wallet-adapter-react-
|
|
|
164
166
|
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
165
167
|
function LinkWalletInner({
|
|
166
168
|
baseURL = "",
|
|
169
|
+
basePath = "/api/auth",
|
|
167
170
|
onLink,
|
|
168
171
|
onUnlink,
|
|
169
172
|
onError,
|
|
170
173
|
className,
|
|
171
174
|
unlinkClassName,
|
|
175
|
+
disabled,
|
|
172
176
|
renderLinked,
|
|
173
177
|
children
|
|
174
178
|
}) {
|
|
@@ -180,7 +184,7 @@ function LinkWalletInner({
|
|
|
180
184
|
const [hasTriggered, setHasTriggered] = useState2(false);
|
|
181
185
|
const fetchWallets = async () => {
|
|
182
186
|
try {
|
|
183
|
-
const res = await fetch(`${baseURL}/
|
|
187
|
+
const res = await fetch(`${baseURL}${basePath}/siws/wallets`);
|
|
184
188
|
const data = await res.json();
|
|
185
189
|
if (data.wallets?.[0]) {
|
|
186
190
|
setLinkedWallet(data.wallets[0].walletAddress);
|
|
@@ -232,7 +236,7 @@ function LinkWalletInner({
|
|
|
232
236
|
return;
|
|
233
237
|
}
|
|
234
238
|
try {
|
|
235
|
-
const createRes = await fetch(`${baseURL}/
|
|
239
|
+
const createRes = await fetch(`${baseURL}${basePath}/siws/nonce`, {
|
|
236
240
|
method: "POST",
|
|
237
241
|
headers: { "Content-Type": "application/json" },
|
|
238
242
|
body: JSON.stringify({
|
|
@@ -257,7 +261,7 @@ function LinkWalletInner({
|
|
|
257
261
|
signature: Array.from(output.signature),
|
|
258
262
|
signedMessage: Array.from(output.signedMessage)
|
|
259
263
|
};
|
|
260
|
-
const linkRes = await fetch(`${baseURL}/
|
|
264
|
+
const linkRes = await fetch(`${baseURL}${basePath}/siws/link`, {
|
|
261
265
|
method: "POST",
|
|
262
266
|
headers: { "Content-Type": "application/json" },
|
|
263
267
|
body: JSON.stringify({ input, output: serializedOutput })
|
|
@@ -283,7 +287,7 @@ function LinkWalletInner({
|
|
|
283
287
|
const handleUnlink = async () => {
|
|
284
288
|
if (!linkedWallet) return;
|
|
285
289
|
try {
|
|
286
|
-
const res = await fetch(`${baseURL}/
|
|
290
|
+
const res = await fetch(`${baseURL}${basePath}/siws/unlink`, {
|
|
287
291
|
method: "POST",
|
|
288
292
|
headers: { "Content-Type": "application/json" },
|
|
289
293
|
body: JSON.stringify({ walletAddress: linkedWallet })
|
|
@@ -306,14 +310,14 @@ function LinkWalletInner({
|
|
|
306
310
|
const truncated = `${linkedWallet.slice(0, 4)}...${linkedWallet.slice(-4)}`;
|
|
307
311
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
308
312
|
/* @__PURE__ */ jsx3("span", { style: { fontFamily: "monospace", fontSize: "0.875rem" }, children: truncated }),
|
|
309
|
-
/* @__PURE__ */ jsx3("button", { onClick: handleUnlink, className: unlinkClassName, type: "button", children: "Unlink" })
|
|
313
|
+
/* @__PURE__ */ jsx3("button", { onClick: handleUnlink, disabled, className: unlinkClassName, type: "button", children: "Unlink" })
|
|
310
314
|
] });
|
|
311
315
|
}
|
|
312
316
|
return /* @__PURE__ */ jsx3(
|
|
313
317
|
"button",
|
|
314
318
|
{
|
|
315
319
|
onClick: handleLinkClick,
|
|
316
|
-
disabled: linking,
|
|
320
|
+
disabled: linking || disabled,
|
|
317
321
|
className,
|
|
318
322
|
type: "button",
|
|
319
323
|
children: children ?? (linking ? "Linking..." : "Link Solana Wallet")
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/provider.tsx","../../src/react/sign-in.tsx","../../src/types.ts","../../src/react/link-wallet.tsx"],"sourcesContent":["\"use client\";\n\nimport { type FC, type ReactNode, useMemo } from \"react\";\nimport {\n ConnectionProvider,\n WalletProvider,\n} from \"@solana/wallet-adapter-react\";\nimport { WalletModalProvider } from \"@solana/wallet-adapter-react-ui\";\nimport { clusterApiUrl } from \"@solana/web3.js\";\n\nimport \"@solana/wallet-adapter-react-ui/styles.css\";\n\nexport type SolanaProviderProps = {\n children: ReactNode;\n cluster?: \"mainnet-beta\" | \"devnet\" | \"testnet\";\n endpoint?: string;\n autoConnect?: boolean;\n};\n\nexport const SolanaProvider: FC<SolanaProviderProps> = ({\n children,\n cluster = \"mainnet-beta\",\n endpoint,\n autoConnect = false,\n}) => {\n const rpcEndpoint = useMemo(\n () => endpoint ?? clusterApiUrl(cluster),\n [endpoint, cluster]\n );\n const wallets = useMemo(() => [], []);\n\n return (\n <ConnectionProvider endpoint={rpcEndpoint}>\n <WalletProvider wallets={wallets} autoConnect={autoConnect}>\n <WalletModalProvider>{children}</WalletModalProvider>\n </WalletProvider>\n </ConnectionProvider>\n );\n};\n","\"use client\";\n\nimport { useEffect, useState, type FC } from \"react\";\nimport { useWallet } from \"@solana/wallet-adapter-react\";\nimport { useWalletModal } from \"@solana/wallet-adapter-react-ui\";\nimport type { SolanaSignInInput } from \"@solana/wallet-standard-features\";\nimport { serializeOutput } from \"../types\";\nimport { SolanaProvider, type SolanaProviderProps } from \"./provider\";\n\nexport type SolanaSignInButtonProps = {\n baseURL?: string;\n onSuccess?: (user: { id: string; name: string; email: string }) => void;\n onError?: (error: string) => void;\n className?: string;\n children?: React.ReactNode;\n cluster?: SolanaProviderProps[\"cluster\"];\n endpoint?: SolanaProviderProps[\"endpoint\"];\n};\n\nfunction SolanaSignInInner({\n baseURL = \"\",\n onSuccess,\n onError,\n className,\n children,\n}: Omit<SolanaSignInButtonProps, \"cluster\" | \"endpoint\">) {\n const { publicKey, connected, disconnect, wallet, connecting, connect } =\n useWallet();\n const { setVisible, visible } = useWalletModal();\n const [loading, setLoading] = useState(false);\n const [signingIn, setSigningIn] = useState(false);\n const [hasTriggered, setHasTriggered] = useState(false);\n\n useEffect(() => {\n if (connected && publicKey && signingIn && !hasTriggered) {\n setHasTriggered(true);\n setVisible(false);\n handleSignIn();\n }\n }, [connected, publicKey, signingIn, wallet, hasTriggered]);\n\n useEffect(() => {\n if (wallet && !connected && !connecting && signingIn) {\n connect().catch(console.error);\n }\n }, [wallet, connected, connecting, signingIn, connect]);\n\n useEffect(() => {\n if (!visible && signingIn && !connected && !connecting && !wallet) {\n setSigningIn(false);\n setHasTriggered(false);\n }\n }, [visible, signingIn, connected, connecting, wallet]);\n\n const handleClick = async () => {\n setSigningIn(true);\n if (wallet && !connected) {\n try {\n await connect();\n } catch {\n setVisible(true);\n }\n } else if (!wallet) {\n setVisible(true);\n }\n };\n\n const handleSignIn = async () => {\n if (!publicKey || !wallet?.adapter) {\n setSigningIn(false);\n return;\n }\n\n const adapter = wallet.adapter as any;\n if (!(\"signIn\" in adapter) || typeof adapter.signIn !== \"function\") {\n onError?.(\n \"Your wallet does not support Sign In With Solana. Please use Phantom v23.11.0 or later.\"\n );\n setSigningIn(false);\n disconnect();\n return;\n }\n\n setLoading(true);\n\n try {\n const nonceRes = await fetch(`${baseURL}/api/auth/siws/nonce`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ walletAddress: publicKey.toBase58() }),\n });\n const nonceData = await nonceRes.json();\n\n const input: SolanaSignInInput = {\n domain: nonceData.domain,\n statement: nonceData.statement,\n uri: nonceData.uri,\n nonce: nonceData.nonce,\n chainId: nonceData.chainId,\n issuedAt: nonceData.issuedAt,\n };\n\n const output = await adapter.signIn(input);\n const serialized = serializeOutput(output);\n\n const verifyRes = await fetch(`${baseURL}/api/auth/siws/verify`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ input, output: serialized }),\n });\n\n const data = await verifyRes.json();\n\n if (!verifyRes.ok) {\n onError?.(data.message || data.error || \"Failed to sign in with Solana\");\n } else {\n onSuccess?.(data.user);\n }\n } catch (error: any) {\n if (error?.message !== \"User rejected the request.\") {\n onError?.(error?.message || \"Failed to sign in with Solana wallet\");\n }\n } finally {\n setLoading(false);\n setSigningIn(false);\n setHasTriggered(false);\n disconnect();\n }\n };\n\n return (\n <button\n onClick={handleClick}\n disabled={loading}\n className={className}\n type=\"button\"\n >\n {children ?? (loading ? \"Authenticating...\" : \"Sign In with Solana\")}\n </button>\n );\n}\n\nexport const SolanaSignInButton: FC<SolanaSignInButtonProps> = ({\n cluster,\n endpoint,\n ...props\n}) => {\n return (\n <SolanaProvider cluster={cluster} endpoint={endpoint}>\n <SolanaSignInInner {...props} />\n </SolanaProvider>\n );\n};\n","import type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\n\nexport type { SolanaSignInInput, SolanaSignInOutput };\n\nexport interface SiwsPluginOptions {\n statement?: string;\n chainId?: string;\n nonceExpiryMs?: number;\n anonymous?: boolean;\n}\n\nexport interface SiwsNonceResponse {\n nonce: string;\n domain: string;\n uri: string;\n statement: string;\n chainId: string;\n issuedAt: string;\n}\n\nexport interface SiwsVerifyRequest {\n output: {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n };\n input: {\n domain: string;\n address?: string;\n statement?: string;\n uri?: string;\n version?: string;\n chainId?: string;\n nonce?: string;\n issuedAt?: string;\n expirationTime?: string;\n notBefore?: string;\n requestId?: string;\n resources?: string[];\n };\n}\n\nexport interface SiwsVerifyResponse {\n success: boolean;\n user: {\n id: string;\n name: string;\n email: string;\n };\n}\n\nexport interface SiwsLinkRequest {\n input: SolanaSignInInput;\n output: {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n };\n}\n\nexport interface SiwsLinkedWallet {\n walletAddress: string;\n createdAt: Date;\n}\n\nexport interface SerializedSolanaOutput {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n signatureType?: string;\n}\n\nexport function serializeOutput(output: SolanaSignInOutput): SerializedSolanaOutput {\n return {\n account: {\n address: output.account.address,\n publicKey: Array.from(output.account.publicKey),\n },\n signature: Array.from(output.signature),\n signedMessage: Array.from(output.signedMessage),\n signatureType: (output as any).signatureType,\n };\n}\n\nexport function deserializeOutput(data: SerializedSolanaOutput): SolanaSignInOutput {\n return {\n account: {\n address: data.account.address,\n publicKey: new Uint8Array(data.account.publicKey),\n chains: [],\n features: [],\n },\n signature: new Uint8Array(data.signature),\n signedMessage: new Uint8Array(data.signedMessage),\n };\n}\n","\"use client\";\n\nimport { useEffect, useState, type FC } from \"react\";\nimport { useWallet } from \"@solana/wallet-adapter-react\";\nimport { useWalletModal } from \"@solana/wallet-adapter-react-ui\";\nimport type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\nimport { SolanaProvider, type SolanaProviderProps } from \"./provider\";\n\nexport type SolanaLinkWalletProps = {\n baseURL?: string;\n onLink?: (walletAddress: string) => void;\n onUnlink?: () => void;\n onError?: (error: string) => void;\n className?: string;\n unlinkClassName?: string;\n cluster?: SolanaProviderProps[\"cluster\"];\n endpoint?: SolanaProviderProps[\"endpoint\"];\n renderLinked?: (wallet: { address: string; onUnlink: () => void }) => React.ReactNode;\n children?: React.ReactNode;\n};\n\nfunction LinkWalletInner({\n baseURL = \"\",\n onLink,\n onUnlink,\n onError,\n className,\n unlinkClassName,\n renderLinked,\n children,\n}: Omit<SolanaLinkWalletProps, \"cluster\" | \"endpoint\">) {\n const { publicKey, connected, disconnect, wallet, connecting, connect } =\n useWallet();\n const { setVisible, visible } = useWalletModal();\n const [linkedWallet, setLinkedWallet] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [linking, setLinking] = useState(false);\n const [hasTriggered, setHasTriggered] = useState(false);\n\n const fetchWallets = async () => {\n try {\n const res = await fetch(`${baseURL}/api/auth/siws/wallets`);\n const data = await res.json();\n if (data.wallets?.[0]) {\n setLinkedWallet(data.wallets[0].walletAddress);\n }\n } catch {}\n setLoading(false);\n };\n\n useEffect(() => {\n fetchWallets();\n }, []);\n\n useEffect(() => {\n if (connected && publicKey && linking && !hasTriggered) {\n setHasTriggered(true);\n setVisible(false);\n handleLink();\n }\n }, [connected, publicKey, linking, wallet, hasTriggered]);\n\n useEffect(() => {\n if (wallet && !connected && !connecting && linking) {\n connect().catch(console.error);\n }\n }, [wallet, connected, connecting, linking, connect]);\n\n useEffect(() => {\n if (!visible && linking && !connected && !connecting && !wallet) {\n setLinking(false);\n setHasTriggered(false);\n }\n }, [visible, linking, connected, connecting, wallet]);\n\n const handleLinkClick = async () => {\n setLinking(true);\n if (wallet && !connected) {\n try {\n await connect();\n } catch {\n setVisible(true);\n }\n } else if (!wallet) {\n setVisible(true);\n }\n };\n\n const handleLink = async () => {\n if (!wallet?.adapter) return;\n\n const adapter = wallet.adapter as any;\n if (!(\"signIn\" in adapter)) {\n onError?.(\"Wallet doesn't support SIWS\");\n setLinking(false);\n disconnect();\n return;\n }\n\n try {\n const createRes = await fetch(`${baseURL}/api/auth/siws/nonce`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n walletAddress: publicKey!.toBase58(),\n }),\n });\n const nonceData = await createRes.json();\n\n const input: SolanaSignInInput = {\n domain: nonceData.domain,\n statement: nonceData.statement,\n uri: nonceData.uri,\n nonce: nonceData.nonce,\n chainId: nonceData.chainId,\n issuedAt: nonceData.issuedAt,\n };\n\n const output: SolanaSignInOutput = await adapter.signIn(input);\n\n const serializedOutput = {\n account: {\n address: output.account.address,\n publicKey: Array.from(output.account.publicKey),\n },\n signature: Array.from(output.signature),\n signedMessage: Array.from(output.signedMessage),\n };\n\n const linkRes = await fetch(`${baseURL}/api/auth/siws/link`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ input, output: serializedOutput }),\n });\n\n if (linkRes.ok) {\n const data = await linkRes.json();\n setLinkedWallet(data.walletAddress);\n onLink?.(data.walletAddress);\n } else {\n const err = await linkRes.json();\n onError?.(err.message || \"Failed to link wallet\");\n }\n } catch (e: any) {\n if (!e?.message?.includes(\"rejected\")) {\n onError?.(\"Failed to link wallet\");\n }\n } finally {\n setLinking(false);\n setHasTriggered(false);\n disconnect();\n }\n };\n\n const handleUnlink = async () => {\n if (!linkedWallet) return;\n try {\n const res = await fetch(`${baseURL}/api/auth/siws/unlink`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ walletAddress: linkedWallet }),\n });\n if (res.ok) {\n setLinkedWallet(null);\n onUnlink?.();\n }\n } catch {\n onError?.(\"Failed to unlink wallet\");\n }\n };\n\n if (loading) {\n return (\n <button disabled className={className}>\n Loading...\n </button>\n );\n }\n\n if (linkedWallet) {\n if (renderLinked) {\n return <>{renderLinked({ address: linkedWallet, onUnlink: handleUnlink })}</>;\n }\n const truncated = `${linkedWallet.slice(0, 4)}...${linkedWallet.slice(-4)}`;\n return (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: \"0.5rem\" }}>\n <span style={{ fontFamily: \"monospace\", fontSize: \"0.875rem\" }}>\n {truncated}\n </span>\n <button onClick={handleUnlink} className={unlinkClassName} type=\"button\">\n Unlink\n </button>\n </div>\n );\n }\n\n return (\n <button\n onClick={handleLinkClick}\n disabled={linking}\n className={className}\n type=\"button\"\n >\n {children ?? (linking ? \"Linking...\" : \"Link Solana Wallet\")}\n </button>\n );\n}\n\nexport const SolanaLinkWallet: FC<SolanaLinkWalletProps> = ({\n cluster,\n endpoint,\n ...props\n}) => {\n return (\n <SolanaProvider cluster={cluster} endpoint={endpoint}>\n <LinkWalletInner {...props} />\n </SolanaProvider>\n );\n};\n"],"mappings":"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","names":["jsx","useEffect","useState","useWallet","useWalletModal","jsx","useWallet","useWalletModal","useState","useEffect"]}
|
|
1
|
+
{"version":3,"sources":["../../src/react/provider.tsx","../../src/react/sign-in.tsx","../../src/types.ts","../../src/react/link-wallet.tsx"],"sourcesContent":["\"use client\";\n\nimport { type FC, type ReactNode, useMemo } from \"react\";\nimport {\n ConnectionProvider,\n WalletProvider,\n} from \"@solana/wallet-adapter-react\";\nimport { WalletModalProvider } from \"@solana/wallet-adapter-react-ui\";\nimport { clusterApiUrl } from \"@solana/web3.js\";\n\nimport \"@solana/wallet-adapter-react-ui/styles.css\";\n\nexport type SolanaProviderProps = {\n children: ReactNode;\n cluster?: \"mainnet-beta\" | \"devnet\" | \"testnet\";\n endpoint?: string;\n autoConnect?: boolean;\n};\n\nexport const SolanaProvider: FC<SolanaProviderProps> = ({\n children,\n cluster = \"mainnet-beta\",\n endpoint,\n autoConnect = false,\n}) => {\n const rpcEndpoint = useMemo(\n () => endpoint ?? clusterApiUrl(cluster),\n [endpoint, cluster]\n );\n const wallets = useMemo(() => [], []);\n\n return (\n <ConnectionProvider endpoint={rpcEndpoint}>\n <WalletProvider wallets={wallets} autoConnect={autoConnect}>\n <WalletModalProvider>{children}</WalletModalProvider>\n </WalletProvider>\n </ConnectionProvider>\n );\n};\n","\"use client\";\n\nimport { useEffect, useState, type FC } from \"react\";\nimport { useWallet } from \"@solana/wallet-adapter-react\";\nimport { useWalletModal } from \"@solana/wallet-adapter-react-ui\";\nimport type { SolanaSignInInput } from \"@solana/wallet-standard-features\";\nimport { serializeOutput } from \"../types\";\nimport { SolanaProvider, type SolanaProviderProps } from \"./provider\";\n\nexport type SolanaSignInButtonProps = {\n baseURL?: string;\n basePath?: string;\n onSuccess?: (user: { id: string; name: string; email: string }) => void;\n onError?: (error: string) => void;\n className?: string;\n disabled?: boolean;\n children?: React.ReactNode;\n cluster?: SolanaProviderProps[\"cluster\"];\n endpoint?: SolanaProviderProps[\"endpoint\"];\n};\n\nfunction SolanaSignInInner({\n baseURL = \"\",\n basePath = \"/api/auth\",\n onSuccess,\n onError,\n className,\n disabled,\n children,\n}: Omit<SolanaSignInButtonProps, \"cluster\" | \"endpoint\">) {\n const { publicKey, connected, disconnect, wallet, connecting, connect } =\n useWallet();\n const { setVisible, visible } = useWalletModal();\n const [loading, setLoading] = useState(false);\n const [signingIn, setSigningIn] = useState(false);\n const [hasTriggered, setHasTriggered] = useState(false);\n\n useEffect(() => {\n if (connected && publicKey && signingIn && !hasTriggered) {\n setHasTriggered(true);\n setVisible(false);\n handleSignIn();\n }\n }, [connected, publicKey, signingIn, wallet, hasTriggered]);\n\n useEffect(() => {\n if (wallet && !connected && !connecting && signingIn) {\n connect().catch(console.error);\n }\n }, [wallet, connected, connecting, signingIn, connect]);\n\n useEffect(() => {\n if (!visible && signingIn && !connected && !connecting && !wallet) {\n setSigningIn(false);\n setHasTriggered(false);\n }\n }, [visible, signingIn, connected, connecting, wallet]);\n\n const handleClick = async () => {\n setSigningIn(true);\n if (wallet && !connected) {\n try {\n await connect();\n } catch {\n setVisible(true);\n }\n } else if (!wallet) {\n setVisible(true);\n }\n };\n\n const handleSignIn = async () => {\n if (!publicKey || !wallet?.adapter) {\n setSigningIn(false);\n return;\n }\n\n const adapter = wallet.adapter as any;\n if (!(\"signIn\" in adapter) || typeof adapter.signIn !== \"function\") {\n onError?.(\n \"Your wallet does not support Sign In With Solana. Please use Phantom v23.11.0 or later.\"\n );\n setSigningIn(false);\n disconnect();\n return;\n }\n\n setLoading(true);\n\n try {\n const nonceRes = await fetch(`${baseURL}${basePath}/siws/nonce`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ walletAddress: publicKey.toBase58() }),\n });\n const nonceData = await nonceRes.json();\n\n const input: SolanaSignInInput = {\n domain: nonceData.domain,\n statement: nonceData.statement,\n uri: nonceData.uri,\n nonce: nonceData.nonce,\n chainId: nonceData.chainId,\n issuedAt: nonceData.issuedAt,\n };\n\n const output = await adapter.signIn(input);\n const serialized = serializeOutput(output);\n\n const verifyRes = await fetch(`${baseURL}${basePath}/siws/verify`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ input, output: serialized }),\n });\n\n const data = await verifyRes.json();\n\n if (!verifyRes.ok) {\n onError?.(data.message || data.error || \"Failed to sign in with Solana\");\n } else {\n onSuccess?.(data.user);\n }\n } catch (error: any) {\n if (error?.message !== \"User rejected the request.\") {\n onError?.(error?.message || \"Failed to sign in with Solana wallet\");\n }\n } finally {\n setLoading(false);\n setSigningIn(false);\n setHasTriggered(false);\n disconnect();\n }\n };\n\n return (\n <button\n onClick={handleClick}\n disabled={loading || disabled}\n className={className}\n type=\"button\"\n >\n {children ?? (loading ? \"Authenticating...\" : \"Sign In with Solana\")}\n </button>\n );\n}\n\nexport const SolanaSignInButton: FC<SolanaSignInButtonProps> = ({\n cluster,\n endpoint,\n ...props\n}) => {\n return (\n <SolanaProvider cluster={cluster} endpoint={endpoint}>\n <SolanaSignInInner {...props} />\n </SolanaProvider>\n );\n};\n","import type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\n\nexport type { SolanaSignInInput, SolanaSignInOutput };\n\nexport interface SiwsPluginOptions {\n statement?: string;\n chainId?: string;\n nonceExpiryMs?: number;\n anonymous?: boolean;\n}\n\nexport interface SiwsNonceResponse {\n nonce: string;\n domain: string;\n uri: string;\n statement: string;\n chainId: string;\n issuedAt: string;\n}\n\nexport interface SiwsVerifyRequest {\n output: {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n };\n input: {\n domain: string;\n address?: string;\n statement?: string;\n uri?: string;\n version?: string;\n chainId?: string;\n nonce?: string;\n issuedAt?: string;\n expirationTime?: string;\n notBefore?: string;\n requestId?: string;\n resources?: string[];\n };\n}\n\nexport interface SiwsVerifyResponse {\n success: boolean;\n user: {\n id: string;\n name: string;\n email: string;\n };\n}\n\nexport interface SiwsLinkRequest {\n input: SolanaSignInInput;\n output: {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n };\n}\n\nexport interface SiwsLinkedWallet {\n walletAddress: string;\n createdAt: Date;\n}\n\nexport interface SerializedSolanaOutput {\n account: {\n address: string;\n publicKey: number[];\n };\n signature: number[];\n signedMessage: number[];\n signatureType?: string;\n}\n\nexport function serializeOutput(output: SolanaSignInOutput): SerializedSolanaOutput {\n return {\n account: {\n address: output.account.address,\n publicKey: Array.from(output.account.publicKey),\n },\n signature: Array.from(output.signature),\n signedMessage: Array.from(output.signedMessage),\n signatureType: (output as any).signatureType,\n };\n}\n\nexport function deserializeOutput(data: SerializedSolanaOutput): SolanaSignInOutput {\n return {\n account: {\n address: data.account.address,\n publicKey: new Uint8Array(data.account.publicKey),\n chains: [],\n features: [],\n },\n signature: new Uint8Array(data.signature),\n signedMessage: new Uint8Array(data.signedMessage),\n };\n}\n","\"use client\";\n\nimport { useEffect, useState, type FC } from \"react\";\nimport { useWallet } from \"@solana/wallet-adapter-react\";\nimport { useWalletModal } from \"@solana/wallet-adapter-react-ui\";\nimport type { SolanaSignInInput, SolanaSignInOutput } from \"@solana/wallet-standard-features\";\nimport { SolanaProvider, type SolanaProviderProps } from \"./provider\";\n\nexport type SolanaLinkWalletProps = {\n baseURL?: string;\n basePath?: string;\n onLink?: (walletAddress: string) => void;\n onUnlink?: () => void;\n onError?: (error: string) => void;\n className?: string;\n unlinkClassName?: string;\n disabled?: boolean;\n cluster?: SolanaProviderProps[\"cluster\"];\n endpoint?: SolanaProviderProps[\"endpoint\"];\n renderLinked?: (wallet: { address: string; onUnlink: () => void }) => React.ReactNode;\n children?: React.ReactNode;\n};\n\nfunction LinkWalletInner({\n baseURL = \"\",\n basePath = \"/api/auth\",\n onLink,\n onUnlink,\n onError,\n className,\n unlinkClassName,\n disabled,\n renderLinked,\n children,\n}: Omit<SolanaLinkWalletProps, \"cluster\" | \"endpoint\">) {\n const { publicKey, connected, disconnect, wallet, connecting, connect } =\n useWallet();\n const { setVisible, visible } = useWalletModal();\n const [linkedWallet, setLinkedWallet] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [linking, setLinking] = useState(false);\n const [hasTriggered, setHasTriggered] = useState(false);\n\n const fetchWallets = async () => {\n try {\n const res = await fetch(`${baseURL}${basePath}/siws/wallets`);\n const data = await res.json();\n if (data.wallets?.[0]) {\n setLinkedWallet(data.wallets[0].walletAddress);\n }\n } catch {}\n setLoading(false);\n };\n\n useEffect(() => {\n fetchWallets();\n }, []);\n\n useEffect(() => {\n if (connected && publicKey && linking && !hasTriggered) {\n setHasTriggered(true);\n setVisible(false);\n handleLink();\n }\n }, [connected, publicKey, linking, wallet, hasTriggered]);\n\n useEffect(() => {\n if (wallet && !connected && !connecting && linking) {\n connect().catch(console.error);\n }\n }, [wallet, connected, connecting, linking, connect]);\n\n useEffect(() => {\n if (!visible && linking && !connected && !connecting && !wallet) {\n setLinking(false);\n setHasTriggered(false);\n }\n }, [visible, linking, connected, connecting, wallet]);\n\n const handleLinkClick = async () => {\n setLinking(true);\n if (wallet && !connected) {\n try {\n await connect();\n } catch {\n setVisible(true);\n }\n } else if (!wallet) {\n setVisible(true);\n }\n };\n\n const handleLink = async () => {\n if (!wallet?.adapter) return;\n\n const adapter = wallet.adapter as any;\n if (!(\"signIn\" in adapter)) {\n onError?.(\"Wallet doesn't support SIWS\");\n setLinking(false);\n disconnect();\n return;\n }\n\n try {\n const createRes = await fetch(`${baseURL}${basePath}/siws/nonce`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n walletAddress: publicKey!.toBase58(),\n }),\n });\n const nonceData = await createRes.json();\n\n const input: SolanaSignInInput = {\n domain: nonceData.domain,\n statement: nonceData.statement,\n uri: nonceData.uri,\n nonce: nonceData.nonce,\n chainId: nonceData.chainId,\n issuedAt: nonceData.issuedAt,\n };\n\n const output: SolanaSignInOutput = await adapter.signIn(input);\n\n const serializedOutput = {\n account: {\n address: output.account.address,\n publicKey: Array.from(output.account.publicKey),\n },\n signature: Array.from(output.signature),\n signedMessage: Array.from(output.signedMessage),\n };\n\n const linkRes = await fetch(`${baseURL}${basePath}/siws/link`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ input, output: serializedOutput }),\n });\n\n if (linkRes.ok) {\n const data = await linkRes.json();\n setLinkedWallet(data.walletAddress);\n onLink?.(data.walletAddress);\n } else {\n const err = await linkRes.json();\n onError?.(err.message || \"Failed to link wallet\");\n }\n } catch (e: any) {\n if (!e?.message?.includes(\"rejected\")) {\n onError?.(\"Failed to link wallet\");\n }\n } finally {\n setLinking(false);\n setHasTriggered(false);\n disconnect();\n }\n };\n\n const handleUnlink = async () => {\n if (!linkedWallet) return;\n try {\n const res = await fetch(`${baseURL}${basePath}/siws/unlink`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ walletAddress: linkedWallet }),\n });\n if (res.ok) {\n setLinkedWallet(null);\n onUnlink?.();\n }\n } catch {\n onError?.(\"Failed to unlink wallet\");\n }\n };\n\n if (loading) {\n return (\n <button disabled className={className}>\n Loading...\n </button>\n );\n }\n\n if (linkedWallet) {\n if (renderLinked) {\n return <>{renderLinked({ address: linkedWallet, onUnlink: handleUnlink })}</>;\n }\n const truncated = `${linkedWallet.slice(0, 4)}...${linkedWallet.slice(-4)}`;\n return (\n <div style={{ display: \"flex\", alignItems: \"center\", gap: \"0.5rem\" }}>\n <span style={{ fontFamily: \"monospace\", fontSize: \"0.875rem\" }}>\n {truncated}\n </span>\n <button onClick={handleUnlink} disabled={disabled} className={unlinkClassName} type=\"button\">\n Unlink\n </button>\n </div>\n );\n }\n\n return (\n <button\n onClick={handleLinkClick}\n disabled={linking || disabled}\n className={className}\n type=\"button\"\n >\n {children ?? (linking ? \"Linking...\" : \"Link Solana Wallet\")}\n </button>\n );\n}\n\nexport const SolanaLinkWallet: FC<SolanaLinkWalletProps> = ({\n cluster,\n endpoint,\n ...props\n}) => {\n return (\n <SolanaProvider cluster={cluster} endpoint={endpoint}>\n <LinkWalletInner {...props} />\n </SolanaProvider>\n );\n};\n"],"mappings":"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","names":["jsx","useEffect","useState","useWallet","useWalletModal","jsx","useWallet","useWalletModal","useState","useEffect"]}
|
package/package.json
CHANGED