Firebase集成Discord OpenID/OAuth2认证遇错求助
解决Firebase与Discord OAuth2集成的认证错误
错误原因分析
1. OIDC Provider方式的auth/invalid-credential错误
Firebase的OpenID Connect Provider要求身份提供者返回标准OIDC格式的id_token(JWT类型,包含iss、sub等必填声明),但Discord的OAuth2服务并未实现完整的OIDC规范,不会返回符合要求的id_token,仅提供access_token,导致Firebase无法验证凭证有效性,触发该错误。
2. signInWithCustomToken的auth/internal-error错误
signInWithCustomToken方法仅接受Firebase Admin SDK生成的自定义令牌,而非第三方服务(如Discord)的access_token。直接传入Discord的access_token不符合Firebase的令牌格式要求,因此触发内部错误。
正确集成方案:自定义OAuth2流程
通过前端发起Discord授权、后端交换令牌并生成Firebase自定义令牌的方式实现集成,步骤如下:
1. 配置Discord OAuth2应用
- 在Discord开发者平台的OAuth2设置中,添加前端回调页面的重定向URL(如
https://your-domain.com/auth/callback) - 勾选所需权限范围(scope):
identify、email(可根据业务需求扩展)
2. 前端发起Discord授权请求
构造授权URL并通过弹窗或重定向跳转:
const discordClientId = "YOUR_DISCORD_CLIENT_ID"; const redirectUri = "https://your-domain.com/auth/callback"; const scope = "identify email"; const state = crypto.randomUUID(); // 生成随机state防范CSRF攻击 // 存储state到localStorage用于后续验证 localStorage.setItem("discord_auth_state", state); // 跳转至Discord授权页 window.location.href = `https://discord.com/api/oauth2/authorize?client_id=${discordClientId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${encodeURIComponent(scope)}&state=${state}`;
3. 后端交换令牌并生成Firebase自定义令牌
注意:此步骤必须在后端执行,避免泄露Discord的client_secret
以Node.js为例:
const admin = require("firebase-admin"); const axios = require("axios"); // 初始化Firebase Admin SDK(提前配置好服务账号密钥) admin.initializeApp({ credential: admin.credential.cert("path/to/serviceAccountKey.json") }); // 交换Discord授权码获取用户信息 async function exchangeDiscordCode(code) { const tokenResponse = await axios.post( "https://discord.com/api/oauth2/token", new URLSearchParams({ client_id: "YOUR_DISCORD_CLIENT_ID", client_secret: "YOUR_DISCORD_CLIENT_SECRET", grant_type: "authorization_code", code: code, redirect_uri: "https://your-domain.com/auth/callback" }), { headers: { "Content-Type": "application/x-www-form-urlencoded" } } ); // 获取Discord用户信息 const userResponse = await axios.get("https://discord.com/api/users/@me", { headers: { Authorization: `Bearer ${tokenResponse.data.access_token}` } }); return userResponse.data; } // 生成Firebase自定义令牌 async function generateFirebaseToken(discordUser) { // 用Discord用户ID作为Firebase UID(可自定义关联逻辑) const uid = `discord:${discordUser.id}`; // 添加自定义用户声明(可选) const additionalClaims = { email: discordUser.email, displayName: `${discordUser.username}#${discordUser.discriminator}`, avatar: discordUser.avatar }; return admin.auth().createCustomToken(uid, additionalClaims); } // 示例接口:接收前端传来的授权码,返回Firebase自定义令牌 app.get("/auth/discord/callback", async (req, res) => { const { code, state } = req.query; const storedState = localStorage.getItem("discord_auth_state"); // 验证state防止CSRF攻击 if (state !== storedState) { return res.status(403).send("Invalid state parameter"); } try { const discordUser = await exchangeDiscordCode(code); const customToken = await generateFirebaseToken(discordUser); res.json({ customToken }); } catch (error) { res.status(500).send("Authentication failed"); } });
4. 前端使用自定义令牌登录Firebase
在回调页面中,获取后端返回的自定义令牌并完成登录:
// 从URL中提取授权码 const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get("code"); // 向后端请求Firebase自定义令牌 fetch(`/auth/discord/callback?code=${code}`) .then(res => res.json()) .then(({ customToken }) => { // 登录Firebase return firebase.auth().signInWithCustomToken(customToken); }) .then((userCredential) => { // 登录成功,处理用户信息 const user = userCredential.user; console.log("登录成功", user); // 跳转到应用首页 window.location.href = "/"; }) .catch(error => { console.error("登录失败", error); });
内容的提问来源于stack exchange,提问作者DIGI Byte
相关产品推荐
相关产品推荐

