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

Next-Auth实现已授权应用免用户同意Discord自动登录

实现已注册用户Discord免授权登录方案

针对你的Next.js + NextAuth + Strapi场景,要实现已注册用户后续登录跳过Discord授权流程,核心思路是通过Strapi验证用户注册状态,结合Discord的静默授权(prompt: none)和token刷新机制,避免重复授权。以下是具体实现步骤:


1. 确保Strapi用户模型关联Discord ID

先确认你的Strapi用户模型中添加了discordId字段(存储Discord用户的唯一ID),这样才能通过Discord账号查询用户是否已注册。


2. 修改[...nextauth].ts配置

2.1 完善signIn回调:控制授权流程

添加signIn回调,判断用户是否已注册,并动态调整Discord授权参数:

callbacks: {
  // 新增signIn回调
  async signIn({ account, profile }) {
    // 通过Discord用户ID查询Strapi中的用户
    const strapiRes = await fetch(
      `${process.env.STRAPI_URL}/api/users?filters[discordId][$eq]=${profile.id}`,
      { headers: { 'Authorization': `Bearer ${process.env.STRAPI_API_TOKEN}` } }
    );
    const strapiUser = await strapiRes.json();

    if (strapiUser.data.length === 0) {
      // 未注册用户:引导到注册页面(按你现有注册流程处理)
      return '/register';
    }

    // 已注册用户:设置Discord静默授权,无需用户确认
    if (account?.authorization) {
      account.authorization.params = {
        ...account.authorization.params,
        prompt: 'none'
      };
    }
    return true;
  },

  // 原session回调修改:同步更多用户信息
  async session({ session, token }) {
    session.jwt = token.jwt;
    session.id = token.id;
    session.strapiUserId = token.strapiUserId;
    session.discordId = token.discordId;
    session.tokenError = token.error;
    return session;
  },

  // 完善jwt回调:持久化token并自动刷新
  async jwt({ token, account, profile }) {
    // 首次登录:存储Discord token和Strapi用户信息
    if (account && profile) {
      const strapiRes = await fetch(
        `${process.env.STRAPI_URL}/api/users?filters[discordId][$eq]=${profile.id}`,
        { headers: { 'Authorization': `Bearer ${process.env.STRAPI_API_TOKEN}` } }
      );
      const strapiUser = await strapiRes.json();

      if (strapiUser.data.length > 0) {
        token.strapiUserId = strapiUser.data[0].id;
        token.discordId = profile.id;
        token.accessToken = account.access_token;
        token.refreshToken = account.refresh_token;
        // 转换过期时间为毫秒
        token.expiresAt = account.expires_at ? account.expires_at * 1000 : Date.now() + 3600000;
      }
    }

    // 自动刷新过期的access token
    if (token.expiresAt && Date.now() > token.expiresAt) {
      try {
        const refreshRes = await fetch('https://discord.com/api/oauth2/token', {
          method: 'POST',
          headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
          body: new URLSearchParams({
            client_id: process.env.DISCORD_CLIENT_ID,
            client_secret: process.env.DISCORD_CLIENT_SECRET,
            grant_type: 'refresh_token',
            refresh_token: token.refreshToken,
          }),
        });

        const refreshedTokens = await refreshRes.json();
        if (!refreshRes.ok) throw refreshedTokens;

        // 更新token信息
        token.accessToken = refreshedTokens.access_token;
        token.expiresAt = Date.now() + refreshedTokens.expires_in * 1000;
        if (refreshedTokens.refresh_token) {
          token.refreshToken = refreshedTokens.refresh_token;
        }
        delete token.error;
      } catch (err) {
        console.error('Discord token刷新失败:', err);
        token.error = 'RefreshAccessTokenError';
      }
    }

    return token;
  }
}

2.2 调整DiscordProvider默认授权参数

默认给新用户显示授权确认页,已注册用户通过signIn回调动态改为静默授权:

DiscordProvider({
  clientId: process.env.DISCORD_CLIENT_ID,
  clientSecret: process.env.DISCORD_CLIENT_SECRET,
  authorization: { 
    params: { 
      scope: DISCORD_SCOPES,
      prompt: 'consent' // 新用户需要手动授权
    } 
  },
}),

3. 前端登录逻辑优化

在登录按钮组件中,先检查现有会话,避免重复登录:

import { useSession, signIn } from 'next-auth/react';
import { useRouter } from 'next/router';

export default function LoginButton() {
  const { data: session } = useSession();
  const router = useRouter();

  const handleLogin = async () => {
    if (session) {
      // 已有有效会话,直接跳转首页
      router.push('/');
      return;
    }
    // 触发Discord登录,已注册用户会自动静默授权
    await signIn('discord');
  };

  return <button onClick={handleLogin}>Discord登录</button>;
}

关键逻辑说明

  1. 静默授权(prompt: none):Discord会自动返回授权结果,无需用户跳转授权页,仅当用户未授权过该应用时才会报错(此时可引导用户重新授权)。
  2. Token自动刷新:利用Discord的refresh_token定期刷新access_token,避免因token过期触发重新授权流程。
  3. Strapi用户校验:每次登录前通过Discord ID查询Strapi,确保只有已注册用户才能触发静默授权。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 19:36:32