Next-auth中可以通过Credentials进行任意凭证登陆,如用户名和密码,那如果通过以太坊钱包进行登陆呢?
基于siwe这个库即可实现
siwe全称:sign in with Ethereum
具体实现
版本:
{
"next-auth": "5.0.0-beta.15",
"siwe": "^2.3.2",
}
- next-auth配置
import NextAuth from 'next-auth';
import Credentials from 'next-auth/providers/credentials';
import { PrismaAdapter } from '@auth/prisma-adapter';
import { SiweMessage } from 'siwe';
import type { SiweMessage as SiweMessageType } from 'siwe';
export const { handlers, auth, signOut, signIn } = NextAuth({
...
providers: [
Credentials({
name: 'Ethereum',
credentials: {
message: {
label: 'Message',
type: 'text',
placeholder: '0x0',
},
signature: {
label: 'Signature',
type: 'text',
placeholder: '0x0',
},
},
async authorize(credentials) {
const siwe = new SiweMessage(
JSON.parse(
credentials?.message as string,
) as Partial<SiweMessageType>,
);
const result = await siwe.verify({
signature: credentials?.signature as string,
});
if (result.success) {
return { // 根据自己需要返回
id: siwe.address,
name: siwe.address,
};
}
return null;
},
}),
],
...
});
- 连接钱包组件
'use client';
import React from 'react';
import { getCsrfToken, signIn } from 'next-auth/react';
import { SiweMessage } from 'siwe';
import { useAccount, useConnect, useSignMessage } from 'wagmi';
import { injected } from 'wagmi/connectors';
import { Button } from '@/components/ui/button';
import { PATHS } from '@/constants';
export async function getServerSideProps() {
return {
props: {
csrfToken: await getCsrfToken(),
},
};
}
export const SignWithWallet = () => {
const { signMessageAsync } = useSignMessage();
const { address, isConnected, chain } = useAccount();
const { connect, status } = useConnect();
const signMsg: () => Promise<void> = React.useCallback(async () => {
const message = new SiweMessage({
domain: window.location.host,
address: address,
statement: 'Sign in with Ethereum to the app.',
uri: window.location.origin,
version: '1',
chainId: chain?.id,
nonce: await getCsrfToken(),
});
const signature = await signMessageAsync({
message: message.prepareMessage(),
});
await signIn('credentials', {
message: JSON.stringify(message),
signature,
redirectTo: PATHS.ADMIN_HOME,
});
}, [address, chain, signMessageAsync]);
React.useEffect(() => {
if (status === 'success') {
signMsg().catch(() => ({}));
}
}, [status, signMsg]);
const handleLogin = async () => {
if (!isConnected) {
connect({ connector: injected() });
return;
}
await signMsg();
};
return (
<Button
variant="default"
className="!w-full"
type="button"
onClick={() => handleLogin()}
>
Connect Wallet
</Button>
);
};
注:因为是客户端组件,所以getCsrfToken需要从next-auth/react导出
效果如图: