@acrossthecloud/passport-azure-ad 中文文档教程

发布于 6年前 浏览 16 项目主页 更新于 3年前

Microsoft Azure Active Directory Passport.js Plug-In


passport-azure-adPassport 策略的集合 帮助您与 Azure Active Directory 集成。 它包括 OpenID 连接, WS-Federation 和 SAML-P 身份验证和授权。 这些供应商让你 将您的 Node 应用程序与 Microsoft Azure AD 集成,以便您可以使用它的许多功能, 包括 Web 单点登录 (WebSSO)、带 OAuth 的端点保护和 JWT 令牌 发行和验证。

passport-azure-ad 已经过测试,可与 Microsoft Azure Active Directory 一起使用 以及 Microsoft Active Directory Federation Services

1. Security Vulnerability in Versions < 1.4.6 and 2.0.0

passport-azure-ad 有一个已知的安全漏洞,影响版本 <1.4.6 和 2.0.0。 请立即更新至>=1.4.6 或>=2.0.1。 有关详细信息,请参阅安全通知

2. Versions

当前版本 - 4.0.0
最低推荐版本 - 1.4.6
您可以在更改日志中找到每个版本的更改。

3. Contribution History

准备就绪的故事

Throughput Graph

4. Installation

$ npm install passport-azure-ad

5. Usage

该库包含两个策略:OIDCStrategy 和 BearerStrategy。

OIDCStrategy 使用 OpenID Connect 协议进行 Web 应用程序登录。 它以下列方式工作: 如果用户未登录,passport 会向 AAD (Azure Active Directory) 发送身份验证请求,AAD 会提示用户输入他或她的登录凭据。 身份验证成功后,根据您选择的流程,Web 应用程序最终将直接从 AAD 授权端点或通过在 AAD 令牌端点兑换代码来取回 id令牌。 Passport 然后验证 idtoken 并将 id_token 中的声明传播回验证回调,并让框架完成剩余的身份验证过程。 如果整个过程成功,passport 将用户信息添加到req.user 并传递给下一个中间件。 如果出现错误,passport 要么发回未经授权的响应,要么将用户重定向到您指定的页面(例如主页或登录页面)。

BearerStrategy 使用 Bearer Token 协议来保护 web 资源/api。 它以下列方式工作: 用户向受保护的 Web API 发送请求,该 API 在授权标头或正文中包含访问令牌。 Passport 提取并验证访问令牌,并将 access_token 中的声明传播到验证回调,让框架完成剩余的身份验证过程。 身份验证成功后,passport 将用户信息添加到req.user 并将其传递给下一个中间件,这通常是 web 资源/api 的业务逻辑。 如果出现错误,passport 会发回未经授权的响应。

对于这两种策略,我们都支持 AAD v1、v2 和 B2C 租户。 请查看第 7 节的示例。 您可以在 https://manage.windowsazure.com 管理 v1 租户和注册应用程序。 对于 v2 租户和应用程序,您应该转到 https://apps.dev.microsoft.com。 对于 B2C 租户,请转到 https://manage.windowsazure.com 并单击“管理 B2C 设置”以注册应用程序和策略。

5.1 OIDCStrategy

5.1.1 Configure strategy and provide callback function

5.1.1.1 Sample using the OIDCStrategy
passport.use(new OIDCStrategy({
    identityMetadata: config.creds.identityMetadata,
    clientID: config.creds.clientID,
    responseType: config.creds.responseType,
    responseMode: config.creds.responseMode,
    redirectUrl: config.creds.redirectUrl,
    allowHttpForRedirectUrl: config.creds.allowHttpForRedirectUrl,
    clientSecret: config.creds.clientSecret,
    validateIssuer: config.creds.validateIssuer,
    isB2C: config.creds.isB2C,
    issuer: config.creds.issuer,
    passReqToCallback: config.creds.passReqToCallback,
    scope: config.creds.scope,
    loggingLevel: config.creds.loggingLevel,
    loggingNoPII: config.creds.loggingNoPII,
    nonceLifetime: config.creds.nonceLifetime,
    nonceMaxAmount: config.creds.nonceMaxAmount,
    useCookieInsteadOfSession: config.creds.useCookieInsteadOfSession,
    cookieEncryptionKeys: config.creds.cookieEncryptionKeys,
    clockSkew: config.creds.clockSkew,
  },
  function(iss, sub, profile, accessToken, refreshToken, done) {
    if (!profile.oid) {
      return done(new Error("No oid found"), null);
    }
    // asynchronous verification, for effect...
    process.nextTick(function () {
      findByOid(profile.oid, function(err, user) {
        if (err) {
          return done(err);
        }
        if (!user) {
          // "Auto-registration"
          users.push(profile);
          return done(null, profile);
        }
        return done(null, user);
      });
    });
  }
));
5.1.1.2 Options
  • identityMetadata(必需)

    Microsoft Identity Portal 提供的元数据端点,可在运行时提供密钥和其他重要信息。 示例:

  • v1 租户特定端点

    https://login.microsoftonline.com/your_tenant_name.onmicrosoft.com/.well-known/openid-configuration
    https://login.microsoftonline.com/your_tenant_guid/.well-known/openid-configuration
  • v1 common endpoint
    https://login.microsoftonline.com/common/.well-known/openid-configuration
  • v2 tenant-specific endpoint
    https://login.microsoftonline.com/your_tenant_name.onmicrosoft.com/v2.0/.well-known/openid-configuration 
    https://login.microsoftonline.com/your_tenant_guid/v2.0/.well-known/openid-configuration
  • v2 common endpoint
    https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration

对于 B2C,您不能使用 v2 公共端点,除非您使用 tenantIdOrName 选项在 passport.authenticate 中指定租户。 有关详细信息,请参阅第 5.1.3 节。

  • clientID(必需)

    您的应用程序在 AAD(Azure Active Directory)中的客户端 ID

  • responseType(必需)

    必须是 'code','code idtoken', “id令牌代码”或“id令牌”。 对于仅登录流程,您可以使用“idtoken”; 如果您想要访问令牌,请使用“code”、“code idtoken”或“id_token code”。

  • responseMode(必需)

    必须是“query”或“formpost”。 这就是您获取代码或 id令牌的方式。 建议在所有情况下使用“form_post”。

  • redirectUrl(必需)

    必须是 https url 字符串,除非您将 allowHttpForRedirectUrl 设置为 true。 这是在 AAD 中为您的应用注册的回复 URL。 生产环境应始终为 redirectUrl 使用 https。

  • passReqToCallback(必需)

    是否要将 req 用作验证回调中的第一个参数。 有关详细信息,请参阅第 5.1.1.3 节。

  • allowHttpForRedirectUrl(视情况而定)

    如果您想为 redirectUrl 使用 http url,如 http://localhost:3000,则需要设置为 true。

  • clientSecret (Conditional)

    responseType 不是id_token 时,我们必须提供客户端凭证来兑换授权码。 此凭据可以是客户端密码或客户端断言。 非B2C租户支持这两种流,但B2C租户只支持client secret流。

    对于 B2C 租户:如果 responseType 不是“id_token”,则需要 clientSecret

    对于非 B2C 租户:如果 responseType 不是 id_token,开发人员必须提供 clientSecretthumbprint 和 <代码>privatePEMKey。 如果提供,我们使用 clientSecret; 否则我们使用 thumbprintprivatePEMKey 作为客户端断言流程。

    clientSecret 是您的应用程序在 AAD 中的应用程序密钥。 对于B2C,app key有时会包含\,请将app key中的\替换成两个\,否则\会被当做转义符开头。

  • thumbprint (视情况而定)

    如果您想使用客户端断言流程,则需要。 thumbprint 是公钥指纹(哈希值)的base64url格式。

  • privatePEMKey (视情况而定)

    如果您想使用客户端断言流程,则为必需项。 privatePEMKey 是私有 pem 密钥字符串。

  • isB2C (视情况而定)

    如果您使用的是 B2C 租户,则需要设置为 true。

  • validateIssuer (视情况而定)

    如果您不想验证颁发者,则需要设置为 false,默认值为 true。 我们根据用户提供的 issuer 值和我们从租户特定端点获得的 issuer 值验证 id_token 中的 iss 声明。 如果您为 identityMetadata 使用公共端点并且想要验证颁发者,则必须提供 issuer,或者使用 tenantIdOrName< 为每个登录请求提供租户passport.authenticate 中的 /code> 选项(有关详细信息,请参阅第 5.1.3 节)。

  • issuer (视情况而定)

    这可以是字符串或字符串数​​组。 需要issuer的情况见validateIssuer

  • jweKeyStore(视情况而定)

    如果您想接受和解密 JWE 紧凑序列化格式的 id_token,则需要此选项。 看 有关详细信息,请参阅第 5.1.1.4 节。

  • useCookieInsteadOfSession(视情况而定)

    Passport-azure-ad 默认情况下会在会话中保存状态和随机数以用于验证目的。 如果 useCookieInsteadOfSession 设置为 true,passport-azure-ad 将加密 state/nonce 和 而是将它们放入 cookie 中。 当我们想要完全无会话时,换句话说,当您在 passport.authenticate 函数中使用 { session: false } 选项时,这很有用。 如果 useCookieInsteadOfSession 设置为 true,您必须提供 cookieEncryptionKeys 用于 cookie 加密和解密。

  • cookieEncryptionKeys(视情况而定)

    如果 useCookieInsteadOfSession 设置为 true,您必须提供 cookieEncryptionKeys。 它是以下格式的数组:[ {key: '…', 'iv': '…' }, {key: '…', 'iv': '…' }, …]。 key 可以是任意长度为 32 的字符串,iv 可以是任意长度为 12 的字符串。我们总是使用第一组 key/iv 来加密 cookie,但是我们尝试所有的 key/iv 来解密 cookie。 如果您想进行密钥翻转,这将很有帮助。 我们使用的加解密算法是aes-256-gcm。 您可以使用 nonceLifetimenonceMaxAmount 选项限制 cookie 数量和过期时间。

  • scope (可选)

    openid 之外的范围值列表,指示访问所请求资源所需的访问令牌范围。 例如,['电子邮件'、'个人资料']。 如果您需要 v2 端点的刷新令牌,则必须包括“离线访问”范围。

  • loggingLevel (可选)

    日志级别。 “信息”、“警告”或“错误”。

  • loggingNoPII(可选)

    如果设置为 true,则不会记录令牌和声明等个人信息。 默认值是true。

  • nonceLifetime (可选)

    nonce 在会话中的生命周期(以秒为单位)。 默认值为 3600 秒。

  • nonceMaxAmount(可选)

    您希望在会话或 cookie 中保留的最大 nonce 数量。 默认数量为 10。如果总数超过,最旧的随机数将被删除。 (您可以将此数字保持得非常小,因为随机数将在验证后从会话或 cookie 中删除。这主要处理用户一次打开多个登录选项卡并希望返回第一个登录页面以输入用户凭据的情况。每个登录选项卡都会在会话或 cookie 中产生一个随机数,因此我们只尊重最近的 nonceMaxAmount 许多登录选项卡。)

  • clockSkew(可选)

    此值是令牌验证中允许的时钟偏差(以秒为单位) . 它必须是正整数。 默认值为 300 秒。

