@pinklemon8/better-auth-siws 0.1.2 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Lymlight
3
+ Copyright (c) 2026 Lymlight
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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 (error) {
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-Q7TSFSYW.js.map
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 (error) {\n console.error(\"SIWS verification error:\", error);\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,SAAS,OAAO;AACd,YAAQ,MAAM,4BAA4B,KAAK;AAC/C,WAAO;AAAA,EACT;AACF;","names":[]}
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 (error) {
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.deleteVerificationValue(
169
- verification.id
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", {
@@ -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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAA6C;AAC7C,qBAAiC;AACjC,iBAAkB;;;ACDlB,kCAA6B;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;AAmBO,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,eAAO,0CAAa,OAAO,aAAa;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AC2BO,SAAS,gBAAgB,QAAoD;AAClF,SAAO;AAAA,IACL,SAAS;AAAA,MACP,SAAS,OAAO,QAAQ;AAAA,MACxB,WAAW,MAAM,KAAK,OAAO,QAAQ,SAAS;AAAA,IAChD;AAAA,IACA,WAAW,MAAM,KAAK,OAAO,SAAS;AAAA,IACtC,eAAe,MAAM,KAAK,OAAO,aAAa;AAAA,IAC9C,eAAgB,OAAe;AAAA,EACjC;AACF;AAEO,SAAS,kBAAkB,MAAkD;AAClF,SAAO;AAAA,IACL,SAAS;AAAA,MACP,SAAS,KAAK,QAAQ;AAAA,MACtB,WAAW,IAAI,WAAW,KAAK,QAAQ,SAAS;AAAA,MAChD,QAAQ,CAAC;AAAA,MACT,UAAU,CAAC;AAAA,IACb;AAAA,IACA,WAAW,IAAI,WAAW,KAAK,SAAS;AAAA,IACxC,eAAe,IAAI,WAAW,KAAK,aAAa;AAAA,EAClD;AACF;;;AFtFO,IAAM,OAAO,CAAC,aAAiC;AAAA,EACpD,IAAI;AAAA,EACJ,WAAW;AAAA,IACT,kBAAc;AAAA,MACZ;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,MAAM,aAAE,OAAO;AAAA,UACb,eAAe,aAAE,OAAO,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE;AAAA,QAC1C,CAAC;AAAA,MACH;AAAA,MACA,OAAO,QAAQ;AACb,cAAM,EAAE,cAAc,IAAI,IAAI;AAC9B,cAAM,QAAQ,cAAc;AAC5B,cAAM,WAAW,SAAS,iBAAiB,IAAI,KAAK;AAEpD,cAAM,IAAI,QAAQ,gBAAgB,wBAAwB;AAAA,UACxD,YAAY,QAAQ,aAAa;AAAA,UACjC,OAAO;AAAA,UACP,WAAW,IAAI,KAAK,KAAK,IAAI,IAAI,QAAQ;AAAA,QAC3C,CAAC;AAED,cAAM,OAAO,IAAI,SAAS,IAAI,MAAM,KAAK;AACzC,cAAM,WAAW,KAAK,SAAS,WAAW,IAAI,SAAS;AACvD,cAAM,SAAS;AACf,cAAM,MAAM,GAAG,QAAQ,MAAM,IAAI;AAEjC,eAAO,IAAI,KAAK;AAAA,UACd;AAAA,UACA;AAAA,UACA;AAAA,UACA,WACE,SAAS,aACT;AAAA,UACF,SAAS,SAAS,WAAW;AAAA,UAC7B,WAAU,oBAAI,KAAK,GAAE,YAAY;AAAA,QACnC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IAEA,yBAAqB;AAAA,MACnB;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,MAAM,aAAE,OAAO;AAAA,UACb,QAAQ,aAAE,OAAO;AAAA,YACf,SAAS,aAAE,OAAO;AAAA,cAChB,SAAS,aAAE,OAAO;AAAA,cAClB,WAAW,aAAE,MAAM,aAAE,OAAO,CAAC;AAAA,YAC/B,CAAC;AAAA,YACD,WAAW,aAAE,MAAM,aAAE,OAAO,CAAC;AAAA,YAC7B,eAAe,aAAE,MAAM,aAAE,OAAO,CAAC;AAAA,UACnC,CAAC;AAAA,UACD,OAAO,aAAE,OAAO;AAAA,YACd,QAAQ,aAAE,OAAO;AAAA,YACjB,SAAS,aAAE,OAAO,EAAE,SAAS;AAAA,YAC7B,WAAW,aAAE,OAAO,EAAE,SAAS;AAAA,YAC/B,KAAK,aAAE,OAAO,EAAE,SAAS;AAAA,YACzB,SAAS,aAAE,OAAO,EAAE,SAAS;AAAA,YAC7B,SAAS,aAAE,OAAO,EAAE,SAAS;AAAA,YAC7B,OAAO,aAAE,OAAO,EAAE,SAAS;AAAA,YAC3B,UAAU,aAAE,OAAO,EAAE,SAAS;AAAA,YAC9B,gBAAgB,aAAE,OAAO,EAAE,SAAS;AAAA,YACpC,WAAW,aAAE,OAAO,EAAE,SAAS;AAAA,YAC/B,WAAW,aAAE,OAAO,EAAE,SAAS;AAAA,YAC/B,WAAW,aAAE,MAAM,aAAE,OAAO,CAAC,EAAE,SAAS;AAAA,UAC1C,CAAC;AAAA,QACH,CAAC;AAAA,QACD,gBAAgB;AAAA,MAClB;AAAA,MACA,OAAO,QAAa;AAClB,cAAM,EAAE,QAAQ,MAAM,IAAI,IAAI;AAC9B,cAAM,gBAAgB,OAAO,QAAQ;AAErC,cAAM,eACJ,MAAM,IAAI,QAAQ,gBAAgB;AAAA,UAChC,QAAQ,aAAa;AAAA,QACvB;AAEF,YAAI,CAAC,gBAAgB,oBAAI,KAAK,IAAI,aAAa,WAAW;AACxD,gBAAM,IAAI,oBAAS,gBAAgB;AAAA,YACjC,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,cAAM,eAAe,kBAAkB,MAAM;AAC7C,cAAM,UAAU,WAAW,OAAO,YAAY;AAE9C,YAAI,CAAC,SAAS;AACZ,gBAAM,IAAI,oBAAS,gBAAgB;AAAA,YACjC,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,cAAM,IAAI,QAAQ,gBAAgB;AAAA,UAChC,QAAQ,aAAa;AAAA,QACvB;AAEA,cAAM,gBAAiB,MAAM,IAAI,QAAQ,QAAQ,QAAQ;AAAA,UACvD,OAAO;AAAA,UACP,OAAO;AAAA,YACL,EAAE,OAAO,cAAc,UAAU,MAAM,OAAO,SAAS;AAAA,YACvD,EAAE,OAAO,aAAa,UAAU,MAAM,OAAO,cAAc;AAAA,UAC7D;AAAA,QACF,CAAC;AAED,YAAI,CAAC,eAAe;AAClB,cAAI,SAAS,WAAW;AACtB,kBAAMA,QAAO,MAAM,IAAI,QAAQ,gBAAgB,WAAW;AAAA,cACxD,MAAM;AAAA,cACN,OAAO,GAAG,aAAa;AAAA,cACvB,OAAO;AAAA,YACT,CAAC;AAED,kBAAM,IAAI,QAAQ,gBAAgB,cAAc;AAAA,cAC9C,QAAQA,MAAK;AAAA,cACb,YAAY;AAAA,cACZ,WAAW;AAAA,cACX,WAAW,oBAAI,KAAK;AAAA,cACpB,WAAW,oBAAI,KAAK;AAAA,YACtB,CAAC;AAED,kBAAMC,WAAU,MAAM,IAAI,QAAQ,gBAAgB;AAAA,cAChDD,MAAK;AAAA,YACP;AAEA,gBAAI,CAACC,UAAS;AACZ,oBAAM,IAAI,oBAAS,yBAAyB;AAAA,gBAC1C,SAAS;AAAA,cACX,CAAC;AAAA,YACH;AAEA,sBAAM,iCAAiB,KAAK,EAAE,SAAAA,UAAS,MAAAD,MAAK,CAAC;AAE7C,mBAAO,IAAI,KAAK;AAAA,cACd,SAAS;AAAA,cACT,MAAM;AAAA,gBACJ,IAAIA,MAAK;AAAA,gBACT,MAAMA,MAAK;AAAA,gBACX,OAAOA,MAAK;AAAA,cACd;AAAA,YACF,CAAC;AAAA,UACH;AAEA,gBAAM,IAAI,oBAAS,gBAAgB;AAAA,YACjC,SACE;AAAA,YACF,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AAEA,cAAM,OAAQ,MAAM,IAAI,QAAQ,QAAQ,QAAQ;AAAA,UAC9C,OAAO;AAAA,UACP,OAAO;AAAA,YACL,EAAE,OAAO,MAAM,UAAU,MAAM,OAAO,cAAc,OAAO;AAAA,UAC7D;AAAA,QACF,CAAC;AAWD,YAAI,CAAC,MAAM;AACT,gBAAM,IAAI,oBAAS,gBAAgB;AAAA,YACjC,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,YAAI,KAAK,QAAQ;AACf,gBAAM,IAAI,oBAAS,aAAa;AAAA,YAC9B,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,cAAM,UAAU,MAAM,IAAI,QAAQ,gBAAgB;AAAA,UAChD,KAAK;AAAA,QACP;AAEA,YAAI,CAAC,SAAS;AACZ,gBAAM,IAAI,oBAAS,yBAAyB;AAAA,YAC1C,SAAS;AAAA,UACX,CAAC;AAAA,QACH;AAEA,kBAAM,iCAAiB,KAAK,EAAE,SAAS,KAAK,CAAC;AAE7C,eAAO,IAAI,KAAK;AAAA,UACd,SAAS;AAAA,UACT,MAAM;AAAA,YACJ,IAAI,KAAK;AAAA,YACT,MAAM,KAAK;AAAA,YACX,OAAO,KAAK;AAAA,UACd;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;","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-Q7TSFSYW.js";
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.deleteVerificationValue(
97
- verification.id
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 (error) {
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-Q7TSFSYW.js";
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":"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","names":[]}
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":[]}
@@ -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}/api/auth/siws/nonce`, {
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}/api/auth/siws/verify`, {
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}/api/auth/siws/wallets`);
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}/api/auth/siws/nonce`, {
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}/api/auth/siws/link`, {
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}/api/auth/siws/unlink`, {
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")
@@ -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"]}
@@ -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}/api/auth/siws/nonce`, {
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}/api/auth/siws/verify`, {
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}/api/auth/siws/wallets`);
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}/api/auth/siws/nonce`, {
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}/api/auth/siws/link`, {
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}/api/auth/siws/unlink`, {
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")
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pinklemon8/better-auth-siws",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Sign In With Solana (SIWS) plugin for Better Auth — wallet authentication, linking, and ready-made React components",
5
5
  "license": "MIT",
6
6
  "type": "module",