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

无需Passport.js等库,在Firebase Cloud Functions实现LinkedIn登录

在Firebase Cloud Functions中实现LinkedIn登录认证(无需Passport.js)

完全可以不用Passport.js这类第三方库,直接基于LinkedIn的OAuth2流程结合Firebase Admin SDK实现认证。核心逻辑是:前端引导用户完成LinkedIn授权拿到授权码,将授权码传给Cloud Functions,后端用授权码兑换access_token、获取用户信息,最后生成Firebase自定义token返回给前端完成登录。

具体步骤分解

1. 前端(React)处理授权跳转

构造LinkedIn授权URL,引导用户跳转完成授权,授权后LinkedIn会携带code参数重定向到你指定的页面。

const handleLinkedInLogin = () => {
  const clientId = "你的LinkedIn客户端ID";
  const redirectUri = "https://你的应用域名/linkedin-callback";
  const scope = "r_liteprofile r_emailaddress"; // 必须申请的权限
  const authUrl = `https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&scope=${encodeURIComponent(scope)}`;
  window.location.href = authUrl;
};

2. 前端回调页获取授权码并传给云函数

在重定向目标页面中提取URL里的code,通过POST请求发送到你的Cloud Functions接口。

import axios from 'axios';
import { getAuth, signInWithCustomToken } from "firebase/auth";

// 在回调页面的useEffect中处理
useEffect(() => {
  const urlParams = new URLSearchParams(window.location.search);
  const code = urlParams.get('code');
  
  if (code) {
    axios.post('/api/linkedin-auth', { code })
      .then(res => {
        // 用Firebase自定义token完成登录
        const auth = getAuth();
        signInWithCustomToken(auth, res.data.token)
          .then(() => {
            // 登录成功,跳转到主页
            window.location.href = '/';
          });
      })
      .catch(err => console.error('认证失败:', err));
  }
}, []);

3. Cloud Functions后端处理授权流程

注意:Cloud Functions环境中发起HTTP请求,推荐使用node-fetch而非axios(避免环境兼容问题),先执行npm install node-fetch安装依赖。

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const fetch = require('node-fetch');

admin.initializeApp();

// 配置LinkedIn密钥(通过firebase functions:config:set linkedin.client_id="xxx" linkedin.client_secret="xxx"设置)
const LINKEDIN_CLIENT_ID = functions.config().linkedin.client_id;
const LINKEDIN_CLIENT_SECRET = functions.config().linkedin.client_secret;
const REDIRECT_URI = "https://你的应用域名/linkedin-callback";

exports.linkedinAuth = functions.https.onRequest(async (req, res) => {
  if (req.method !== 'POST') {
    return res.status(405).send('仅支持POST请求');
  }

  const { code } = req.body;
  if (!code) {
    return res.status(400).send('缺少授权码');
  }

  try {
    // 1. 用授权码兑换access_token
    const tokenRes = await fetch('https://www.linkedin.com/oauth/v2/accessToken', {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        grant_type: 'authorization_code',
        code,
        client_id: LINKEDIN_CLIENT_ID,
        client_secret: LINKEDIN_CLIENT_SECRET,
        redirect_uri: REDIRECT_URI
      })
    });
    const tokenData = await tokenRes.json();
    if (!tokenData.access_token) throw new Error('获取access_token失败');

    // 2. 获取用户基本信息
    const profileRes = await fetch('https://api.linkedin.com/v2/me', {
      headers: { 'Authorization': `Bearer ${tokenData.access_token}` }
    });
    const profileData = await profileRes.json();

    // 3. 获取用户邮箱
    const emailRes = await fetch('https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', {
      headers: { 'Authorization': `Bearer ${tokenData.access_token}` }
    });
    const emailData = await emailRes.json();
    const userEmail = emailData.elements[0]['handle~'].emailAddress;

    // 4. 创建或获取Firebase用户
    let userRecord;
    try {
      userRecord = await admin.auth().getUserByEmail(userEmail);
    } catch (err) {
      // 用户不存在则创建新用户
      userRecord = await admin.auth().createUser({
        email: userEmail,
        displayName: `${profileData.localizedFirstName} ${profileData.localizedLastName}`,
        photoURL: profileData.profilePicture?.displayImage~?.elements[0]?.identifiers[0]?.identifier
      });
    }

    // 5. 生成Firebase自定义token返回给前端
    const customToken = await admin.auth().createCustomToken(userRecord.uid);
    res.status(200).json({ token: customToken });

  } catch (err) {
    functions.logger.error('LinkedIn认证出错:', err);
    res.status(500).send('认证流程失败');
  }
});

常见问题排查

  • Cloud Functions请求报错:检查是否安装了node-fetch,LinkedIn的client ID/secret是否通过Firebase配置正确,请求头和参数是否符合LinkedIn OAuth2规范。
  • CORS问题:在云函数开头添加CORS处理逻辑,或使用cors库解决跨域。
  • 权限不足:确保在LinkedIn开发者平台已申请r_liteprofile和r_emailaddress权限,且redirect URI已正确配置。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 13:25:33