gpt4 book ai didi

Next.js 13 + NextAuth + Sign-In with Ethereum: CSRF Token Mismatch between Client and Server(Next.js 13+NextAuth+使用Etherum登录:客户端和服务器之间的CSRF令牌不匹配)

转载 作者:bug小助手 更新时间:2023-10-25 22:28:39 25 4
gpt4 key购买 nike



I'm currently working on implementing Ethereum sign-in functionality in my Next.js 13 application using NextAuth and Siwe. However, I'm encountering an issue where the CSRF token doesn't match between the client and server sides.
Here's the relevant code snippets:

我目前正在使用NextAuth和Swe在我的Next.js13应用程序中实现以太登录功能。但是,我遇到了客户端和服务器端的CSRF令牌不匹配的问题。以下是相关的代码片段:


In app/api/auth/[...nextauth]/route.ts

在app/api/auth/[...nextauth]/route.ts中


import { NextApiRequest, NextApiResponse } from "next";
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import { getCsrfToken } from 'next-auth/react';
import { NextRequest, NextResponse } from "next/server";
import { SiweMessage } from 'siwe';


// @ts-ignore
export const authOptions = ({ req }) => ({
secret: process.env.NEXTAUTH_SECRET,
providers: [
CredentialsProvider({
name: 'Ethereum',
type: 'credentials', // default for Credentials
// Default values if it was a form
credentials: {
message: {
label: 'Message',
type: 'text',
placeholder: '0x0',
},
signature: {
label: 'Signature',
type: 'text',
placeholder: '0x0',
},
},
authorize: async (credentials) => {
try {

const siwe = new SiweMessage(
JSON.parse(credentials?.message ?? '{}')
);
const nonce = await getCsrfToken({ req:{headers:req?.headers} });

console.log("NONCE IS: ",nonce);

const verifyParams = {
signature: credentials?.signature || '',
nonce: nonce,
};

const verifyOpts = {};

// const fields = await siwe.validate(credentials?.signature || "");
const verificationResult = await siwe.verify(
verifyParams,
verifyOpts
);

// if (fields.nonce !== nonce) {
if (!verificationResult.success) {
return null;
}

// Create User Account
console.log(verificationResult);

// Create User Account End

//
return {
id: verificationResult.data.address,
};
} catch (error) {
// Uncomment or add logging if needed
console.error({ error });
return null;
}
},
}),
/**
* ...add more providers here.
*
* Most other providers require a bit more work than the Discord provider. For example, the
* GitHub provider requires you to add the `refresh_token_expires_in` field to the Account
* model. Refer to the NextAuth.js docs for the provider you want to use. Example:
*
* @see https://next-auth.js.org/providers/github
*/
],
callbacks: {
// token.sub will refer to the id of the wallet address
// @ts-ignore
session: ({ session, token }) => ({
...session,
user: {
...session.user,
id: token.sub,
},
}),
},
});

// @ts-ignore
const Auth = (req, res: NextApiResponse) => {
const authOpts = authOptions({ req });

// const isDefaultSigninPage =
// req.method === 'GET';

// // Hide Sign-In with Ethereum from default sign page
// if (isDefaultSigninPage) {
// // Removes from the authOptions.providers array
// authOpts.providers.pop();
// }

return NextAuth(req, res, authOpts);
};

export { Auth as GET, Auth as POST };

In Components/Navbar.tsx

在组件/Navbar.tsx中


"use client";
import Link from "next/link";
import { Button } from "./ui/button";
import { useWeb3Modal } from "@web3modal/react";
import { useAccount, useDisconnect, useNetwork, useSignMessage } from 'wagmi';
import { useEffect, useState } from "react";
import { getCsrfToken, signIn, signOut, useSession } from 'next-auth/react';
import { SiweMessage } from 'siwe';

type Props = {

}

