next-auth授权twitter

YoungJeff
·发布于 3 个月前·
42 人看过

1. 安装 NextAuth.js

npm install next-auth

详情参考NextAuth.js

2. 创建 Session Provider

新建 /src/providers/session.tsx,用于全局包裹应用的会话状态:

'use client';
import { ReactNode } from 'react';
import { SessionProvider } from 'next-auth/react';

type Props = {
    children: ReactNode;
};
export const SessionClientProvider = ({ children }: Props) => {
    return <SessionProvider>{children}</SessionProvider>;
};

3. 使用 Session Provider 包裹应用

在 layout.tsx 中引入 SessionClientProvider,确保应用中各组件可以使用会话状态:

import { ReactNode } from 'react';

import { SessionClientProvider } from '@/providers/index';
type Props = {
    children: ReactNode;
};
export default function RootLayout({ children }: Props) {
    return (
        <html lang="en">
            <body>
                <SessionClientProvider>{children}</SessionClientProvider>
            </body>
        </html>
    );
}

4. 配置 NextAuth.js 路由

在 /src/app/api/auth/[...nextauth]/route.js 中配置 NextAuth.js 和 Twitter Provider:

import NextAuth from 'next-auth';
import TwitterProvider from 'next-auth/providers/twitter';

const handler = NextAuth({
    session: { strategy: 'jwt' },
    providers: [
        TwitterProvider({
            clientId: process.env.TWITTER_ID,
            clientSecret: process.env.TWITTER_SECRET,
            client: {
                httpOptions: {
                    timeout: 20000, // 若终端里有超时报错,则延长超时时间
                },
            },
            version: '2.0', // 重要
            authorization: {
                params: {
                    scope: 'tweet.read users.read follows.read offline.access', // 访问权限
                },
            },
            profile(profile) { // 这一步是为了拿到twitter更详细的用户信息,否则下面的session只能取到name,而取不到username
                return {
                    id: profile.data.id,
                    name: profile.data.name,
                    screen_name: profile.data.username,
                    image: profile.data.profile_image_url,
                };
            },
        }),
    ],
    cookies: {
      sessionToken: {
        name: `next-auth.session-token`,
        options: {
          httpOnly: true,
          sameSite: 'lax',
          path: '/',
          secure: process.env.NODE_ENV === 'production'
        }
      },
    },
    secret: process.env.AUTH_SECRET, // 重要:根据环境区分,需要和twitter后台中的callback url保持一致
    debug: process.env.NODE_ENV !== 'production',
    callbacks: {
        async jwt({ token, account, user }) {
            if (account) {
                token.accessToken = account.access_token;
                token.sub = account.providerAccountId;
            }
            if (user) {
                token.user = { ...user };
            }
            return token;
        },
        async session({ session, token }) {
            if (token?.sub) {
                session.user.id = token.sub;
            }
            session.user = { ...token.user };
            return session;
        },
    },
});

export { handler as GET, handler as POST };

5. Twitter 开发者后台设置

申请 Twitter 应用

  1. 访问 Twitter Developer Portal

  2. 选择 X API v2 免费版本,点击 Developer Portal

  3. 进入后台,创建应用,获取 TWITTER_ID 和 TWITTER_SECRET。

Pasted Graphic 2.png

Pasted Graphic 3.png
勾选协议后,进入后台,默认有一个应用,点击set up设置应用

Pasted Graphic 6.png

Pasted Graphic 7.png
注意:
Callback URL需要根据环境更改
(但是我使用过程中,发现更改Callback URL后,不能立即生效,很耽误事,所以我的做法是注册多个后台帐号,建多个应用,然后项目中根据环境变量加载不同应用id和密钥,就不用频繁更改配置)

  • 本地开发就是http://localhost:3000/api/auth/callback/twitter
  • 测试环境就是https://test.aaa.com/api/auth/callback/twitter

设置后点击save,保存好如下id和密钥,项目中要用(密钥可以重置)

Client Secret.png

6. 配置环境变量

在项目根目录下创建 .env、.env.test 和 .env.production 文件,添加如下环境变量:

******************** next-auth ********************
TWITTER_ID=a0M4U0bydjNGaedDSFI2RWwtSGw6rMTpjaQ
TWITTER_SECRET=sP_6mNcaf3xew-X-nc5v-dmwTJ4fFhdHMAUsiDrAK82ijd4y
AUTH_SECRET=a2e85298ff00bde2wbcbde4d5c429f21b9aac7f343be90566e5b5a89444b575f

