next-auth授权twitter
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 应用
-
选择 X API v2 免费版本,点击 Developer Portal。
-
进入后台,创建应用,获取 TWITTER_ID 和 TWITTER_SECRET。
勾选协议后,进入后台,默认有一个应用,点击set up设置应用
注意:
Callback URL需要根据环境更改
(但是我使用过程中,发现更改Callback URL后,不能立即生效,很耽误事,所以我的做法是注册多个后台帐号,建多个应用,然后项目中根据环境变量加载不同应用id和密钥,就不用频繁更改配置)
- 本地开发就是
http://localhost:3000/api/auth/callback/twitter
- 测试环境就是
https://test.aaa.com/api/auth/callback/twitter
设置后点击save,保存好如下id和密钥,项目中要用(密钥可以重置)
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里的值
这里说一下我的理解:
首先需要知道构建环境和运行环境:
• 构建时的环境(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帐号,授权流程确可以切不同的帐号,反之也一样。