You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Next-Auth中分离登录流程与权限授权流程?

实现Google登录与Google Ads权限授权分离

问题核心

使用NextAuth实现Google登录时,需将基础登录(仅获取用户信息)与Google Ads权限授权流程分离:用户先完成基础登录,后续可自主选择是否关联Google Ads账户,此时再触发Google的权限授权提示;但当前遇到授权后数据库中存储的账户scope未更新,导致调用Google Ads API时出现PERMISSION_DENIED: Request had insufficient authentication scopes.错误。

解决方案

1. 动态调整Google Provider的授权Scope

在NextAuth的API路由中,根据前端传递的自定义参数动态设置授权Scope,区分基础登录和Ads权限授权请求:

// ./src/pages/api/[...nextauth].ts
import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
import { prisma } from "../../server/db/client";
import { env } from "../../env/server.mjs";

export default NextAuth({
  adapter: PrismaAdapter(prisma),
  providers: [
    GoogleProvider({
      clientId: env.GOOGLE_CLIENT_ID,
      clientSecret: env.GOOGLE_CLIENT_SECRET,
      // 动态生成授权配置
      authorization: (params) => {
        // 从请求Query中获取自定义标识
        const askForAdsPerm = params?.req?.query?.askForGoogleAdsPermissions === "true";
        // 基础登录Scope
        const baseScopes = "https://www.googleapis.com/auth/userinfo.email openid https://www.googleapis.com/auth/userinfo.profile";
        // 拼接完整Scope
        const finalScopes = askForAdsPerm 
          ? `${baseScopes} https://www.googleapis.com/auth/adwords`
          : baseScopes;

        return {
          url: "https://accounts.google.com/o/oauth2/v2/auth",
          params: {
            scope: finalScopes,
            response_type: "code",
            // 确保请求额外权限时强制弹出授权提示
            prompt: askForAdsPerm ? "consent" : "select_account",
          },
        };
      },
    }),
  ],
  session: { strategy: "jwt" },
  // 其他配置...
});

2. 前端发起不同类型的登录请求

使用signIn函数的第三个参数传递自定义标识,区分基础登录和权限授权:

// 基础登录(仅获取用户信息)
await signIn("google");

// 请求Google Ads权限授权
await signIn("google", undefined, { askForGoogleAdsPermissions: "true" });

3. 强制更新数据库中的账户Scope

NextAuth的Prisma Adapter默认不会自动更新已存在的Account记录的Scope,需在signIn回调中手动处理更新:

// 在[...nextauth].ts的callbacks中添加
callbacks: {
  async signIn({ account, user }) {
    if (!account) return false;

    // 查找用户已有的Google账户记录
    const existingAccount = await prisma.account.findFirst({
      where: {
        userId: user.id,
        provider: "google",
        providerAccountId: account.providerAccountId,
      },
    });

    if (existingAccount) {
      // 更新账户的Scope、Token等信息
      await prisma.account.update({
        where: { id: existingAccount.id },
        data: {
          scope: account.scope,
          access_token: account.access_token,
          refresh_token: account.refresh_token,
          expires_at: account.expires_at,
        },
      });
    }

    return true;
  },
  // 在JWT回调中同步更新Scope信息,确保Session中能获取到最新权限
  async jwt({ token, account }) {
    if (account) {
      token.accessToken = account.access_token;
      token.refreshToken = account.refresh_token;
      token.expiresAt = account.expires_at;
      token.scope = account.scope;
    }
    return token;
  },
  // 同步Session中的权限信息
  async session({ session, token }) {
    if (token) {
      session.accessToken = token.accessToken;
      session.refreshToken = token.refreshToken;
      session.expiresAt = token.expiresAt;
      session.scope = token.scope;
    }
    return session;
  },
},

4. API调用前验证权限

在调用Google Ads API前,先从Session中校验是否已获取对应权限:

// 示例API路由
import { getServerSession } from "next-auth/next";
import { authOptions } from "./[...nextauth]";

export default async function handler(req, res) {
  const session = await getServerSession(req, res, authOptions);
  
  if (!session) return res.status(401).json({ error: "未授权" });

  // 检查是否包含Google Ads权限
  const hasAdsPermission = session.scope?.includes("https://www.googleapis.com/auth/adwords");
  if (!hasAdsPermission) {
    return res.status(403).json({ error: "缺少Google Ads权限,请先关联账户" });
  }

  // 执行Google Ads API调用逻辑
  // ...
}

内容的提问来源于stack exchange,提问作者Shawn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 19:40:32