5.1.1.3 Verify callback

如果将 passReqToCallback 选项设置为 false,则可以使用以下签名之一进行验证回调

  function(iss, sub, profile, jwtClaims, access_token, refresh_token, params, done)
  function(iss, sub, profile, access_token, refresh_token, params, done)
  function(iss, sub, profile, access_token, refresh_token, done)
  function(iss, sub, profile, done)
  function(iss, sub, done)
  function(profile, done)

如果将 passReqToCallback 选项设置为 true,则可以使用以下签名之一对于验证回调

  function(req, iss, sub, profile, jwtClaims, access_token, refresh_token, params, done)
  function(req, iss, sub, profile, access_token, refresh_token, params, done)
  function(req, iss, sub, profile, access_token, refresh_token, done)
  function(req, iss, sub, profile, done)
  function(req, iss, sub, done)
  function(req, profile, done)

5.1.1.4 JWE support

我们支持 JWE 紧凑序列化格式的加密 id_token。

支持的密钥加密算法有:

RSA1_5RSA-OAEPA128KWA256KWdir< /代码>。

支持的内容加密算法有:

A128CBC-HS256A192CBC-HS384A256CBC-HS512A128GCM、 <代码>A256GCM

为了解密 id_token,必须使用 jweKeyStore 选项以 JWK 格式提供密钥。 我们会先 用相应的孩子尝试钥匙。 如果解密失败,我们将尝试 jweKeyStore 中所有可能的密钥。 以下是 jweKeyStore

    jweKeyStore: [ 
      { 'kid': 'sym_key_256', 'kty': 'oct', 'k': 'WIVds2iwJPwNhgUgwZXmn/46Ql1EkiL+M+QqDRdQURE=' }, 
      { 'kid': 'sym_key_128', 'kty': 'oct', 'k': 'GawgguFyGrWKav7AX4VKUg'}, 
      { 'kid': 'sym_key_384', 'kty': 'oct', 'k': 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v'},
      { 'kid': 'rsa_key', 
        'kty': 'RSA', 
        "n":"6-FrFkt_TByQ_L5d7or-9PVAowpswxUe3dJeYFTY0Lgq7zKI5OQ5RnSrI0\n\
             T9yrfnRzE9oOdd4zmVj9txVLI-yySvinAu3yQDQou2Ga42ML_-K4Jrd5cl\n\
             MUPRGMbXdV5Rl9zzB0s2JoZJedua5dwoQw0GkS5Z8YAXBEzULrup06fnB5\n\
             n6x5r2y1C_8Ebp5cyE4Bjs7W68rUlyIlx1lzYvakxSnhUxSsjx7u_mIdyw\n\
             yGfgiT3tw0FsWvki_KYurAPR1BSMXhCzzZTkMWKE8IaLkhauw5MdxojxyB\n\
             VuNY-J_elq-HgJ_dZK6g7vMNvXz2_vT-SykIkzwiD9eSI9UWfsjw",
        "e":"AQAB",
        "d":"C6EGZYf9U6RI5Z0BBoSlwy_gKumVqRx-dBMuAfPM6KVbwIUuSJKT3ExeL5\n\
             P0Ky1b4p-j2S3u7Afnvrrj4HgVLnC1ks6rEOc2ne5DYQq8szST9FMutyul\n\
             csNUKLOM5cVromALPz3PAqE2OCLChTiQZ5XZ0AiH-KcG-3hKMa-g1MVnGW\n\
             -SSmm27XQwRtUtFQFfxDuL0E0fyA9O9ZFBV5201ledBaLdDcPBF8cHC53G\n\
             m5G6FRX3QVpoewm3yGk28Wze_YvNl8U3hvbxei2Koc_b9wMbFxvHseLQrx\n\
             vFg_2byE2em8FrxJstxgN7qhMsYcAyw1qGJY-cYX-Ab_1bBCpdcQ",
        "p":"_avCCyuo7hHlqu9Ec6R47ub_Ul_zNiS-xvkkuYwW-4lNnI66A5zMm_BOQV\n\
             MnaCkBua1OmOgx7e63-jHFvG5lyrhyYEmkA2CS3kMCrI-dx0fvNMLEXInP\n\
             xd4np_7GUd1_XzPZEkPxBhqf09kqryHMj_uf7UtPcrJNvFY-GNrzlJk",
        "q":"7gvYRkpqM-SC883KImmy66eLiUrGE6G6_7Y8BS9oD4HhXcZ4rW6JJKuBzm\n\
             7FlnsVhVGro9M-QQ_GSLaDoxOPQfHQq62ERt-y_lCzSsMeWHbqOMci_pbt\n\
             vJknpMv4ifsQXKJ4Lnk_AlGr-5r5JR5rUHgPFzCk9dJt69ff3QhzG2c",
        "dp":"ErP3OpudePAY3uGFSoF16Sde69PnOra62jDEZGnPx_v3nPNpA5sr-tNc8\n\
              bQP074yQl5kzSFRjRlstyW0TpBVMP0ocbD8RsN4EKsgJ1jvaSIEoP87Ox\n\
              duGkim49wFA0Qxf_NyrcYUnz6XSidY3lC_pF4JDJXg5bP_x0MUkQCTtQE",
        "dq":"YbBsthPt15Pshb8rN8omyfy9D7-m4AGcKzqPERWuX8bORNyhQ5M8JtdXc\n\
              u8UmTez0j188cNMJgkiN07nYLIzNT3Wg822nhtJaoKVwZWnS2ipoFlgrB\n\
              gmQiKcGU43lfB5e3qVVYUebYY0zRGBM1Fzetd6Yertl5Ae2g2CakQAcPs",
        "qi":"lbljWyVY-DD_Zuii2ifAz0jrHTMvN-YS9l_zyYyA_Scnalw23fQf5WIcZ\n\
              ibxJJll5H0kNTIk8SCxyPzNShKGKjgpyZHsJBKgL3iAgmnwk6k8zrb_lq\n\
              a0sd1QWSB-Rqiw7AqVqvNUdnIqhm-v3R8tYrxzAqkUsGcFbQYj4M5_F_4"
      },
      { 'kid': 'ras_key_2',
        'kty': 'RSA',
        'privatePemKey': 
          '-----BEGIN RSA PRIVATE KEY-----\n\
          MIIEowIBAAKCAQEA6+FrFkt/TByQ/L5d7or+9PVAowpswxUe3dJeYFTY0Lgq7zKI\n\
          5OQ5RnSrI0T9yrfnRzE9oOdd4zmVj9txVLI+yySvinAu3yQDQou2Ga42ML/+K4Jr\n\
          d5clMUPRGMbXdV5Rl9zzB0s2JoZJedua5dwoQw0GkS5Z8YAXBEzULrup06fnB5n6\n\
          x5r2y1C/8Ebp5cyE4Bjs7W68rUlyIlx1lzYvakxSnhUxSsjx7u/mIdywyGfgiT3t\n\
          w0FsWvki/KYurAPR1BSMXhCzzZTkMWKE8IaLkhauw5MdxojxyBVuNY+J/elq+HgJ\n\
          /dZK6g7vMNvXz2/vT+SykIkzwiD9eSI9UWfsjwIDAQABAoIBAAuhBmWH/VOkSOWd\n\
          AQaEpcMv4CrplakcfnQTLgHzzOilW8CFLkiSk9xMXi+T9CstW+Kfo9kt7uwH5766\n\
          4+B4FS5wtZLOqxDnNp3uQ2EKvLM0k/RTLrcrpXLDVCizjOXFa6JgCz89zwKhNjgi\n\
          woU4kGeV2dAIh/inBvt4SjGvoNTFZxlvkkpptu10MEbVLRUBX8Q7i9BNH8gPTvWR\n\
          QVedtNZXnQWi3Q3DwRfHBwudxpuRuhUV90FaaHsJt8hpNvFs3v2LzZfFN4b28Xot\n\
          iqHP2/cDGxcbx7Hi0K8bxYP9m8hNnpvBa8SbLcYDe6oTLGHAMsNahiWPnGF/gG/9\n\
          WwQqXXECgYEA/avCCyuo7hHlqu9Ec6R47ub/Ul/zNiS+xvkkuYwW+4lNnI66A5zM\n\
          m/BOQVMnaCkBua1OmOgx7e63+jHFvG5lyrhyYEmkA2CS3kMCrI+dx0fvNMLEXInP\n\
          xd4np/7GUd1/XzPZEkPxBhqf09kqryHMj/uf7UtPcrJNvFY+GNrzlJkCgYEA7gvY\n\
          RkpqM+SC883KImmy66eLiUrGE6G6/7Y8BS9oD4HhXcZ4rW6JJKuBzm7FlnsVhVGr\n\
          o9M+QQ/GSLaDoxOPQfHQq62ERt+y/lCzSsMeWHbqOMci/pbtvJknpMv4ifsQXKJ4\n\
          Lnk/AlGr+5r5JR5rUHgPFzCk9dJt69ff3QhzG2cCgYASs/c6m5148Bje4YVKgXXp\n\
          J17r0+c6trraMMRkac/H+/ec82kDmyv601zxtA/TvjJCXmTNIVGNGWy3JbROkFUw\n\
          /ShxsPxGw3gQqyAnWO9pIgSg/zs7F24aSKbj3AUDRDF/83KtxhSfPpdKJ1jeUL+k\n\
          XgkMleDls//HQxSRAJO1AQKBgGGwbLYT7deT7IW/KzfKJsn8vQ+/puABnCs6jxEV\n\
          rl/GzkTcoUOTPCbXV3LvFJk3s9I9fPHDTCYJIjdO52CyMzU91oPNtp4bSWqClcGV\n\
          p0toqaBZYKwYJkIinBlON5XweXt6lVWFHm2GNM0RgTNRc3rXemHq7ZeQHtoNgmpE\n\
          AHD7AoGBAJW5Y1slWPgw/2bootonwM9I6x0zLzfmEvZf88mMgP0nJ2pcNt30H+Vi\n\
          HGYm8SSZZeR9JDUyJPEgscj8zUoShio4KcmR7CQSoC94gIJp8JOpPM62/5amtLHd\n\
          UFkgfkaosOwKlarzVHZyKoZvr90fLWK8cwKpFLBnBW0GI+DOfxf+\n\
          -----END RSA PRIVATE KEY-----\n';
      }
  ]

5.1.2 Use passport.authenticate to protect routes

的示例:要完成示例,请提供与路径对应的路由 发送给策略的配置参数:

app.get('/login', 
  passport.authenticate('azuread-openidconnect', { failureRedirect: '/' }),
  function(req, res) {
    log.info('Login was called in the Sample');
    res.redirect('/');
});

// POST /auth/openid/return
//   Use passport.authenticate() as route middleware to authenticate the
//   request.  If authentication fails, the user will be redirected back to the
//   home page.  Otherwise, the primary route function function will be called,
//   which, in this example, will redirect the user to the home page.
app.post('/auth/openid/return',
  passport.authenticate('azuread-openidconnect', { failureRedirect: '/' }),
  function(req, res) { 
    res.redirect('/');
  });

app.get('/logout', function(req, res){
  req.logout();
  res.redirect('/');
});

5.1.3 Options available for passport.authenticate

  • failureRedirect:认证失败时重定向到的url

  • session:如果你不想要一个持久的登录会话,你可以使用<代码>会话:假。 默认值是true。

  • customState:如果您想使用自定义状态值而不是随机生成的状态值

  • resourceURL:如果您需要某些资源的 access_token。 请注意,此选项仅适用于 v1 端点和 codecode id_tokenid_token code 流。 对于 v2 端点,资源被视为一个范围,因此在创建时应在 scope 字段中指定 战略。

  • tenantIdOrName:如果您想指定要用于此请求的租户。 您可以使用租户 GUID 或租户名称(如“contoso.onmicrosoft.com”)。 注意:

  • 您必须为 identityMetadata 使用通用端点,否则此选项将被忽略。 我们将从您为此请求指定的租户获取和使用元数据。

  • 此选项仅适用于登录请求,换句话说,不应包含代码或 idtoken 的请求。 Passport 在发送身份验证请求之前将 tenantIdOrName 值保存在会话中。 当我们收到包含代码或 id令牌的请求时,我们会从会话中检索保存的 tenantIdOrName 值并使用该值。

  • 如果您使用的是 B2C 公共端点,则每个登录请求都必须使用 tenantIdOrName

  • domain_hint:如果您想指定用户登录时应使用的域。B2C 租户不支持此选项。

  • login_hint:如果您想在登录页面中使用给定值预填充用户名。 该值应该是用户的 upn,而不是电子邮件(尽管大多数时候它们是相同的)。

  • 提示:v1和v2端点支持loginconsentadmin_consent; B2C 端点仅支持登录

  • response:如果你想使用 cookie 而不是 session 来保存状态/随机数,这是必需的。 请参阅第 5.1.4 节。

示例:

  passport.authenticate('azuread-openidconnect', { failureRedirect: '/', session: false, customState: 'my_state', resourceURL: 'https://graph.microsoft.com/mail.send'});

  passport.authenticate('azuread-openidconnect', { tenantIdOrName: 'contoso.onmicrosoft.com' });

5.1.4 Session free support

Passport 框架使用会话来保持持久的登录会话。 作为一个插件,我们也默认使用session来存储state和nonce,不管你在passport.authenticate中是否使用{session: false}选项。 要完全无会话,您必须配置 passport-azure-ad 以创建状态/随机数 cookie,而不是将它们保存在会话中。 请遵循以下示例:

  passport.use(new OIDCStrategy({
    ...
    nonceLifetime: 600,  // state/nonce cookie expiration in seconds
    nonceMaxAmount: 5,   // max amount of state/nonce cookie you want to keep (cookie is deleted after validation so this can be very small)
    useCookieInsteadOfSession: true,  // use cookie, not session
    cookieEncryptionKeys: [ { key: '12345678901234567890123456789012', 'iv': '123456789012' }],  // encrypt/decrypt key and iv, see `cookieEncryptionKeys` instruction in section 5.1.1.2
  },
    // any supported verify callback
    function(iss, sub, profile, accessToken, refreshToken, done) {
    ...
  });

  app.get('/login', passport.authenticate('azuread-openidconnect', { session: false }));

5.2 BearerStrategy

5.2.1 Configure strategy and provide callback function

5.2.1.1 Sample using the BearerStrategy
// We pass these options in to the ODICBearerStrategy.

var options = {
  identityMetadata: config.creds.identityMetadata,
  clientID: config.creds.clientID,
  validateIssuer: config.creds.validateIssuer,
  issuer: config.creds.issuer,
  passReqToCallback: config.creds.passReqToCallback,
  isB2C: config.creds.isB2C,
  policyName: config.creds.policyName,
  allowMultiAudiencesInToken: config.creds.allowMultiAudiencesInToken,
  audience: config.creds.audience,
  loggingLevel: config.creds.loggingLevel,
  loggingNoPII: config.creds.loggingNoPII,
  clockSkew: config.creds.clockSkew,
  scope: config.creds.scope
};

var bearerStrategy = new BearerStrategy(options,
  function(token, done) {
    log.info('verifying the user');
    log.info(token, 'was the token retreived');
    findById(token.oid, function(err, user) {
      if (err) {
        return done(err);
      }
      if (!user) {
        // "Auto-registration"
        log.info('User was added automatically as they were new. Their oid is: ', token.oid);
        users.push(token);
        owner = token.oid;
        return done(null, token);
      }
      owner = token.oid;
      return done(null, user, token);
    });
  }
);
5.2.1.2 Options
  • identityMetadata(必需)

    Microsoft Identity Portal 提供的元数据端点,它在运行时提供密钥和其他重要信息。 示例:

  • v1 租户特定端点

    https://login.microsoftonline.com/your_tenant_name.onmicrosoft.com/.well-known/openid-configuration
    https://login.microsoftonline.com/your_tenant_guid/.well-known/openid-configuration
  • v1 common endpoint
    https://login.microsoftonline.com/common/.well-known/openid-configuration
  • v2 tenant-specific endpoint
    https://login.microsoftonline.com/your_tenant_name.onmicrosoft.com/v2.0/.well-known/openid-configuration 
    https://login.microsoftonline.com/your_tenant_guid/v2.0/.well-known/openid-configuration
  • v2 common endpoint
    https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration

对于 B2C,您只能使用 v2 租户特定端点。

  • clientID (必需)

    您的应用程序在 AAD(Azure Active Directory)中的客户端 ID

  • passReqToCallback (必需)

    您是否要使用 req 作为验证回调中的第一个参数。 有关详细信息,请参阅第 5.2.1.3 节。

  • isB2C (视情况而定)

    如果您使用的是 B2C 租户,则需要设置为 true。

  • policyName (视情况而定)

    如果您使用的是 B2C 租户,则为必需。 它是以“B2C1”(不区分大小写)开头的字符串。

  • validateIssuer (视情况而定)

    如果您不想验证颁发者,则需要设置为 false,默认值为 true。 我们根据用户提供的 issuer 值和我们从租户特定端点获得的 issuer 值验证 id_token 中的 iss 声明。 如果您使用 identityMetadata 的公共端点并且想要验证颁发者,则必须提供 issuer,或在 passport.authenticate 中提供 tenantIdOrName

  • issuer (视情况而定)

    这可以是字符串或字符串数​​组。 需要issuer的情况见validateIssuer

  • allowMultiAudiencesInToken(视情况而定)

    如果您允许其 aud 声明包含多个值的 access_token,则为必需。

  • scope(可选)

    此值是您接受的范围数组。 如果提供了这个值,我们将检查令牌是否包含以下之一 这些公认的范围。 如果未提供此值,我们将不会检查令牌范围。

  • audience(可选)

    必须是字符串或字符串数​​组。 我们使 access_token 中针对 audienceaud 声明无效。 audience 的默认值为 clientID

  • loggingLevel (可选)

    日志级别。 “信息”、“警告”或“错误”。

  • loggingNoPII(可选)

    如果设置为 true,则不会记录令牌和声明等个人信息。 默认值是true。

  • clockSkew(可选)

    此值是令牌验证中允许的时钟偏差(以秒为单位)。 它必须是正整数。 默认值为 300 秒。

5.2.1.3 Verify callback

如果将 passReqToCallback 选项设置为 false,则可以使用以下验证回调

  function(token, done)

如果将 passReqToCallback 选项设置为 true,则可以使用以下验证回调

  function(req, token, done)

5.2.2 Use passport.authenticate to protect resources or APIs

在下面的示例中,我们正在使用护照来保护“/api/tasks”。 用户向“/api/tasks”发送 GET 请求,并在授权标头或正文中使用访问令牌。 Passport 验证访问令牌,将来自 access_token 的相关声明添加到 req.user,并将请求传递给 listTasks 中间件。 然后 listTasks 中间件可以读取 req.user 中的用户信息,并列出与该用户相关的所有任务。 请注意,我们每次都进行身份验证,因此我们不需要将此用户保持在会话中,这可以通过使用 session: false 选项来实现。

  server.get('/api/tasks', passport.authenticate('oauth-bearer', { session: false }), listTasks);

5.2.3 Options available for passport.authenticate

  • session:如果你不想要持久的登录会话,你可以使用session: false。 默认值是true。

  • tenantIdOrName:如果您使用公共端点,则可以使用此选项动态提供租户。

示例:

  passport.authenticate('oauth-bearer', { session: false });

6. Test

在库根文件夹中,输入以下命令安装依赖包:

    $ npm install

6.1. Run all tests except the end to end tests

输入以下命令运行测试:

    $ npm test

6.2. Run all tests including the end to end tests

6.2.1. Create test applications

首先,您需要在v1 租户中注册一个应用程序,在v2 租户中注册一个应用程序,在B2C 租户中注册一个应用程序。

对于 v2 应用程序,您应该在 https://apps.dev.microsoft.com/ 而不是 Azure 门户上注册它。

对于 B2C 应用程序,创建名为“B2C1signin”、“B2C1signup”的策略。 对于每个策略,选择“本地帐户”作为身份提供者,然后选择 以下:

  • “B2C1注册”:

  • 注册属性:“显示名称”、“电子邮件地址”、“名字”、“姓氏”

  • 应用程序声明:“显示名称”、“电子邮件地址”、 'Given Name'、'Identity Provider'、'Surname'、'Users Object ID'

  • 'B2C1signin':

  • 应用程序声明:'Display Name'、'Email Addresses'、'Given Name'、'身份提供商”、“姓氏”、“用户对象 ID”

您还需要单击“B2C1注册”边栏选项卡中的“立即运行”按钮以创建用户。

对于 B2C 应用程序,您还需要创建至少一个范围并将其提供给测试参数。 请参阅如何为 B2C 访问令牌创建范围。 在承载b2c测试中,我们将使用 OIDCStrategy 来获得一个 B2C 范围的访问令牌,并使用 BearerStrategy 来验证范围。 注意范围我们使用完整的 url b2c_params.scopeForOIDC 但只有 b2c_params.scopeForBearer 中的名称。 例如, b2c_params.scopeForOIDC=['https://sijun1b2c.onmicrosoft.com/oidc-b2c/read']b2c_params.scopeForBearer=['read']

6.2.2. Fill the test parameters

打开test/End_to_end_test/script.js,设置is_test_parameters_completed参数为true。 对于 test_parameters 变量,填写您的应用程序的租户 ID/客户端 ID/客户端密码,以及您的应用程序用户的用户名/密码。

对于 thumbprintprivatePEMKey 参数,您需要为您的应用指定证书并在 Azure Active Directory 中注册公钥。 thumbprint是公钥指纹的base64url格式,privatePEMKey是pem私钥字符串。 对于 v1 租户,您可以关注 this post 生成证书并注册公钥。 对于 v2 租户,您可以转到 v2 门户 中的应用程序页面,然后单击生成新密钥对。 将生成证书供您下载。 在这种情况下会自动注册相应的公钥。

6.2.3. Run the tests

键入以下命令来运行测试:

    $ cd test/End_to_end_test
    $ npm install
    $ npm install grunt -g
    $ grunt run_tests_with_e2e

测试将自动运行,在终端中您可以看到有多少测试通过/失败。 可以在此处找到更多详细信息。

7. Logging

Personal Identifiable Information (PII) & Organizational Identifiable Information (OII)

默认情况下,passport-azure-ad 日志记录不会捕获或记录任何 PII 或 OII。 该库允许应用程序开发人员通过在配置选项中配置 loggingNoPII 来启用此功能。 通过启用 PII 或 OII,应用程序负责安全处理高度敏感的数据并遵守任何监管要求。

//PII or OII logging disabled. Default Logger does not capture any PII or OII.
var options = {
  ...
  loggingNoPII: true,
  ...
};

//PII or OII logging enabled
var options = {
  ...
  loggingNoPII: false,
  ...
};

8. Samples and Documentation

我们在 GitHub 上提供了全套示例应用程序和文档 帮助你开始学习 Azure 身份系统。 这包括 Windows、Windows Phone、iOS、OSX、Android、 和Linux。 我们还提供身份验证流程的完整演练,例如 OAuth2、OpenID Connect、Graph API 和其他很棒的功能。

此插件的 Azure Identity 示例可在以下链接中找到:

8.1 Samples for OpenID connect strategy

8.2 Samples for Bearer strategy

9. Community Help and Support

我们利用 Stack Overflow 与社区合作,支持 Azure Active Directory 及其 SDK,包括这个。 我们强烈建议您在 Stack Overflow 上提问(我们都在那里!)同时浏览现有问题,看看是否有人以前有过您的问题。

我们建议您使用“adal”标签,以便我们可以看到它! 以下是关于 ADAL 堆栈溢出的最新问答:http://stackoverflow.com/questions/tagged/adal

10. Security Reporting

如果您发现我们的库或服务存在安全问题,请向 secure@microsoft.com 报告并提供尽可能详细的信息。 您提交的内容可能有资格通过 Microsoft 赏金 计划获得赏金。 请不要将安全问题发布到 GitHub Issues 或任何其他公共站点。 我们将在收到信息后尽快与您联系。 我们鼓励您访问此页面并订阅安全咨询警报,以获取有关何时发生安全事件的通知。

11. Contributing

所有代码均根据 MIT 许可证获得许可,我们在 GitHub 上积极分类。 我们热忱欢迎贡献和反馈。 您可以克隆存储库并立即开始贡献。

更多详细信息关于贡献

12. Releases

请检查releases 更新。

13. Acknowledgements

该代码基于 Henri Bergius 的 passport-saml 库和 Matias Woloski 的 passport-wsfed-saml2 库以及 Kiyofumi Kondoh 的 passport-openid -谷歌

14. License

版权所有 (c) Microsoft Corp。保留所有权利。 根据麻省理工学院许可证获得许可;

15. Microsoft Open Source Code of Conduct

我们重视并遵守 Microsoft 开源行为准则

该项目采用了 Microsoft 开源行为准则。 有关详细信息,请参阅行为准则常见问题解答或联系 如有任何其他问题或意见,请联系 opencode@microsoft.com

Microsoft Azure Active Directory Passport.js Plug-In


passport-azure-ad is a collection of Passport Strategies to help you integrate with Azure Active Directory. It includes OpenID Connect, WS-Federation, and SAML-P authentication and authorization. These providers let you integrate your Node app with Microsoft Azure AD so you can use its many features, including web single sign-on (WebSSO), Endpoint Protection with OAuth, and JWT token issuance and validation.

passport-azure-ad has been tested to work with both Microsoft Azure Active Directory and with Microsoft Active Directory Federation Services.

1. Security Vulnerability in Versions < 1.4.6 and 2.0.0

passport-azure-ad has a known security vulnerability affecting versions <1.4.6 and 2.0.0. Please update to >=1.4.6 or >=2.0.1 immediately. For more details, see the security notice.

2. Versions

Current version - 4.0.0
Minimum recommended version - 1.4.6
You can find the changes for each version in the change log.

3. Contribution History

Stories in Ready

Throughput Graph

4. Installation

$ npm install passport-azure-ad

5. Usage

This library contains two strategies: OIDCStrategy and BearerStrategy.

OIDCStrategy uses OpenID Connect protocol for web application login purposes. It works in the following manner: If a user is not logged in, passport sends an authentication request to AAD (Azure Active Directory), and AAD prompts the user for his or her sign-in credentials. On successful authentication, depending on the flow you choose, web application will eventually get an idtoken back either directly from the AAD authorization endpoint or by redeeming a code at the AAD token endpoint. Passport then validates the idtoken and propagates the claims in id_token back to the verify callback, and let the framework finish the remaining authentication procedure. If the whole process is successful, passport adds the user information to req.user and passes it to the next middleware. In case of error, passport either sends back an unauthorized response or redirects the user to the page you specified (such as homepage or login page).

BearerStrategy uses Bearer Token protocol to protect web resource/api. It works in the following manner: User sends a request to the protected web api which contains an accesstoken in either the authorization header or body. Passport extracts and validates the accesstoken, and propagates the claims in access_token to the verify callback and let the framework finish the remaining authentication procedure. On successful authentication, passport adds the user information to req.user and passes it to the next middleware, which is usually the business logic of the web resource/api. In case of error, passport sends back an unauthorized response.

We support AAD v1, v2 and B2C tenants for both strategies. Please check out section 7 for the samples. You can manage v1 tenants and register applications at https://manage.windowsazure.com. For v2 tenants and applications, you should go to https://apps.dev.microsoft.com. For B2C tenants, go to https://manage.windowsazure.com and click 'Manage B2C settings' to register applications and policies.

5.1 OIDCStrategy

5.1.1 Configure strategy and provide callback function

5.1.1.1 Sample using the OIDCStrategy
passport.use(new OIDCStrategy({
    identityMetadata: config.creds.identityMetadata,
    clientID: config.creds.clientID,
    responseType: config.creds.responseType,
    responseMode: config.creds.responseMode,
    redirectUrl: config.creds.redirectUrl,
    allowHttpForRedirectUrl: config.creds.allowHttpForRedirectUrl,
    clientSecret: config.creds.clientSecret,
    validateIssuer: config.creds.validateIssuer,
    isB2C: config.creds.isB2C,
    issuer: config.creds.issuer,
    passReqToCallback: config.creds.passReqToCallback,
    scope: config.creds.scope,
    loggingLevel: config.creds.loggingLevel,
    loggingNoPII: config.creds.loggingNoPII,
    nonceLifetime: config.creds.nonceLifetime,
    nonceMaxAmount: config.creds.nonceMaxAmount,
    useCookieInsteadOfSession: config.creds.useCookieInsteadOfSession,
    cookieEncryptionKeys: config.creds.cookieEncryptionKeys,
    clockSkew: config.creds.clockSkew,
  },
  function(iss, sub, profile, accessToken, refreshToken, done) {
    if (!profile.oid) {
      return done(new Error("No oid found"), null);
    }
    // asynchronous verification, for effect...
    process.nextTick(function () {
      findByOid(profile.oid, function(err, user) {
        if (err) {
          return done(err);
        }
        if (!user) {
          // "Auto-registration"
          users.push(profile);
          return done(null, profile);
        }
        return done(null, user);
      });
    });
  }
));
5.1.1.2 Options
  • identityMetadata (Required)

    The metadata endpoint provided by the Microsoft Identity Portal that provides the keys and other important information at runtime. Examples:

  • v1 tenant-specific endpoint

    https://login.microsoftonline.com/your_tenant_name.onmicrosoft.com/.well-known/openid-configuration
    https://login.microsoftonline.com/your_tenant_guid/.well-known/openid-configuration
  • v1 common endpoint
    https://login.microsoftonline.com/common/.well-known/openid-configuration
  • v2 tenant-specific endpoint
    https://login.microsoftonline.com/your_tenant_name.onmicrosoft.com/v2.0/.well-known/openid-configuration 
    https://login.microsoftonline.com/your_tenant_guid/v2.0/.well-known/openid-configuration
  • v2 common endpoint
    https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration

For B2C, you cannot use v2 common endpoint unless you specify the tenant in passport.authenticate using tenantIdOrName option. See section 5.1.3 for more details.

  • clientID (Required)

    The client ID of your application in AAD (Azure Active Directory)

  • responseType (Required)

    Must be 'code', 'code idtoken', 'idtoken code' or 'idtoken'. For login only flows you can use 'idtoken'; if you want accesstoken, use 'code', 'code idtoken' or 'id_token code'.

  • responseMode (Required)

    Must be 'query' or 'formpost'. This is how you get code or idtoken back. 'form_post' is recommended for all scenarios.

  • redirectUrl (Required)

    Must be a https url string, unless you set allowHttpForRedirectUrl to true. This is the reply URL registered in AAD for your app. Production environment should always use https for redirectUrl.

  • passReqToCallback (Required)

    Whether you want to use req as the first parameter in the verify callback. See section 5.1.1.3 for more details.

  • allowHttpForRedirectUrl (Conditional)

    Required to set to true if you want to use http url for redirectUrl like http://localhost:3000.

  • clientSecret (Conditional)

    When responseType is not id_token, we have to provide client credential to redeem the authorization code. This credential could be client secret or client assertion. Non-B2C tenant supports both flows, but B2C tenant only supports client secret flow.

    For B2C tenant: clientSecret is required if the responseType is not 'id_token'.

    For non-B2C tenant: If responseType is not id_token, developer must provide either clientSecret, or thumbprint and privatePEMKey. We use clientSecret if it is provided; otherwise we use thumbprint and privatePEMKey for client assertion flow.

    clientSecret is the app key of your app in AAD. For B2C, the app key sometimes contains \, please replace \ with two \'s in the app key, otherwise \ will be treated as the beginning of an escaping character.

  • thumbprint (Conditional)

    Required if you want to use client assertion flow. thumbprint is the base64url format of the thumbprint (hash value) of the public key.

  • privatePEMKey (Conditional)

    Required if you want to use client assertion flow. privatePEMKey is the private pem key string.

  • isB2C (Conditional)

    Required to set to true if you are using B2C tenant.

  • validateIssuer (Conditional)

    Required to set to false if you don't want to validate issuer, default value is true. We validate the iss claim in id_token against user provided issuer values and the issuer value we get from tenant-specific endpoint. If you use common endpoint for identityMetadata and you want to validate issuer, then you have to either provide issuer, or provide the tenant for each login request using tenantIdOrName option in passport.authenticate (see section 5.1.3 for more details).

  • issuer (Conditional)

    This can be a string or an array of strings. See validateIssuer for the situation that requires issuer.

  • jweKeyStore (Conditional)

    This option is required if you want to accept and decrypt id_token in JWE Compact Serialization format. See section 5.1.1.4 for more details.

  • useCookieInsteadOfSession (Conditional)

    Passport-azure-ad saves state and nonce in session by default for validation purpose. If useCookieInsteadOfSession is set to true, passport-azure-ad will encrypt the state/nonce and put them into cookie instead. This is helpful when we want to be completely session-free, in other words, when you use { session: false } option in passport.authenticate function. If useCookieInsteadOfSession is set to true, you must provide cookieEncryptionKeys for cookie encryption and decryption.

  • cookieEncryptionKeys (Conditional)

    If useCookieInsteadOfSession is set to true, you must provide cookieEncryptionKeys. It is an array of the following format: [ {key: '…', 'iv': '…' }, {key: '…', 'iv': '…' }, …]. key could be any string of length 32, and iv could be any string of length 12. We always use the first set of key/iv to encrypt cookie, but we try all the key/iv to decrypt cookie. This is helpful if you want to do key rollover. The encryption/decryption algorithm we use is aes-256-gcm. You can limit the cookie amount and expiration using nonceLifetime and nonceMaxAmount options.

  • scope (Optional)

    List of scope values besides openid indicating the required scope of the access token for accessing the requested resource. For example, ['email', 'profile']. If you need refreshtoken for v2 endpoint, then you have to include the 'offlineaccess' scope.

  • loggingLevel (Optional)

    Logging level. 'info', 'warn' or 'error'.

  • loggingNoPII (Optional)

    If this is set to true, no personal information such as tokens and claims will be logged. The default value is true.

  • nonceLifetime (Optional)

    The lifetime of nonce in session in seconds. The default value is 3600 seconds.

  • nonceMaxAmount (Optional)

    The max amount of nonce you want to keep in session or cookies. The default number is 10. The oldest nonce(s) will be removed if the total number exceeds. (You can keep this number very small because nonce will be removed from session or cookies after validation. This mainly handles the case when user opens more than one login tabs at once and wants to go back to the first login page to type user credentials. Each login tab results in a nonce in session or cookie, so we only honor the most recent nonceMaxAmount many login tabs.)

  • clockSkew (Optional)

    This value is the clock skew (in seconds) allowed in token validation. It must be a positive integer. The default value is 300 seconds.

5.1.1.3 Verify callback

If you set passReqToCallback option to false, you can use one of the following signatures for the verify callback

  function(iss, sub, profile, jwtClaims, access_token, refresh_token, params, done)
  function(iss, sub, profile, access_token, refresh_token, params, done)
  function(iss, sub, profile, access_token, refresh_token, done)
  function(iss, sub, profile, done)
  function(iss, sub, done)
  function(profile, done)

If you set passReqToCallback option to true, you can use one of the following signatures for the verify callback

  function(req, iss, sub, profile, jwtClaims, access_token, refresh_token, params, done)
  function(req, iss, sub, profile, access_token, refresh_token, params, done)
  function(req, iss, sub, profile, access_token, refresh_token, done)
  function(req, iss, sub, profile, done)
  function(req, iss, sub, done)
  function(req, profile, done)

5.1.1.4 JWE support

We support encrypted id_token in JWE Compact Serialization format.

The key encryption algorithms supported are:

RSA1_5, RSA-OAEP, A128KW, A256KW, dir.

The content encryption algorithms supported are:

A128CBC-HS256, A192CBC-HS384, A256CBC-HS512, A128GCM, and A256GCM.

In order to decrypt the id_token, keys have to be provided in JWK format using jweKeyStore option. We will first try the key with the corresponding kid. If decryption fails, we will try every possible key in jweKeyStore. The following is an example of jweKeyStore:

    jweKeyStore: [ 
      { 'kid': 'sym_key_256', 'kty': 'oct', 'k': 'WIVds2iwJPwNhgUgwZXmn/46Ql1EkiL+M+QqDRdQURE=' }, 
      { 'kid': 'sym_key_128', 'kty': 'oct', 'k': 'GawgguFyGrWKav7AX4VKUg'}, 
      { 'kid': 'sym_key_384', 'kty': 'oct', 'k': 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v'},
      { 'kid': 'rsa_key', 
        'kty': 'RSA', 
        "n":"6-FrFkt_TByQ_L5d7or-9PVAowpswxUe3dJeYFTY0Lgq7zKI5OQ5RnSrI0\n\
             T9yrfnRzE9oOdd4zmVj9txVLI-yySvinAu3yQDQou2Ga42ML_-K4Jrd5cl\n\
             MUPRGMbXdV5Rl9zzB0s2JoZJedua5dwoQw0GkS5Z8YAXBEzULrup06fnB5\n\
             n6x5r2y1C_8Ebp5cyE4Bjs7W68rUlyIlx1lzYvakxSnhUxSsjx7u_mIdyw\n\
             yGfgiT3tw0FsWvki_KYurAPR1BSMXhCzzZTkMWKE8IaLkhauw5MdxojxyB\n\
             VuNY-J_elq-HgJ_dZK6g7vMNvXz2_vT-SykIkzwiD9eSI9UWfsjw",
        "e":"AQAB",
        "d":"C6EGZYf9U6RI5Z0BBoSlwy_gKumVqRx-dBMuAfPM6KVbwIUuSJKT3ExeL5\n\
             P0Ky1b4p-j2S3u7Afnvrrj4HgVLnC1ks6rEOc2ne5DYQq8szST9FMutyul\n\
             csNUKLOM5cVromALPz3PAqE2OCLChTiQZ5XZ0AiH-KcG-3hKMa-g1MVnGW\n\
             -SSmm27XQwRtUtFQFfxDuL0E0fyA9O9ZFBV5201ledBaLdDcPBF8cHC53G\n\
             m5G6FRX3QVpoewm3yGk28Wze_YvNl8U3hvbxei2Koc_b9wMbFxvHseLQrx\n\
             vFg_2byE2em8FrxJstxgN7qhMsYcAyw1qGJY-cYX-Ab_1bBCpdcQ",
        "p":"_avCCyuo7hHlqu9Ec6R47ub_Ul_zNiS-xvkkuYwW-4lNnI66A5zMm_BOQV\n\
             MnaCkBua1OmOgx7e63-jHFvG5lyrhyYEmkA2CS3kMCrI-dx0fvNMLEXInP\n\
             xd4np_7GUd1_XzPZEkPxBhqf09kqryHMj_uf7UtPcrJNvFY-GNrzlJk",
        "q":"7gvYRkpqM-SC883KImmy66eLiUrGE6G6_7Y8BS9oD4HhXcZ4rW6JJKuBzm\n\
             7FlnsVhVGro9M-QQ_GSLaDoxOPQfHQq62ERt-y_lCzSsMeWHbqOMci_pbt\n\
             vJknpMv4ifsQXKJ4Lnk_AlGr-5r5JR5rUHgPFzCk9dJt69ff3QhzG2c",
        "dp":"ErP3OpudePAY3uGFSoF16Sde69PnOra62jDEZGnPx_v3nPNpA5sr-tNc8\n\
              bQP074yQl5kzSFRjRlstyW0TpBVMP0ocbD8RsN4EKsgJ1jvaSIEoP87Ox\n\
              duGkim49wFA0Qxf_NyrcYUnz6XSidY3lC_pF4JDJXg5bP_x0MUkQCTtQE",
        "dq":"YbBsthPt15Pshb8rN8omyfy9D7-m4AGcKzqPERWuX8bORNyhQ5M8JtdXc\n\
              u8UmTez0j188cNMJgkiN07nYLIzNT3Wg822nhtJaoKVwZWnS2ipoFlgrB\n\
              gmQiKcGU43lfB5e3qVVYUebYY0zRGBM1Fzetd6Yertl5Ae2g2CakQAcPs",
        "qi":"lbljWyVY-DD_Zuii2ifAz0jrHTMvN-YS9l_zyYyA_Scnalw23fQf5WIcZ\n\
              ibxJJll5H0kNTIk8SCxyPzNShKGKjgpyZHsJBKgL3iAgmnwk6k8zrb_lq\n\
              a0sd1QWSB-Rqiw7AqVqvNUdnIqhm-v3R8tYrxzAqkUsGcFbQYj4M5_F_4"
      },
      { 'kid': 'ras_key_2',
        'kty': 'RSA',
        'privatePemKey': 
          '-----BEGIN RSA PRIVATE KEY-----\n\
          MIIEowIBAAKCAQEA6+FrFkt/TByQ/L5d7or+9PVAowpswxUe3dJeYFTY0Lgq7zKI\n\
          5OQ5RnSrI0T9yrfnRzE9oOdd4zmVj9txVLI+yySvinAu3yQDQou2Ga42ML/+K4Jr\n\
          d5clMUPRGMbXdV5Rl9zzB0s2JoZJedua5dwoQw0GkS5Z8YAXBEzULrup06fnB5n6\n\
          x5r2y1C/8Ebp5cyE4Bjs7W68rUlyIlx1lzYvakxSnhUxSsjx7u/mIdywyGfgiT3t\n\
          w0FsWvki/KYurAPR1BSMXhCzzZTkMWKE8IaLkhauw5MdxojxyBVuNY+J/elq+HgJ\n\
          /dZK6g7vMNvXz2/vT+SykIkzwiD9eSI9UWfsjwIDAQABAoIBAAuhBmWH/VOkSOWd\n\
          AQaEpcMv4CrplakcfnQTLgHzzOilW8CFLkiSk9xMXi+T9CstW+Kfo9kt7uwH5766\n\
          4+B4FS5wtZLOqxDnNp3uQ2EKvLM0k/RTLrcrpXLDVCizjOXFa6JgCz89zwKhNjgi\n\
          woU4kGeV2dAIh/inBvt4SjGvoNTFZxlvkkpptu10MEbVLRUBX8Q7i9BNH8gPTvWR\n\
          QVedtNZXnQWi3Q3DwRfHBwudxpuRuhUV90FaaHsJt8hpNvFs3v2LzZfFN4b28Xot\n\
          iqHP2/cDGxcbx7Hi0K8bxYP9m8hNnpvBa8SbLcYDe6oTLGHAMsNahiWPnGF/gG/9\n\
          WwQqXXECgYEA/avCCyuo7hHlqu9Ec6R47ub/Ul/zNiS+xvkkuYwW+4lNnI66A5zM\n\
          m/BOQVMnaCkBua1OmOgx7e63+jHFvG5lyrhyYEmkA2CS3kMCrI+dx0fvNMLEXInP\n\
          xd4np/7GUd1/XzPZEkPxBhqf09kqryHMj/uf7UtPcrJNvFY+GNrzlJkCgYEA7gvY\n\
          RkpqM+SC883KImmy66eLiUrGE6G6/7Y8BS9oD4HhXcZ4rW6JJKuBzm7FlnsVhVGr\n\
          o9M+QQ/GSLaDoxOPQfHQq62ERt+y/lCzSsMeWHbqOMci/pbtvJknpMv4ifsQXKJ4\n\
          Lnk/AlGr+5r5JR5rUHgPFzCk9dJt69ff3QhzG2cCgYASs/c6m5148Bje4YVKgXXp\n\
          J17r0+c6trraMMRkac/H+/ec82kDmyv601zxtA/TvjJCXmTNIVGNGWy3JbROkFUw\n\
          /ShxsPxGw3gQqyAnWO9pIgSg/zs7F24aSKbj3AUDRDF/83KtxhSfPpdKJ1jeUL+k\n\
          XgkMleDls//HQxSRAJO1AQKBgGGwbLYT7deT7IW/KzfKJsn8vQ+/puABnCs6jxEV\n\
          rl/GzkTcoUOTPCbXV3LvFJk3s9I9fPHDTCYJIjdO52CyMzU91oPNtp4bSWqClcGV\n\
          p0toqaBZYKwYJkIinBlON5XweXt6lVWFHm2GNM0RgTNRc3rXemHq7ZeQHtoNgmpE\n\
          AHD7AoGBAJW5Y1slWPgw/2bootonwM9I6x0zLzfmEvZf88mMgP0nJ2pcNt30H+Vi\n\
          HGYm8SSZZeR9JDUyJPEgscj8zUoShio4KcmR7CQSoC94gIJp8JOpPM62/5amtLHd\n\
          UFkgfkaosOwKlarzVHZyKoZvr90fLWK8cwKpFLBnBW0GI+DOfxf+\n\
          -----END RSA PRIVATE KEY-----\n';
      }
  ]

5.1.2 Use passport.authenticate to protect routes

To complete the sample, provide a route that corresponds to the path configuration parameter that is sent to the strategy:

app.get('/login', 
  passport.authenticate('azuread-openidconnect', { failureRedirect: '/' }),
  function(req, res) {
    log.info('Login was called in the Sample');
    res.redirect('/');
});

// POST /auth/openid/return
//   Use passport.authenticate() as route middleware to authenticate the
//   request.  If authentication fails, the user will be redirected back to the
//   home page.  Otherwise, the primary route function function will be called,
//   which, in this example, will redirect the user to the home page.
app.post('/auth/openid/return',
  passport.authenticate('azuread-openidconnect', { failureRedirect: '/' }),
  function(req, res) { 
    res.redirect('/');
  });

app.get('/logout', function(req, res){
  req.logout();
  res.redirect('/');
});

5.1.3 Options available for passport.authenticate

  • failureRedirect: the url redirected to when the authentication fails

  • session: if you don't want a persistent login session, you can use session: false. The default value is true.

  • customState: if you want to use a custom state value instead of a random generated one

  • resourceURL: if you need access_token for some resource. Note this option is only for v1 endpoint and code, code id_token, id_token code flow. For v2 endpoint, resource is considered as a scope, so it should be specified in the scope field when you create the strategy.

  • tenantIdOrName: if you want to specify the tenant to use for this request. You can use the tenant guid or tenant name (like 'contoso.onmicrosoft.com'). Note:

  • You must use common endpoint for identityMetadata, otherwise this option will be ignored. We will fetch and use the metadata from the tenant you specify for this request.

  • This option only applies to the login request, in other words, the request which is not supposed to contain code or idtoken. Passport saves the tenantIdOrName value in session before sending the authentication request. When we receive a request containing code or idtoken, we retrieve the saved tenantIdOrName value from session and use that value.

  • If you are using B2C common endpoint, then tenantIdOrName must be used for every login request.

  • domain_hint: if you want to specify the domain that the user should use to sign in. This option is not supported for B2C tenant.

  • login_hint: if you want to prefill the username with a given value in the login page. The value should be the upn of a user, not the email (most times they are the same though).

  • prompt: v1 and v2 endpoint support login, consent and admin_consent; B2C endpoint only supports login.

  • response: this is required if you want to use cookie instead of session to save state/nonce. See section 5.1.4.

Example:

  passport.authenticate('azuread-openidconnect', { failureRedirect: '/', session: false, customState: 'my_state', resourceURL: 'https://graph.microsoft.com/mail.send'});

  passport.authenticate('azuread-openidconnect', { tenantIdOrName: 'contoso.onmicrosoft.com' });

5.1.4 Session free support

Passport framework uses session to keep a persistent login session. As a plug in, we also use session to store state and nonce by default, regardless whether you use { session: false } option in passport.authenticate or not. To be completely session free, you must configure passport-azure-ad to create state/nonce cookie instead of saving them in session. Please follow the following example:

  passport.use(new OIDCStrategy({
    ...
    nonceLifetime: 600,  // state/nonce cookie expiration in seconds
    nonceMaxAmount: 5,   // max amount of state/nonce cookie you want to keep (cookie is deleted after validation so this can be very small)
    useCookieInsteadOfSession: true,  // use cookie, not session
    cookieEncryptionKeys: [ { key: '12345678901234567890123456789012', 'iv': '123456789012' }],  // encrypt/decrypt key and iv, see `cookieEncryptionKeys` instruction in section 5.1.1.2
  },
    // any supported verify callback
    function(iss, sub, profile, accessToken, refreshToken, done) {
    ...
  });

  app.get('/login', passport.authenticate('azuread-openidconnect', { session: false }));

5.2 BearerStrategy

5.2.1 Configure strategy and provide callback function

5.2.1.1 Sample using the BearerStrategy
// We pass these options in to the ODICBearerStrategy.

var options = {
  identityMetadata: config.creds.identityMetadata,
  clientID: config.creds.clientID,
  validateIssuer: config.creds.validateIssuer,
  issuer: config.creds.issuer,
  passReqToCallback: config.creds.passReqToCallback,
  isB2C: config.creds.isB2C,
  policyName: config.creds.policyName,
  allowMultiAudiencesInToken: config.creds.allowMultiAudiencesInToken,
  audience: config.creds.audience,
  loggingLevel: config.creds.loggingLevel,
  loggingNoPII: config.creds.loggingNoPII,
  clockSkew: config.creds.clockSkew,
  scope: config.creds.scope
};

var bearerStrategy = new BearerStrategy(options,
  function(token, done) {
    log.info('verifying the user');
    log.info(token, 'was the token retreived');
    findById(token.oid, function(err, user) {
      if (err) {
        return done(err);
      }
      if (!user) {
        // "Auto-registration"
        log.info('User was added automatically as they were new. Their oid is: ', token.oid);
        users.push(token);
        owner = token.oid;
        return done(null, token);
      }
      owner = token.oid;
      return done(null, user, token);
    });
  }
);
5.2.1.2 Options
  • identityMetadata (Required)

    The metadata endpoint provided by the Microsoft Identity Portal that provides the keys and other important information at runtime. Examples:

  • v1 tenant-specific endpoint

    https://login.microsoftonline.com/your_tenant_name.onmicrosoft.com/.well-known/openid-configuration
    https://login.microsoftonline.com/your_tenant_guid/.well-known/openid-configuration
  • v1 common endpoint
    https://login.microsoftonline.com/common/.well-known/openid-configuration
  • v2 tenant-specific endpoint
    https://login.microsoftonline.com/your_tenant_name.onmicrosoft.com/v2.0/.well-known/openid-configuration 
    https://login.microsoftonline.com/your_tenant_guid/v2.0/.well-known/openid-configuration
  • v2 common endpoint
    https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration

For B2C, you can only use v2 tenant-specific endpoint.

  • clientID (Required)

    The client ID of your application in AAD (Azure Active Directory)

  • passReqToCallback (Required)

    Whether you want to use req as the first parameter in the verify callback. See section 5.2.1.3 for more details.

  • isB2C (Conditional)

    Required to set to true if you are using B2C tenant.

  • policyName (Conditional)

    Required if you are using B2C tenant. It is a string starting with 'B2C1' (case insensitive).

  • validateIssuer (Conditional)

    Required to set to false if you don't want to validate issuer, default value is true. We validate the iss claim in id_token against user provided issuer values and the issuer value we get from tenant-specific endpoint. If you use common endpoint for identityMetadata and you want to validate issuer, then you must provide issuer, or provide tenantIdOrName in passport.authenticate.

  • issuer (Conditional)

    This can be a string or an array of strings. See validateIssuer for the situation that requires issuer.

  • allowMultiAudiencesInToken (Conditional)

    Required if you allow access_token whose aud claim contains multiple values.

  • scope (Optional)

    This value is an array of scopes you accept. If this value is provided, we will check if the token contains one of these accepted scopes. If this value is not provided, we won't check token scopes.

  • audience (Optional)

    Must be a string or an array of strings. We invalidate the aud claim in access_token against audience. The default value for audience is clientID.

  • loggingLevel (Optional)

    Logging level. 'info', 'warn' or 'error'.

  • loggingNoPII (Optional)

    If this is set to true, no personal information such as tokens and claims will be logged. The default value is true.

  • clockSkew (Optional)

    This value is the clock skew (in seconds) allowed in token validation. It must be a positive integer. The default value is 300 seconds.

5.2.1.3 Verify callback

If you set passReqToCallback option to false, you can use the following verify callback

  function(token, done)

If you set passReqToCallback option to true, you can use the following verify callback

  function(req, token, done)

5.2.2 Use passport.authenticate to protect resources or APIs

In the following example, we are using passport to protect '/api/tasks'. User sends a GET request to '/api/tasks' with accesstoken in authorization header or body. Passport validates the accesstoken, adds the related claims from access_token to req.user, and passes the request to listTasks middleware. The listTasks middleware can then read the user information in req.user and list all the tasks related to this user. Note that we do authentication every time, so we don't need to keep this user in session, and this can be achieved by using session: false option.

  server.get('/api/tasks', passport.authenticate('oauth-bearer', { session: false }), listTasks);

5.2.3 Options available for passport.authenticate

  • session: if you don't want a persistent login session, you can use session: false. The default value is true.

  • tenantIdOrName: if you use common endpoint, you can use this option to dynamically provide the tenant.

Example:

  passport.authenticate('oauth-bearer', { session: false });

6. Test

In the library root folder, type the following command to install the dependency packages:

    $ npm install

6.1. Run all tests except the end to end tests

Type the following command to run tests:

    $ npm test

6.2. Run all tests including the end to end tests

6.2.1. Create test applications

First you need to register one application in v1 tenant, one in v2 tenant and one in B2C tenant.

For the v2 application, you should register it at https://apps.dev.microsoft.com/ instead of Azure Portal.

For the B2C application, create policies named 'B2C1signin', 'B2C1signup'. For each policy, select 'Local Account' as the identity provider, and select the following:

  • 'B2C1signup':

  • Sign-up attributes: 'Display Name', 'Email Address', 'Given Name', 'Surname'

  • Application claims: 'Display Name', Email Addresses', 'Given Name', 'Identity Provider', 'Surname', 'Users Object ID'

  • 'B2C1signin':

  • Application claims: 'Display Name', Email Addresses', 'Given Name', 'Identity Provider', 'Surname', 'Users Object ID'

You will also need to click the 'Run now' button in the 'B2C1signup' blade to create an user.

For B2C application, you will also need to create at least one scope and provide it to test parameters. See how to create scope for B2C access token. In the bearerb2ctest, We will use OIDCStrategy to get a B2C access token for the scope, and use BearerStrategy to validate the scope. Note for scope we use the full url in b2c_params.scopeForOIDC but only the name in b2c_params.scopeForBearer. For example, b2c_params.scopeForOIDC=['https://sijun1b2c.onmicrosoft.com/oidc-b2c/read'] and b2c_params.scopeForBearer=['read'].

6.2.2. Fill the test parameters

Open test/End_to_end_test/script.js, set is_test_parameters_completed parameter to true. For test_parameters variable, fill in the tenant id/client id/client secret of your applications, and the username/password of your application user.

For thumbprint and privatePEMKey parameters, you need to specify a certificate for your app and register the public key in Azure Active Directory. thumbprint is the base64url format of the thumbprint of the public key, and privatePEMKey is the private pem key string. For a v1 tenant, you can follow this post to generate a certificate and register the public key. For a v2 tenant, you can go to your application page in the v2 portal and click Generate New Key Pair. A certificate will be generated for you to download. The corresponding public key is automatically registered in this case.

6.2.3. Run the tests

Type the following commands to run the tests:

    $ cd test/End_to_end_test
    $ npm install
    $ npm install grunt -g
    $ grunt run_tests_with_e2e

Tests will run automatically and in the terminal you can see how many tests are passing/failing. More details can be found here.

7. Logging

Personal Identifiable Information (PII) & Organizational Identifiable Information (OII)

By default, passport-azure-ad logging does not capture or log any PII or OII. The library allows app developers to turn this on by configuring loggingNoPII in the config options. By turning on PII or OII, the app takes responsibility for safely handling highly-sensitive data and complying with any regulatory requirements.

//PII or OII logging disabled. Default Logger does not capture any PII or OII.
var options = {
  ...
  loggingNoPII: true,
  ...
};

//PII or OII logging enabled
var options = {
  ...
  loggingNoPII: false,
  ...
};

8. Samples and Documentation

We provide a full suite of sample applications and documentation on GitHub to help you get started with learning the Azure Identity system. This includes tutorials for native clients such as Windows, Windows Phone, iOS, OSX, Android, and Linux. We also provide full walkthroughs for authentication flows such as OAuth2, OpenID Connect, Graph API, and other awesome features.

Azure Identity samples for this plug-in can be found in the following links:

8.1 Samples for OpenID connect strategy

8.2 Samples for Bearer strategy

9. Community Help and Support

We leverage Stack Overflow to work with the community on supporting Azure Active Directory and its SDKs, including this one. We highly recommend you ask your questions on Stack Overflow (we're all on there!) Also browser existing issues to see if someone has had your question before.

We recommend you use the "adal" tag so we can see it! Here is the latest Q&A on Stack Overflow for ADAL: http://stackoverflow.com/questions/tagged/adal

10. Security Reporting

If you find a security issue with our libraries or services please report it to secure@microsoft.com with as much detail as possible. Your submission may be eligible for a bounty through the Microsoft Bounty program. Please do not post security issues to GitHub Issues or any other public site. We will contact you shortly upon receiving the information. We encourage you to get notifications of when security incidents occur by visiting this page and subscribing to Security Advisory Alerts.

11. Contributing

All code is licensed under the MIT license and we triage actively on GitHub. We enthusiastically welcome contributions and feedback. You can clone the repo and start contributing now.

More details about contribution

12. Releases

Please check the releases for updates.

13. Acknowledgements

The code is based on Henri Bergius's passport-saml library and Matias Woloski's passport-wsfed-saml2 library as well as Kiyofumi Kondoh's passport-openid-google.

14. License

Copyright (c) Microsoft Corp. All rights reserved. Licensed under the MIT License;

15. Microsoft Open Source Code of Conduct

We Value and Adhere to the Microsoft Open Source Code of Conduct

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

更多

友情链接

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文