next-auth集成以太坊钱包登陆

YoungJeff
·发布于 7 个月前·
64 人看过

Next-auth中可以通过Credentials进行任意凭证登陆,如用户名和密码,那如果通过以太坊钱包进行登陆呢?

基于siwe这个库即可实现

siwe全称:sign in with Ethereum

具体实现

版本:

{
  "next-auth": "5.0.0-beta.15",
	"siwe": "^2.3.2",
}
  1. 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;
          },
        }),
      ],
      ...
    });
    
    
  2. 连接钱包组件

    '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导出

效果如图:

2024-05-22 10.52.22.gif

项目源码:https://github.com/Young-Jeff/blog2.0

web3
$ cd ..