How to?: Sign In With Ethereum and have a user record created?

I have an application running and just got Sign In With Ethereum 'working' with nextauth/drizzle orm adapter/planetscale, but nothing is stored in the database user records. When i log in with different oauth providers (discord) i get new records added. Should this be creating new records in my database by default, or is the nextjs discord provider coded to do that, and I need to write that programming in the authorize callback to handle it? Here is my nextauth auth.ts file:
2 Replies
Trader Launchpad
...
declare module "next-auth" {
interface Session extends DefaultSession {
user: {
id: string;
// ...other properties
// role: UserRole;
} & DefaultSession["user"];
}
}

export const authOptions: NextAuthOptions = {
callbacks: {
session: ({ session, token }) => {
return {
...session,
user: {
...session.user,
id: token.sub,
},
};
},
jwt: ({ token, user }) => {
return {
...token,
user,
};
},
},
adapter: DrizzleAdapter(db, mysqlTable),
providers: [
DiscordProvider({
clientId: env.DISCORD_CLIENT_ID,
clientSecret: env.DISCORD_CLIENT_SECRET,
}),
CredentialsProvider({
name: "Ethereum",
credentials: {
message: {
label: "Message",
type: "text",
placeholder: "0x0",
},
signature: {
label: "Signature",
type: "text",
placeholder: "0x0",
},
},
async authorize(credentials, req) {
try {
console.log("siwe step 1");
const siwe = new SiweMessage(
JSON.parse(credentials?.message ?? "{}"),
);
const nextAuthUrl = new URL(process.env.NEXTAUTH_URL!);

const result = await siwe.verify({
signature: credentials?.signature ?? "",
domain: nextAuthUrl.host,
nonce: await getCsrfToken({ req }),
});

console.log("result", result);

if (result.success) {
return {
id: siwe.address,
};
}
return null;
} catch (e) {
return null;
}
},
}),
],
session: { strategy: "jwt" },
};
export const getServerAuthSession = () => getServerSession(authOptions);
...
declare module "next-auth" {
interface Session extends DefaultSession {
user: {
id: string;
// ...other properties
// role: UserRole;
} & DefaultSession["user"];
}
}

export const authOptions: NextAuthOptions = {
callbacks: {
session: ({ session, token }) => {
return {
...session,
user: {
...session.user,
id: token.sub,
},
};
},
jwt: ({ token, user }) => {
return {
...token,
user,
};
},
},
adapter: DrizzleAdapter(db, mysqlTable),
providers: [
DiscordProvider({
clientId: env.DISCORD_CLIENT_ID,
clientSecret: env.DISCORD_CLIENT_SECRET,
}),
CredentialsProvider({
name: "Ethereum",
credentials: {
message: {
label: "Message",
type: "text",
placeholder: "0x0",
},
signature: {
label: "Signature",
type: "text",
placeholder: "0x0",
},
},
async authorize(credentials, req) {
try {
console.log("siwe step 1");
const siwe = new SiweMessage(
JSON.parse(credentials?.message ?? "{}"),
);
const nextAuthUrl = new URL(process.env.NEXTAUTH_URL!);

const result = await siwe.verify({
signature: credentials?.signature ?? "",
domain: nextAuthUrl.host,
nonce: await getCsrfToken({ req }),
});

console.log("result", result);

if (result.success) {
return {
id: siwe.address,
};
}
return null;
} catch (e) {
return null;
}
},
}),
],
session: { strategy: "jwt" },
};
export const getServerAuthSession = () => getServerSession(authOptions);
If i do need to code in my own logic, would it look something like this, and should i go through a trpc route?:
async authorize(credentials, req) {
try {
const siwe = new SiweMessage(
JSON.parse(credentials?.message ?? "{}"),
);
const nextAuthUrl = new URL(process.env.NEXTAUTH_URL!);

const result = await siwe.verify({
signature: credentials?.signature ?? "",
domain: nextAuthUrl.host,
nonce: await getCsrfToken({ req }),
});

console.log("result", result);

if (result.success) {
// Check if the user with this Ethereum address already exists in the database
let user = await db.findUserByWalletId(siwe.address);

if (!user) {
// If the user doesn't exist, create a new user in the database
user = await db.createUser({ walletId: siwe.address });
}

return {
id: siwe.address,
...user, // Include additional user properties from the database
};
}
return null;
} catch (e) {
return null;
}
},
async authorize(credentials, req) {
try {
const siwe = new SiweMessage(
JSON.parse(credentials?.message ?? "{}"),
);
const nextAuthUrl = new URL(process.env.NEXTAUTH_URL!);

const result = await siwe.verify({
signature: credentials?.signature ?? "",
domain: nextAuthUrl.host,
nonce: await getCsrfToken({ req }),
});

console.log("result", result);

if (result.success) {
// Check if the user with this Ethereum address already exists in the database
let user = await db.findUserByWalletId(siwe.address);

if (!user) {
// If the user doesn't exist, create a new user in the database
user = await db.createUser({ walletId: siwe.address });
}

return {
id: siwe.address,
...user, // Include additional user properties from the database
};
}
return null;
} catch (e) {
return null;
}
},
madova
madova5mo ago
I'm looking at doing something similar with the Farcaster sign in, and a little stumped in this spot as well - did you figure out a direction on this?