在 Azure AD 中检索交互式身份验证的 2 个访问令牌

Retrieve 2 Access Tokens on Interactive Authentication in Azure AD

开发信息:Azure 注册移动应用程序,Azure 托管 API,Visual Studio 2019,移动应用程序构建在面向 .Net-5 的 Xamarin Forms 中API 内置于 Asp.Net 目标 .Net-5。

所需的工作流程:如果 AcquireTokenSilently 失败,用户使用 MS Authenticator 进行授权代码流身份验证交互。一旦通过身份验证,用户将获得 Graph 和自定义托管 API.

的访问令牌

工作原理:我可以使用 MSAL 库执行 MFA 身份验证,并且在移动应用程序中收到访问令牌,但该令牌仅适用于拨打电话到图表。我还需要能够调用受保护的资源(托管 API)。我最初的想法是 JWT 访问令牌可以与托管 API 以及 Graph 一起使用,但在阅读更多内容后,我了解到您必须获取两个单独的令牌以防止两个资源可能具有的问题相同的范围。

我面临的问题是,我认为在进行身份验证时,将有一种简单的方法来获取另一个资源访问令牌,而无需进行第二次身份验证。这不是 OpenID 身份验证的重点吗?我最后的想法是使用客户端 ID、客户端密码从移动应用程序到受保护的 API 进行隐式身份验证,但我不想在移动应用程序中存储客户端密码,因为担心它可能会暴露一些东西对用户敏感。我尝试按照 Microsoft 的文档并为托管 API 设置特定范围并将其注册在 Azure 门户中,但这似乎对身份验证部分没有任何影响。我仍然需要根据 API 进行身份验证,这不是想要的结果。

是否可以对 Graph 进行一次身份验证,然后知道用户已正确进行身份验证,因为他们现在拥有 Graph 的有效令牌,然后以某种方式调用此受保护 API 而不必强制他们再次进行身份验证受保护的 API 如果是这样,如何在不暴露移动应用程序中的任何敏感信息(客户端机密)的情况下做到这一点?

代码

  // Microsoft Authentication client for native/mobile apps
  public static IPublicClientApplication PCA;
  
  //Setting up the PublicClientApplicationBuilder
  //OAuthSettings is a static class with required values (app id, tenant id, etc)
  var builder = PublicClientApplicationBuilder
                            .Create(OAuthSettings.ApplicationId)
                            .WithTenantId(OAuthSettings.TenantId)
                            .WithBroker()
                            .WithRedirectUri(OAuthSettings.RedirectUri);

  PCA = builder.Build();

  //Scopes being used in initial authentication
  //I tried adding to this with a custom scope, I added on the 
  //protected api and it caused an exception, so I didn't think
  //I could use those together with the scopes for Graph
  //Custom scope for the protected API is as follows:
  //XXX.User.Common (where XXX is our company name)
  public const string Scopes = "User.Read MailboxSettings.Read Calendars.ReadWrite";
  
  try
  {
     var accounts = await PCA.GetAccountsAsync();

     var silentAuthResult = await PCA
                    .AcquireTokenSilent(Scopes.Split(' '), accounts.FirstOrDefault())
                    .ExecuteAsync();

                
   }
   catch (MsalUiRequiredException msalEx)
   {
               
       var windowLocatorService = DependencyService.Get<IParentWindowLocatorService>();

       // Prompt the user to sign-in
       var interactiveRequest = PCA.AcquireTokenInteractive(Scopes);
                

       AuthUIParent = windowLocatorService?.GetCurrentParentWindow();

       if (AuthUIParent != null)
       {
          interactiveRequest = interactiveRequest
                        .WithParentActivityOrWindow(AuthUIParent);
       }

       var interactiveAuthResult = await interactiveRequest.ExecuteAsync();
       var accounts = await PCA.GetAccountsAsync();
       
       //at this point, I have a valid Graph token, but I need to
       //get a valid token for the protected API, 
       //unsure of how to do this last piece 
    }
           

这个答案大部分要感谢 Gaurav Mantri,但他太谦虚了,无法接受荣誉,他让我 post 回答。

我需要更改的第一件事是身份验证流程。

根据受保护的 API 对用户进行身份验证,然后获取 Graph 令牌,因为 Graph 身份验证与 MSAL 库的 AcquireTokenSilent(IEnumerable<string> scopes, IAccount account) 方法配合得很好。我通过对 Graph 进行身份验证然后尝试获取受保护的 API 令牌来向后执行此操作。

因此,验证代码如下所示:

 public async Task SignIn()
 {
     try
     {
         //check for any accounts which are authenticated
         var accounts = await PCA.GetAccountsAsync();

         //try to Authenticate against the protected API silently     
         var silentAuthResult = await PCA
                    .AcquireTokenSilent(new string[] { "api://{your api client id}/.default" }, accounts.FirstOrDefault())
                    .ExecuteAsync();

                
     }
     catch (MsalUiRequiredException msalEx)
     {
        // This exception is thrown when an interactive sign-in is required.
        var windowLocatorService = DependencyService.Get<IParentWindowLocatorService>();

        // Prompt the user to sign-in
        var interactiveRequest = PCA.AcquireTokenInteractive(new string[] { "api://{your api client id}/.default" });


        AuthUIParent = windowLocatorService?.GetCurrentParentWindow();

        if (AuthUIParent != null)
        {
             interactiveRequest = interactiveRequest
                        .WithParentActivityOrWindow(AuthUIParent);
        }

        var interactiveAuthResult = await interactiveRequest.ExecuteAsync();
        var accounts = await PCA.GetAccountsAsync();
        
        //Now we can get the Graph token silently
        //We now have valid tokens for both Graph and our protected API        
        var graphtokenresult = await PCA.AcquireTokenSilent(Scopes, accounts.First()).ExecuteAsync();
                
      }
      catch (Exception ex)
      {
                
      }               
  }
 

在 Azure 门户中,我需要确保在配置中设置了一些内容。

  1. 确保为受保护的 API - quickstart-configure-app-expose-web-apis

    设置一些自定义范围
  2. (这个在 MSDN 文档中没有记载)

    在同一页面上,您可以为受保护的 API 添加范围,其中有一个名为 添加客户端应用程序 的部分,您可以点击添加,然后选择您要授予访问权限的应用程序 API 就足够了,但事实并非如此。

您还需要进入受保护的 API 的清单并添加您要手动授予访问权限的应用程序的客户端 ID,因为单击添加按钮并选择客户端应用程序不会修改显现。因此,打开受保护 API 的清单并将客户端应用程序 ID 添加到标记为 knownClientApplications:

的清单部分

完成所有这些操作后,您现在可以收到一个访问令牌,该令牌将针对受保护的 API 进行授权,以及一个用于获取用户信息的图形令牌。我希望这对您有所帮助,再次感谢 Gaurav Mantri。如果有人对此有更多疑问,请联系我,我会尽我所能传授我所学到的。