# 本地开发localhost可不配置
NEXTAUTH_URL=https://test.aaa.com/

• TWITTER_ID 和 TWITTER_SECRET 在第 5 步获取。

• AUTH_SECRET 可以通过命令生成:

openssl rand -hex 32

注意:
除非部署在vercel,否则NEXTAUTH_URL一定要配置,且和第5步中的Callback UR域名保持一致官网有说明

  • 测试环境https://test.aaa.com/
  • 线上环境https://prod.aaa.com/

7. 配置 package.json 脚本

为不同的环境配置打包和启动命令,更新 package.json:

"scripts": {
    "build:test": "cross-env NEXT_PUBLIC_ENV=test NODE_ENV=test next build",
    "build:prod": "cross-env NEXT_PUBLIC_ENV=prod next build",
    "start:test": "cross-env NODE_ENV=test next start",
    "start": "next start",
},

注意: start:test很重要,需要在测试环境执行

明明打包都区分了test,为什么start的时候还要区分test?

先说一下我遇到的问题:

一开始,我的scripts是这样的(没有start:test)

"scripts": {
    "build:test": "cross-env NEXT_PUBLIC_ENV=test NODE_ENV=test next build",
    "build:prod": "cross-env NEXT_PUBLIC_ENV=prod next build",
    "start": "next start",
},

在本地pnpm build:test时,输出的环境变量是取自.env.test没问题,但是当发布到测试环境后,next-auth取到的却是.env.production里的值。

我是通过这个接口发现授权运行时取的是.env.production里的值

Pasted Graphic 10.png

这里说一下我的理解:
首先需要知道构建环境和运行环境:

构建时的环境(build:test 和 build:prod)主要决定了项目的打包行为,比如使用的 API 接口地址、功能开关等,打包后的静态资源会依据这些变量进行生成。

运行时环境(start:test 和 start)则影响到 Node.js 环境的行为。例如,运行时可能会使用不同的数据库连接、日志记录策略、缓存策略等,这些配置通常依赖于 NODE_ENV 变量。

而在我的配置中,buidl:test和start:test的分别代表构建和运行,前者在控制台输出的是构建时的环境变量(所以我在控制台看到的没问题),后者是运行时的环境变量。

而next start 是 Next.js 用来启动生产环境服务的命令。如果不指定默认运行环境就是prod;

next-auth中需要的变量不是手动引入到nextAuth配置中的,而是按照不同providers的规则在env中定义好。
例如:

// github
AUTH_GITHUB_ID=ce24341f1c3c6e106b3
AUTH_GITHUB_SECRET=253f8533ff2edfc121771132e57c4087f
// twitter
TWITTER_ID=123Tp23233dfdfd23jaQ
TWITTER_SECRET=s_xhew-X323nc5v-dmwTJ4fFhdH1MAUr82ijd4y

另外next-auth实际上是服务端逻辑,所以在运行期间next-auth会自动去取对应的环境变量(而非构建时的),如果是next start,那自然取了.env.production。
所以,我通过cross-env NODE_ENV=test next start在运行期间,指定为test,就让其取到了.env.test,就解决了这个问题

8. 在组件中使用 NextAuth

配置完成后,可以在组件中使用 NextAuth 提供的 signIn、signOut 和 useSession:

import { signIn, signOut, useSession } from 'next-auth/react';
const ConnectTwitter = () => {
    const { data: session } = useSession();
    const xClick  = () => {
      if(session?.user?.screen_name){
        // 退出
        signOut({ redirect: false });
        window.open('https://twitter.com/logout') // 根据需要执行
      }else{
        // 登陆
        signIn('twitter');
      }
    }
    return (
        <div onClick={xClick}>
           {session?.user?.name}
        </div>
    );
};
export default ConnectTwitter;

注意:
signOut只能把当前的授权状态清理掉

如果想实现切换twitter账户,只能通过window.open('https://twitter.com/logout')才能退出切换其他帐号。

因为我实操发现,如果不通过这种方式,就算你去twitter上切换了帐号,比如你从a切到b后,再回来你自己的网页上授权,发现跳转授权的页面依然是a的。

我的猜测:twitter授权和twitter官网登录状态是不同的体系,也就是说,twitter官网上你可以一直登陆a帐号,授权流程确可以切不同的帐号,反之也一样。

Next.js
NextAuth.js
$ cd ..