Node.js 当服务位于代理之后时,如何使用证书从Azure Active Directory获取访问令牌

Node.js 当服务位于代理之后时,如何使用证书从Azure Active Directory获取访问令牌,node.js,azure-active-directory,jwt,msal,msal.js,Node.js,Azure Active Directory,Jwt,Msal,Msal.js,我需要创建调用graph api来访问公司数据的服务。为了进行身份验证,我需要来自Azure Active Directory的JWT令牌。身份验证将使用带有签名证书的应用程序模式。我尝试使用MSAL节点机密客户端应用程序,但该服务需要使用http代理连接到internet。据我所知,MSAL节点不支持此调用,并导致库无法解析“”的地址https://login.microsoftonline.com". 如何使MSAL节点使用代理或在不使用ODMSAL的情况下获取JWT令牌?为了在没有MSAL

我需要创建调用graph api来访问公司数据的服务。为了进行身份验证,我需要来自Azure Active Directory的JWT令牌。身份验证将使用带有签名证书的应用程序模式。我尝试使用MSAL节点机密客户端应用程序,但该服务需要使用http代理连接到internet。据我所知,MSAL节点不支持此调用,并导致库无法解析“”的地址https://login.microsoftonline.com". 如何使MSAL节点使用代理或在不使用ODMSAL的情况下获取JWT令牌?

为了在没有MSAL节点的情况下从azure active directory获取JWT令牌,必须自己生成适当的JWT令牌,然后使用证书私钥对其进行签名。令牌的标头由以下字段组成:

{
  typ: "JWT",
  alg: "RS256",
  kid: "156E...",
  x5t: "iTYVn..."
}
{
  aud: "https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token",
  iss: "{clientId}",
  nbf: 1617952610,
  exp: 1617953210,
  sub: "{clientId}",
  jti: "e13efcf..."
}
  • “kid”是用于签署请求的证书的指纹-下面是一个使用powershell为pfx文件获取它的好例子
  • “x5t”是base64编码和净化的证书指纹
base64编码字符串的清理意味着:

  • 在末尾修剪“=”符号
  • 将“/”替换为“\”
  • 将“+”替换为“-”
用于消毒的示例C#代码:

var sanitized = s.Split('=')[0].Replace('+', '-').Replace('/', '_');
和JS代码:

var sanitized = s.split('=')[0].replace('+', '-').replace('/', '_');
令牌的有效负载由以下字段组成:

{
  typ: "JWT",
  alg: "RS256",
  kid: "156E...",
  x5t: "iTYVn..."
}
{
  aud: "https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token",
  iss: "{clientId}",
  nbf: 1617952610,
  exp: 1617953210,
  sub: "{clientId}",
  jti: "e13efcf..."
}
  • {tenantId}和{clientId}是我们正在验证的应用程序的Azure AD数据
  • “nbf”是令牌开始有效的时间,通常是生成令牌的时间。它具有unix epoch格式,是一个整数
  • “exp”-unix历元格式的令牌过期时间
  • “jti”-唯一的令牌标识符。它可能是随机生成的guid。每个请求都应该不同
如何在JavaScript中获取“nbf”值的示例:

var nbf = Math.floor(new Date().getTime() / 1000);
准备就绪时,标题和有效负载应在上序列化(带有消毒),并用“.”连接:

然后,我们需要使用证书私钥对其进行签名,使用base 64对其进行编码(使用清理),并准备clientAssertion值:

var clientAssertion = token + "." + signedToken;
作为最后一步,您可以发送请求以获取JWT令牌:

const body = new URLSearchParams();

const token = await fetch("https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token", {
    agent: new HttpsProxyAgent("http://..."),
    body: new URLSearchParams({
      "client_assertion": clientAssertion,
      "client_id": "{clientId}",
      "scope": "https://graph.microsoft.com/.default"
      "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
      "grant_type": "client_credentials"
    }),
    method: "POST",
    headers: {
        "content-type": "application/x-www-form-urlencoded"
    }
})
.then(response => response.json().access_token);