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

使用@node-saml/node-saml开发SAML SP失败,求修复及Azure配置指引

问题描述

我基于@node-saml/node-saml库开发SAML服务提供商(SP),但认证功能无法正常工作,希望得到修复方法或可靠参考方向。同时对Azure Entra ID中的SAML应用配置存在疑问,不清楚Assertion Consumer Service(ACS)URL和实体ID的正确设置方式,目前误将应用基础URL设为ACS URL。

代码片段

vc.ts

export const SAML = async () => {
  const express = require("express");
  const bodyParser = require("body-parser");
  const { SAML } = require("@node-saml/node-saml");

  const app = express();
  const port = 3001;

  app.use(bodyParser.urlencoded({ extended: true }));

  const spOptions = {
    issuer: "saml_XXX",
    cert: "MII...XXX",
    assert_endpoint: "https://5XXX",
    audience: "https://5XXX",
  };

  const sp = new SAML(spOptions);

  // SAMLrequest
  app.get("/login", (req: any, res: { redirect: (arg0: any) => void }) => {
    const request = sp.createLoginRequest();
    res.redirect(request);
  });

  // SAML Assertion
  app.post(
    "/assert",
    (
      req: { body: { SAMLResponse: any } },
      res: {
        status: (arg0: number) => {
          (): any;
          new (): any;
          send: { (arg0: any): any; new (): any };
        };
        redirect: (arg0: string) => any;
        send: (arg0: string) => void;
      }
    ) => {
      const response = req.body.SAMLResponse;
      sp.parseLoginResponse(
        response,
        (err: any, profile: any, loggedOut: any) => {
          if (err) {
            console.log("err:", err);
            return res.status(500).send(err);
          }

          console.log(profile);

          if (loggedOut) {
            return res.redirect("/");
          }

          res.send("Login successful!");
          return "OK!";
        }
      );
    }
  );

  return app;
};

saml-request.ts

import { error } from "console";
import { SAML } from "../../../lib/vc";

const createSamlRequest = async (req: any, res: any) => {
  const app = await SAML();
  app(req, res);
  try {
    console.log("success");
  } catch {
    console.log("failed", error);
  }
};

export default createSamlRequest;
修复建议与配置说明

代码层面修复点

  1. 补充SP配置关键参数:spOptions缺少IDP核心配置,@node-saml/node-saml需要IDP的地址和证书才能生成有效请求并验证响应。补充以下参数:
    const spOptions = {
      issuer: "saml_XXX", // SP实体ID,需和Azure配置完全一致
      cert: "MII...XXX", // SP签名证书(可选,若启用SP请求签名)
      assert_endpoint: "https://你的公网域名/assert", // ACS URL,和Azure配置一致
      audience: "saml_XXX", // 应为SP实体ID,而非ACS URL
      entryPoint: "https://login.microsoftonline.com/你的租户ID/saml2", // Azure提供的SSO URL
      idpCert: "MII...", // Azure的签名证书,用于验证IDP响应
    };
    
  2. 修正登录请求生成逻辑:createLoginRequest需通过回调获取正确的跳转URL,直接调用无法生成有效地址:
    app.get("/login", (req: any, res: any) => {
      sp.createLoginRequestUrl(req, (err: any, url: any) => {
        if (err) return res.status(500).send(err);
        res.redirect(url);
      });
    });
    
  3. 修正响应解析参数:parseLoginResponse需传入完整请求对象req,库会自动解析请求体中的SAML响应:
    sp.parseLoginResponse(req, (err: any, profile: any, loggedOut: any) => {
      // 原有逻辑
    });
    
  4. 避免重复创建Express实例:SAML函数每次调用都会生成新的Express实例,建议将Express初始化与路由定义分离,防止重复注册路由导致的冲突。

Azure Entra ID配置说明

  1. 实体ID(Entity ID):对应SP配置的issuer参数,是SP的唯一标识,可自定义字符串(如saml_XXX)或URL格式(如https://你的域名/saml/metadata),必须和spOptions.issuer完全一致。
  2. ACS URL:是IDP发送SAML响应的专属端点,对应代码中assert_endpoint的值,需为公网可访问的https地址(如https://你的域名/assert),不能与应用基础URL混淆。
  3. 基础URL设置:仅作为应用的根地址标识,无需与ACS URL一致,根据实际应用部署地址填写即可。
  4. 证书配置:在Azure中上传SP的公钥证书(对应spOptions.cert),用于IDP验证SP请求的签名;同时复制Azure提供的签名证书到spOptions.idpCert,用于SP验证IDP的响应合法性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 08:43:17