SIWE Auth plugin

hey! I'm trying to get a siwe-plugin working, but i'm pretty sure I'm doing something wrong here. Once my plugin is initialized, I still can't access it via signIn.siwe
2 Replies
Second State
Second StateOP2w ago
index.ts, too long to paste: https://privatebin.net/?9eb49b1afc9c5e5b#BmYfj1t5AJZVMyv62eiowufQcse6WVsK5cs7EUaTaiXA client.ts:
import type { BetterAuthClientPlugin } from "better-auth";
import { createSiweMessage } from "viem/siwe";
import type { Address, Hex } from "viem";
import type { siwePlugin } from "./index";

type SiwePlugin = typeof siwePlugin;

export type SiweClientPluginOptions = {
statement?: string;
domain?: string;
origin?: string;
chainId?: number;
};

// Extend the client plugin type to include our SIWE methods
declare module "better-auth" {
interface BetterAuthClientPlugin {
signIn?: {
siwe: (params: {
address: Address;
chainId: number;
signMessage: (message: string) => Promise<Hex>;
}) => Promise<{
user: {
id: string;
ethAddress: string;
name: string;
avatar: string;
email: string;
createdAt: Date;
updatedAt: Date;
};
session: {
token: string;
expiresAt: Date;
};
}>;
};
}
}

export const siweClientPlugin = (options?: SiweClientPluginOptions) => {
return {
id: "siwe",
$InferServerPlugin: {} as ReturnType<SiwePlugin>,

signIn: {
siwe: async ({
address,
chainId,
signMessage,
}: {
address: Address;
chainId: number;
signMessage: (message: string) => Promise<Hex>;
}) => {
// Get nonce from server
const nonceRes = await fetch("/api/auth/siwe/nonce", {
method: "GET",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
publicKey: address,
}),
});

if (!nonceRes.ok) {
throw new Error("Failed to get nonce");
}
const { nonce } = await nonceRes.json();

// Create SIWE message
const messageObj = createSiweMessage({
domain: options?.domain || window.location.host,
address,
statement:
options?.statement || "Sign in with Ethereum to authenticate.",
uri: options?.origin || window.location.origin,
version: "1",
chainId: options?.chainId || chainId,
nonce,
});

// Get signature from wallet
const signature = await signMessage(messageObj);

// Verify signature with server
const verifyRes = await fetch("/api/auth/siwe/verify", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
message: messageObj,
signature,
publicKey: address,
}),
});

if (!verifyRes.ok) {
const error = await verifyRes.json();
throw new Error(error.message || "Failed to verify signature");
}

return verifyRes.json();
},
},
} satisfies BetterAuthClientPlugin;
};
import type { BetterAuthClientPlugin } from "better-auth";
import { createSiweMessage } from "viem/siwe";
import type { Address, Hex } from "viem";
import type { siwePlugin } from "./index";

type SiwePlugin = typeof siwePlugin;

export type SiweClientPluginOptions = {
statement?: string;
domain?: string;
origin?: string;
chainId?: number;
};

// Extend the client plugin type to include our SIWE methods
declare module "better-auth" {
interface BetterAuthClientPlugin {
signIn?: {
siwe: (params: {
address: Address;
chainId: number;
signMessage: (message: string) => Promise<Hex>;
}) => Promise<{
user: {
id: string;
ethAddress: string;
name: string;
avatar: string;
email: string;
createdAt: Date;
updatedAt: Date;
};
session: {
token: string;
expiresAt: Date;
};
}>;
};
}
}

export const siweClientPlugin = (options?: SiweClientPluginOptions) => {
return {
id: "siwe",
$InferServerPlugin: {} as ReturnType<SiwePlugin>,

signIn: {
siwe: async ({
address,
chainId,
signMessage,
}: {
address: Address;
chainId: number;
signMessage: (message: string) => Promise<Hex>;
}) => {
// Get nonce from server
const nonceRes = await fetch("/api/auth/siwe/nonce", {
method: "GET",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
publicKey: address,
}),
});

if (!nonceRes.ok) {
throw new Error("Failed to get nonce");
}
const { nonce } = await nonceRes.json();

// Create SIWE message
const messageObj = createSiweMessage({
domain: options?.domain || window.location.host,
address,
statement:
options?.statement || "Sign in with Ethereum to authenticate.",
uri: options?.origin || window.location.origin,
version: "1",
chainId: options?.chainId || chainId,
nonce,
});

// Get signature from wallet
const signature = await signMessage(messageObj);

// Verify signature with server
const verifyRes = await fetch("/api/auth/siwe/verify", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
message: messageObj,
signature,
publicKey: address,
}),
});

if (!verifyRes.ok) {
const error = await verifyRes.json();
throw new Error(error.message || "Failed to verify signature");
}

return verifyRes.json();
},
},
} satisfies BetterAuthClientPlugin;
};
made 2 actions which I can now call from my client comp.
export async function getSiweNonce(publicKey: string) {
return await auth.api.getNonce({
body: {
publicKey,
},
});
}

export async function verifySiweSignature(
message: string,
signature: string,
publicKey: string
) {
return await auth.api.verify({
body: {
message,
signature,
publicKey,
},
});
}
export async function getSiweNonce(publicKey: string) {
return await auth.api.getNonce({
body: {
publicKey,
},
});
}

export async function verifySiweSignature(
message: string,
signature: string,
publicKey: string
) {
return await auth.api.verify({
body: {
message,
signature,
publicKey,
},
});
}
I now get an user and session created after signing the nonce. so is there anything missing from here, or it's good enough? Thanks!
Ping
Ping2w ago
Hey, I'm not familar with SIWE, but maybe @bekacru could let you know if you're potentially missing anything.

Did you find this page helpful?