export default function Navbar(props: Props) {

const { data: sessionData } = useSession();
const { open } = useWeb3Modal();
const { signMessageAsync } = useSignMessage();
const { address, isConnected } = useAccount();
const { chain } = useNetwork();
const [isMounted, setIsMounted] = useState(false);

const onClickSignIn = async () => {
try {
const message = new SiweMessage({
domain: window.location.host,
address: address,
statement: 'Sign in to App.',
uri: window.location.origin,
version: '1',
chainId: chain?.id,
// nonce is used from CSRF token
nonce: await getCsrfToken(),
});
const signature = await signMessageAsync({
message: message.prepareMessage(),
});
signIn('credentials', {
message: JSON.stringify(message),
redirect: false,
signature,
});
// document.cookie = "_csrf=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
} catch (error) {
window.alert(error);
}
};

const onClickSignOut = async () => {
await signOut();
};

useEffect(() => setIsMounted(true), [])
if (!isMounted) return <></>;

return (
<nav className="fixed inset-x-0 top-0 bg-white dark:bg-gray-950 z-[10] h-fit border-b border-zinc-300 py-2">
<div className="flex items-center justify-center h-full gap-2 px-8 mx-auto sm:justify-between max-w-7xl">
<Link href="/" className="items-center hidden gap-2 sm:flex" >
<p className="rounded-lg border-2 border-b-4 border-r-4 border-black px-2 py-1 text-xl transition-all hover:-translate-y-[2px] md:block dark:border-white font-semibold">
NextAuth Error
</p>
</Link>
<div className="flex items-center">
{/* Connect Wallet button goes here. */}
<Button variant={"default"} onClick={open}>
{isConnected ? 'Connected' : 'Connect Wallet'}
</Button>
{isConnected && (
<Button className="ml-2" variant={"outline"} onClick={sessionData ? onClickSignOut:onClickSignIn}>
{sessionData?'Sign Out':"Sign in"}
</Button>
)}
</div>
</div>
</nav>
)
}

In terminal

在终端中


NONCE IS:  ac0e019c927cf06ded90aad76e50a66f444923ca2b6fd4a356deca28724fa509
{
error: {
success: false,
data: SiweMessage {
domain: 'localhost:3000',
address: '0x4BF20785a0B2E6a375B1d49Ba64c6145AC50AAD6',
statement: 'Sign in to App.',
uri: 'http://localhost:3000',
version: '1',
chainId: 80001,
nonce: '6c0876fe35f884fa2dd1f064d45a86e7ff1af082ea50eac5570e1b0661043cd1',
issuedAt: '2023-09-10T05:51:10.990Z'
},
error: SiweError {
type: 'Nonce does not match provided nonce for verification.',
expected: 'ac0e019c927cf06ded90aad76e50a66f444923ca2b6fd4a356deca28724fa509',
received: '6c0876fe35f884fa2dd1f064d45a86e7ff1af082ea50eac5570e1b0661043cd1'
}
}
}

Versions:

版本:


"siwe": "^2.1.4",
"next": "13.4.19",
"next-auth": "^4.23.1",

On the client side, I consistently receive the same nonce every time I call getCsrfToken(). However, on the server side, it generates a different CSRF token.

在客户端,每次调用getCsrfToken()时都会收到相同的随机数。但是,在服务器端,它会生成一个不同的CSRF令牌。


Thank you in advance!

提前谢谢您!


更多回答
优秀答案推荐

Not the ideal solution, but after reading and looking at a ton of different reports and articles.

这不是理想的解决方案,但在阅读和阅读了大量不同的报告和文章后。


One being: Reddit

标签:Reddit


I came up with a similar solution:

我想出了一个类似的解决方案:


Mainly just pulling the csrf from the cookie rather:

主要是从cookie中提取CSRF,而不是:


const csrf = cookies().get('next-auth.csrf-token')?.value.split('|')[0]

authorize: async (credentials) => {
try {
const csrf = cookies().get('next-auth.csrf-token')?.value.split('|')[0]
const siwe = new SiweMessage(JSON.parse(credentials?.message || '{}'))
const nextAuthUrl = new URL(process.env.NEXTAUTH_URL!)
if (siwe.domain !== nextAuthUrl.host) {
return null
}
// siwe will validate that the message is signed by the address
const verifyParams = {
signature: credentials?.signature || '',
nonce: csrf,
};

const verifyOpts = {};

// const fields = await siwe.validate(credentials?.signature || "");
const verificationResult = await siwe.verify(
verifyParams,
verifyOpts
);

// if (fields.nonce !== nonce) {
if (!verificationResult.success) {
return null;
}

return {
id: siwe.address,
}
} catch (error) {
// Uncomment or add logging if needed
console.error({ error });
return null;
}
},
}),

更多回答

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com