在 nextjs 项目中使用 GitHub 授权登录

发布时间:2025-04-02
6599 字, 需阅读 14 分钟
已被阅读76

本文主要介绍如何在 nextjs 项目中使用 GitHub 授权登录,通过本文你将了解到什么是NextAuth.js,如何配置 GitHub app,如何使用nestjs进行服务端验证 GitHub登录后的 token 的有效性。

什么是 NextAuth.js

NextAuth.js 是适用于 Next.js 应用程序的完整开源身份验证解决方案,它支持多种身份验证方式,包括 OAuth(如 Google、GitHub 、Twitter 等)、电子邮件登录和自定义凭证登录。

OAuth 简介

OAuth(开放授权)是一个开放标准协议,允许用户授权第三方应用访问其在其他服务提供商(如社交媒体平台)上存储的受保护资源,而无需向这些应用透露自己的用户名和密码。

通过OAuth,用户可以提供一个访问令牌(token)给第三方应用,该令牌限定了访问权限的范围(scope)和有效期限,从而确保用户数据的安全性和可控性。

前端代码

首先在nexjs项目中需要安装 NextAuth.js ,以下代码使用 NextAuth v4。

npm install next-auth

创建 api 文件

app/api/auth/[...nextauth]/route.js

import NextAuth from "next-auth" import GithubProvider from "next-auth/providers/github" export const authOptions = { // providers 配置 providers: [ GithubProvider({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET, }), ], } export default NextAuth(authOptions)

请注意你的nexjs版本,如果你的nexjs版本是Next.js 13.2 之前的,路径模式为pages模式的,则你的路径应该为pages/api/auth/[...nextauth]/route.js

创建你的 GitHub 应用

打开 https://github.com/settings/apps

1.png 2.png 3.png

我们拿到复制 Client ID 和 Client secret 到 .env.local 文件中。

# GitHub OAuth GITHUB_ID=你的Client ID GITHUB_SECRET=你的Client secret

前端登录界面代码

import { signIn } from "next-auth/react"; const handleGitHubLogin = async () => { try { messageApi.loading("正在跳转到 GitHub 登录..."); const result = await signIn("github", { redirect: false, callbackUrl: searchParams.get("callbackUrl") || "/" }); } catch (error) { console.error("GitHub login exception:", error); messageApi.error(`登录异常: ${error.message || "未知错误"}`); } }; ...省略其他的代码 <Button icon={<GithubOutlined />} size="large" className="w-full flex items-center justify-center" onClick={handleGitHubLogin} > 使用 GitHub 账号登录 </Button>

完善 [...nextauth]/route.js

import NextAuth from "next-auth"; import GitHubProvider from "next-auth/providers/github"; import { verifyGitHubUser } from "@/services/user"; // 检查环境变量是否配置 const githubId = process.env.GITHUB_ID; const githubSecret = process.env.GITHUB_SECRET; if (!githubId || !githubSecret) { console.error('警告: GitHub OAuth 凭据未配置。请在 .env.local 文件中设置 GITHUB_ID 和 GITHUB_SECRET'); } const handler = NextAuth({ providers: [ GitHubProvider({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET, // 添加配置项以获取更多信息 profile(profile) { return { id: profile.id.toString(), name: profile.name || profile.login, email: profile.email, image: profile.avatar_url, // 保存其他有用信息 login: profile.login, githubProfile: profile }; }, httpOptions: { timeout: 10000 }, }) ], pages: { signIn: '/auth/login', }, // 指定需要保护的路由 matcher: [ '/protected/:path*', // 排除不需要认证的路径 '/((?!api|_next/static|_next/image|favicon.ico).*)', ], callbacks: { async signIn({ user, account, profile, email, credentials }) { return true; }, async jwt({ token, user, account }) { try { // 基本信息处理 if (user) { token.email = user?.email; if (user?.accessToken) { token.accessToken = user.accessToken; } // GitHub登录处理 if (account?.provider === "github") { // 基本信息设置 token.name = user.name; token.image = user.image; token.login = user.login; token.githubToken = account.access_token; // 注意:确保等待这个异步操作完成 if (account.access_token) { try { // 自己的后台验证服务 const response = await verifyGitHubUser({ token: account.access_token }); if (response?.data?.token) { token.accessToken = response.data.token; // 标记验证已完成 token.githubVerified = true; } } catch (error) { // 更详细的错误日志 console.error("GitHub验证失败:", { message: error.message, status: error.response?.status, data: error.response?.data }); } } } } return token; } catch (error) { console.error('JWT处理错误:', error); return token; } }, async session({ session, token }) { try { // 使用解构和展开运算符处理用户信息 session.user = { ...session.user, ...(token?.email && { email: token.email }), ...(token?.name && { name: token.name }), ...(token?.image && { image: token.image }), ...(token?.login && { login: token.login }) }; // 处理 token 相关信息 if (token?.accessToken) { session.accessToken = token.accessToken; } if (token?.githubToken) { session.githubToken = token.githubToken; } return session; } catch (error) { console.error('Session callback error:', { error: error.message, sessionId: session?.id, userId: session?.user?.email }); return session; } }, }, session: { strategy: 'jwt', maxAge: 30 * 24 * 60 * 60, }, }); export { handler as GET, handler as POST };

因为GitHub登录是在前端运行的,我们往往需要记住用户的登录信息然后生成自己服务端的认证token,上面的 verifyGitHubUser 请求就是验证前端使用GitHub登录后信息的有效性,如果验证有效则自己的业务服务需生成jwt token。

后端验证逻辑

async verifyGitHubToken(token: string) { try { // 使用 GitHub API 验证 token const response = await fetch('https://api.github.com/user', { headers: { 'Authorization': `token ${token}`, 'Accept': 'application/vnd.github.v3+json', 'User-Agent': 'NestJS-App' } }); // console.log('verifyGitHubToken response',response) if (!response.ok) { throw new HttpException( { status: HttpStatus.UNAUTHORIZED, message: 'GitHub token 无效', }, HttpStatus.UNAUTHORIZED ); } } // 验证通过生成 jwt token return { token:this.jwtService.sign(payload) }

总结

使用 NextAuth.js 非常方便接入第三方登录和自定义登录,因为 nextjs本身是全栈架构,你可以在nextjs中适配自己的用户数据库,本文不做叙述。

作者:admin
版权声明:
本文采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
分享到: