使用 Azure AD、ReactJS 和 NodeJS 验证用户并使用 Graph API

Posted

技术标签:

【中文标题】使用 Azure AD、ReactJS 和 NodeJS 验证用户并使用 Graph API【英文标题】:Authenticate Users and use Graph API using Azure AD, ReactJS and NodeJS 【发布时间】:2017-04-11 16:03:15 【问题描述】:

我有一个使用 NodeJS + ReactJS 创建的项目应用程序,我想要的只是使用 Azure AD 对用户进行身份验证,并使用 Azure 的 Graph API 获取他的数据,如姓名、组、图片、职业等。

我已经在 Azure 门户上正确配置了 Azure AD 和应用程序。包括权限委派和所有这些人员。

我试图了解如何做到这一点的最佳方法,但没有成功。我一直在尝试在 google、***、Microsoft 文档,甚至项目示例上找到。

一些示例已经奏效,但我无法理解其中任何一个示例并将其放入我的项目中以用作生产应用程序。

我已经用这个来验证用户,但是返回的AccessToken对于调用Graph API是无效的:

passport.use(new OIDCStrategy(
    redirectUrl: config.creds.returnURL,
    realm: config.creds.realm,
    clientID: config.creds.clientID,
    clientSecret: config.creds.clientSecret,
    oidcIssuer: config.creds.issuer,
    identityMetadata: config.creds.identityMetadata,
    skipUserProfile: config.creds.skipUserProfile,
    responseType: config.creds.responseType,
    responseMode: config.creds.responseMode,
    allowHttpForRedirectUrl: config.creds.allowHttpForRedirectUrl
  ,
  function(iss, sub, profile, accessToken, refreshToken, done) 
    console.log(accessToken);
    profile = profile._json;
    if (!profile.email) 
      return done(new Error("No email found"), null);
    
    // asynchronous verification, for effect...
    process.nextTick(function () 
      findByEmail(profile.email, function(err, user) 
        if (err) 
          return done(err);
        
        if (!user) 
          // "Auto-registration"
          users.push(profile);
          return done(null, profile);
        
        return done(null, user);
      );
    );
  
));

这个给了我正确的 AccessToken 来使用 Graph API,但我不明白如何使用它而不是 Passport:

function createAuthorizationUrl(state) 
  var authorizationUrl = templateAuthzUrl.replace('<client_id>', sampleParameters.clientId);
  authorizationUrl = authorizationUrl.replace('<redirect_uri>',redirectUri);
  authorizationUrl = authorizationUrl.replace('<state>', state);
  authorizationUrl = authorizationUrl.replace('<resource>', resource);
  return authorizationUrl;


// Clients get redirected here in order to create an OAuth authorize url and redirect them to AAD.
// There they will authenticate and give their consent to allow this app access to
// some resource they own.
app.get('/auth', function(req, res) 
  crypto.randomBytes(48, function(ex, buf) 
    var token = buf.toString('base64').replace(/\//g,'_').replace(/\+/g,'-');

    res.cookie('authstate', token);
    var authorizationUrl = createAuthorizationUrl(token);

    res.redirect(authorizationUrl);
  );
);

// After consent is granted AAD redirects here.  The ADAL library is invoked via the
// AuthenticationContext and retrieves an access token that can be used to access the
// user owned resource.
app.get('/getAToken', function(req, res) 
  if (req.cookies.authstate !== req.query.state) 
    res.send('error: state does not match');
  
  var authenticationContext = new AuthenticationContext(authorityUrl);
  authenticationContext.acquireTokenWithAuthorizationCode(req.query.code, redirectUri, resource, sampleParameters.clientId, sampleParameters.clientSecret, function(err, response) 
    var message = '';
    if (err) 
      message = 'error: ' + err.message + '\n';
    
    message += 'response: ' + JSON.stringify(response);

    if (err) 
      res.send(message);
      return;
    

    // Later, if the access token is expired it can be refreshed.
    authenticationContext.acquireTokenWithRefreshToken(response.refreshToken, sampleParameters.clientId, sampleParameters.clientSecret, resource, function(refreshErr, refreshResponse) 
      if (refreshErr) 
        message += 'refreshError: ' + refreshErr.message + '\n';
      
      message += 'refreshResponse: ' + JSON.stringify(refreshResponse);

      res.send(message); 
    ); 
  );
);

如果有人可以帮助我提供一些真实的应用示例、视频或其他内容,那就太好了。我正在失去理智去弄清楚。

谢谢!

【问题讨论】:

【参考方案1】:

护照的OIDCStrategy 跟随Authorize access to web applications using OpenID Connect and Azure Active Directory 对用户进行身份验证和授权。而如果需要资源的访问权限,还需要完成Use the authorization code to request an access token的步骤。

我根据openid connection sample添加了一些代码sn-ps:

返回路由函数将使用code获取访问令牌并保存到用户视觉对象中。

app.post('/auth/openid/return',
  passport.authenticate('azuread-openidconnect',  failureRedirect: '/login' ),
  function (req, res) 
    let option = 
      method:'POST',
      uri:'https://login.microsoftonline.com/<tenant_id>/oauth2/token',
      headers:
        'Content-Type':'application/x-www-form-urlencoded'
      ,
      form:
        grant_type:'authorization_code',
        client_id:'<client_id>',
        resource:'https://graph.windows.net',
        client_secret:'<secret_key>',
        code:req.body.code,
        redirect_uri:'http://localhost:3000/auth/openid/return'
      
    
    console.log(option);
    request(option,function(err,res,body)
      req.user.access_token = JSON.parse(body).access_token;
    )

    // log.info('We received a return from AzureAD.');
    res.redirect('/');
  );

路由'/me'是为了采样使用访问令牌来要求资源。

app.get('/me',ensureAuthenticated ,function(req,response)
  request.get("https://graph.windows.net/<tenant_id>/me?api-version=1.5", 
    'headers': 
      'Authorization': "Bearer " + req.user.access_token,
      'Content-Type': 'application/json'
    
  , function(err, res, body)
    if(err)
      console.log("err: " + err);
    
    else
      console.log("res: " + res);
      response.send(res);
    
  );
);

如有任何疑问,请随时告诉我。

【讨论】:

嗨,加里!感谢您的回复...在看到您的答案之前,我使用 adal-node 而不是 Passport 使用此方法得到了结果:authenticationContext.acquireTokenWithAuthorizationCode() 但是现在,我在 Azure 上配置了另一个 WebAPI,我仍然有问题要调用此 API 的方法。我更改了 adal-node 方法的资源参数,并获得了一个有效的令牌来调用我的 WebAPI,但现在图表无法使用此令牌。 我的问题是:我是否需要获得 2 个不同的令牌才能使用我的 WebAPI 和 Graph API?如果我使用“acquireTokenWithAuthorizationCode”对用户进行身份验证并调用 Graph 并使用“acquireTokenWithClientCredentials”来要求使用令牌调用 WebAPI 是否可以? 在这些示例github.com/AzureAD/… 中为不同资源更改resource 参数。例如。使用resource = "https://graph.windows.net" 获取 Azure AD 图形资源的访问令牌。 完美!但是,这是一个好习惯吗?比如:使用 adal 对用户进行身份验证,然后创建一种方法来获取不同资源的 accessTokens?我创建了一个方法,它接收资源作为参数并调用 adal 方法以从 azure 获取令牌,现在它工作得很好。但我真的不知道这是否是一个好习惯。你怎么看? 出于安全考虑,它是由 OAuth 2.0 设计的。

以上是关于使用 Azure AD、ReactJS 和 NodeJS 验证用户并使用 Graph API的主要内容,如果未能解决你的问题,请参考以下文章

ReactJS 与 azure SAML SSO 的集成

为啥我的 ReactJS 应用程序在本地运行但不在 Azure 中运行?

使用 Passport 和 passport-azure-ad 时没有刷新令牌

Azure AD 与 Azure AD B2C 与 Azure AD B2B

使用 KeyVault 和 Azure PowerShell 证书身份验证创建 Azure AD 应用程序

Azure AD和Windows AD功能对比