简体   繁体   English

使用 Passport.js 的访问令牌的 Azure AD 无效签名

[英]Azure AD Invalid Signature of Access Token using Passport.js

I'm trying to setup authentication for my NodeJS/VueJS app using Azure AD B2B using the passport-azure-ad strategy.我正在尝试使用 Passport-azure-ad 策略使用 Azure AD B2B 为我的 NodeJS/VueJS 应用程序设置身份验证。 I use Authorization Code Flow to get access_token and id_token.我使用授权码流来获取 access_token 和 id_token。 The problem comes when I pass the access_token to the passport-azure-ad strategy.当我将 access_token 传递给 Passport-azure-ad 策略时,问题就出现了。 I get an error saying that the token has an invalid signature (jwt.io shows the same).我收到一条错误消息,指出令牌的签名无效(jwt.io 显示相同)。 If I use the id_token then everything works fine.如果我使用 id_token 那么一切正常。 However I don't want to use the id_token because I want to implement silent refresh and I can only do that with the access_token.但是我不想使用 id_token,因为我想实现静默刷新,而我只能使用 access_token 来做到这一点。

Here is my setup:这是我的设置:

  • I have a VueJS app (with Node.js as backend) and I want to use AAD B2B as a login我有一个 VueJS 应用程序(使用 Node.js 作为后端)并且我想使用 AAD B2B 作为登录
  • On the frontend I'm doing this redirect first:在前端,我首先执行此重定向:
let url = 'https://login.microsoftonline.com/'+TENANT_ID+'/oauth2/v2.0/authorize?'+
    'client_id='+CLIENT_ID+
    '&response_type=code'+
    '&scope=openid offline_access'+
    '&redirect_uri='+window.location.origin+
    '&state='+encodeURIComponent(generateRandomString())+
    '&nonce='+nonce;
  • Then I'm sending the code to my backend to exchange it for a token with this request:然后我将代码发送到我的后端,以使用此请求将其交换为令牌:
let data = {
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
    grant_type: 'authorization_code',
    redirect_uri: window.location.origin,
    code: code
  }
let response = await axios.post(`/${process.env.TENANT}/oauth2/v2.0/token`, data)
  • Then I save the token on the frontend in session storage and send it with each request as Authorization header.然后我将令牌保存在前端的会话存储中,并将其作为授权标头与每个请求一起发送。

Here is the configuration of my BearerStrategy:这是我的 BearerStrategy 的配置:

let options = {
    identityMetadata: "https://login.microsoftonline.com/"+envVars.TENANT+"/v2.0/.well-known/openid-configuration",
    clientID: envVars.CLIENT_ID,
    passReqToCallback: false,
    audience: envVars.CLIENT_ID,
    validateIssuer: true,
    issuer: 'https://sts.windows.net/'+envVars.TENANT+'/',
    loggingLevel: 'error',
}
var bearerStrategy = new OIDCBearerStrategy(options,
  function(token, done) {
    console.log(token)
    if (!token.oid) {
      return done(new Error('oid is not found in token'));
    }
    else {
      return done(null, token.unique_name, token);
    }
  }
);

passport.use(bearerStrategy);

I think the problem is that for some reason I'm getting a v1 token back instead of v2.我认为问题是出于某种原因,我取回了 v1 令牌而不是 v2。 I tried setting "accessTokenAcceptedVersion": 2 in the manifest of the app registration but it didn't work.我尝试在应用程序注册清单中设置"accessTokenAcceptedVersion": 2但它不起作用。

What am I doing wrong?我究竟做错了什么?

Here is the decoded token with some information hidden:这是隐藏了一些信息的解码令牌:

HEADER:ALGORITHM & TOKEN TYPE
{
  "typ": "JWT",
  "nonce": "1BaxlG05CpKz_zLXaJjZ0-nyYqLpMkktzvL9WiOEL74",
  "alg": "RS256",
  "x5t": "kg2LYs2T0CTjIfj4rt6JIynen38",
  "kid": "kg2LYs2T0CTjIfj4rt6JIynen38"
}

PAYLOAD:DATA
{
  "aud": "00000003-0000-0000-c000-000000000000",
  "iss": "https://sts.windows.net/[tenant_id]/",
  "iat": 1601568985,
  "nbf": 1601568985,
  "exp": 1601572885,
  "acct": 0,
  "acr": "1",
  "aio": "ASQA2/8RAAAAq7Z5bMg6AIJJ25iq7RI34DjbdRD9C6vnTlL3ZeilylQ=",
  "amr": [
    "pwd"
  ],
  "app_displayname": "[app_displayname]",
  "appid": "[appid]",
  "appidacr": "1",
  "family_name": "[family_name]",
  "given_name": "[given_name]",
  "idtyp": "user",
  "ipaddr": "[ipaddr]",
  "name": "[name]",
  "oid": "[oid]",
  "platf": "5",
  "puid": "10033FFFAFA83EAE",
  "rh": "0.ATAAdcJAG5J6AkqwgY-6rF6WssXSwci23txClbKekzYO8PwwAE8.",
  "scp": "User.Read profile openid email",
  "sub": "44PNYdl91SJrrrj4F25e1hXFyj3uTt-1ko6bUyT_Gq8",
  "tenant_region_scope": "EU",
  "tid": "[tenant_id]",
  "unique_name": "[email_address]",
  "upn": "[email_address]",
  "uti": "DNAFrnHtrU6wprPVywDNAA",
  "ver": "1.0",
  "wids": [
    "b79fbf4d-3ef9-4689-8143-76b194e85509"
  ],
  "xms_st": {
    "sub": "xpu87pTRk0UTZGgL9MGwrwTtZXP7qn7Aw-byR3_N_fU"
  },
  "xms_tcdt": 1504270371
}

UPDATE: It turned out I was getting a token for the MS Graph API which I couldn't use for my own API.更新:结果我得到了一个不能用于我自己的 API 的 MS Graph API 令牌。 So I had to expose an API for my app and add it to the permissions in AAD.所以我不得不为我的应用公开一个 API 并将其添加到 AAD 中的权限中。 This is a really good tutorial for this case -> https://authguidance.com/2017/12/01/azure-ad-spa-code-sample/对于这种情况,这是一个非常好的教程-> https://authguidance.com/2017/12/01/azure-ad-spa-code-sample/

According to the information you provide, we request access token to call Microsoft Graph.根据您提供的信息,我们请求访问令牌以调用 Microsoft Graph。 If so, we do not need to validate Microsoft graph signature.如果是这样,我们不需要验证 Microsoft 图形签名。 Because MsGraph recognized an opportunity to improve security for users.因为 MsGraph 认识到了提高用户安全性的机会。 They achieved this by putting a 'nonce' into the jwt header.他们通过在 jwt 标头中放入一个“nonce”来实现这一点。 The JWS is signed with a SHA2 of the nonce, the nonce is replaced before the JWS is serialized. JWS 用随机数的 SHA2 签名,在 JWS 序列化之前替换nonce To Validate this token, the nonce will need to be replaced with the SHA2 of the nonce in the header.要验证此令牌,需要将nonce替换为标头中nonce的 SHA2。 Now this can change since there is no public contract.现在这可以改变,因为没有公共合同。 So when calling Microsoft Graph, you should treat access tokens as opaque.因此,在调用 Microsoft Graph 时,您应该将访问令牌视为不透明的。 For more details, please refer here and here有关更多详细信息,请参阅此处此处

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM