【问题标题】:Handling MSAL Exceptions in Blazor Server App处理 Blazor 服务器应用程序中的 MSAL 异常
【发布时间】:2022-12-17 04:58:36
【问题描述】:

我正在编写一个 Blazor Server 应用程序,该应用程序使用 Microsoft Identity Platform 和 MSAL 根据 Azure Active Directory 对用户进行身份验证。 我正在使用 .net 6

我正在尝试从 Microsoft Graph API 获取用户配置文件。我有一个大部分时间都可以使用的功能。但是,当 MSAL 想要再次显示 UI 时(例如,缓存令牌已过期、不可用或没有范围),一切都崩溃了! :-(

记录在案的机制是 API 调用抛出 MsalUiRequiredException 并且调用代码应捕获此异常并将其委托给 ConsentHandler.HandleException()

问题不在于没有文档向您展示如何从那里进行恢复。就我而言,我尝试再次调用图形 API,但仍然遇到相同的异常。

这是我的方法:

   private async Task<User> GetUserProfile()
    {
        try
        {
            return await GraphServiceClient.Me.Request().GetAsync();
        }
        catch (Exception ex)
        {
            ConsentHandler.HandleException(ex);

            //What now?? - I still need to return a user!
            
            //Try calling the graph service again?
            return await GraphServiceClient.Me.Request().GetAsync();  //throws same exception again!
        }
    }

我得到的例外是

Microsoft.Graph.ServiceException: Code: generalException
Message: An error occurred sending the request.

 ---> Microsoft.Identity.Web.MicrosoftIdentityWebChallengeUserException: IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent. 
 ---> MSAL.NetCore.4.42.0.0.MsalUiRequiredException: 
    ErrorCode: user_null
Microsoft.Identity.Client.MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call. 
   at Microsoft.Identity.Client.Internal.Requests.Silent.SilentRequest.ExecuteAsync(CancellationToken cancellationToken)

...

错误消息中的链接解释了我使用的模式,但该示例并未继续完成其 API 调用。

如果用户刷新浏览器几次,问题就会消失(不会奇怪地显示用户界面),直到下次令牌过期或我重新启动服务。

问题是:catch 块应该是什么样的?

更新

从 kavya 的回答中得出的结论是,您可以让异常冒泡到顶层,并在调用 ConsentHandler.HandleException(e) 后有效地放弃请求这允许平台重定向浏览器以收集同意,然后再次重定向回来重新启动原始请求。在 blazor 服务器中,我的代码如下所示:

   //top level method in this blazor request
    protected override async Task OnInitializedAsync()
    {
        try
        {
            //really function that eventually calls GetUserProfile.
            var user = await GetUserProfile();
        }
        catch (Exception e)
        {
            ConsentHandler.HandleException(e);
            throw; //exits OnInitializedAsync and allows the platform to take over and redirect.
        }
        
        //snip rest of this function
    }

    private async Task<User> GetUserProfile()
    {
            // no handling here let the exception bubble to the top of the stack 
            return await GraphServiceClient.Me.Request().GetAsync();

    }

...

【问题讨论】:

    标签: c# azure-active-directory blazor blazor-server-side msal


    【解决方案1】:

    我试图在我的环境中重现该场景:

    我调用了以下控制器方法:

    我的控制器.cs

    public async Task<IActionResult> Index()
            {
                var user = await _graphServiceClient.Me.Request().GetAsync();
                ViewData["ApiResult"] = user.DisplayName;
    
                return View();
            }
    

    收到同样的错误:

    MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call.
    Microsoft.Identity.Client.Internal.Requests.Silent.SilentRequest.ExecuteAsync(CancellationToken cancellationToken)
    MicrosoftIdentityWebChallengeUserException: IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent.
    

    • 原因是在使用 Azure Active Directory 使用 msal 时未提及控制器方法上的属性 [Authorize] 进行授权:

    • 确认并包括您在授权期间需要的范围以及 azure 广告门户中给出的范围。在提及范围后[AuthorizeForScopes]属性:

      [AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")]
                public async Task<IActionResult> Index()
                {
                    var user = await _graphServiceClient.Me.Request().GetAsync();
                    ViewData["ApiResult"] = user.DisplayName;
      
                    return View();
                }
      

    • 此过滤器识别用户何时未成功验证并重定向以再次登录。

    并允许MicrosoftIdentityWebChallengeUserException在调用用户时处理 try-catch 块中的异常。

    异常处理:

    try
    {
        
        // ITokenAcquisition.GetAccessTokenForUserAsync(...)
     //  return await GraphServiceClient.Me.Request().GetAsync();
         await GraphServiceClient.Me.Request().GetAsync();
    }
    catch (MicrosoftIdentityWebChallengeUserException)
    {
        
    //MicrosoftIdentityWebChallengeUserException  should  bed re-throwed , so 
        // as to be caught [AuthorizeForScopes] exception handling attribute 
        // added to app Controller class.
        throw;
    }
    catch (Exception ex)
    {
        exceptionMessage = ex.Message;
    }
    
    • 我在应用程序设置中声明了图形范围。

    应用设置.json

      {
        "AzureAd": {
          "Instance": "https://login.microsoftonline.com/",
          "Domain": "testxxxx.onmicrosoft.com",
          "ClientId": "xxxx",
          "TenantId": "xxx",
          "ClientSecret": "xxxx",
          "ClientCertificates": [
          ],
          "CallbackPath": "/signin-oidc"
        },
        "DownstreamApi": {
          "BaseUrl": "https://graph.microsoft.com/v1.0",
          "Scopes": "https://graph.microsoft.com/.default"
        }
    

    确保提供获取用户信息所需的api permissions。 IE;User.Read并且必须通过门户或在身份验证期间授予管理员同意。

    然后调用我的 API 成功:

    接口:

    您可以检查此c# - MsalUiRequiredException when calling Microsoft Graph SDK from NET Core web app - Stack Overflow以使用异常处理程序中间件

    【讨论】:

    • 嗨@Kavya,感谢您提供如此全面的答案!您建议的其中一件事是将异常处理提升到一个更高的级别,我已经这样做了,它似乎修复了应用程序,但“无帐户或登录提示”仍然被隐藏在幕后。我在 application.config 中也有图形范围,但作为一个 Blazor 应用程序,没有控制器——将 [authorize] 添加到 OnInitializedAsync() 似乎没有帮助。你给了我很多东西让我看看我会做什么并回复你。
    猜你喜欢
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 2019-10-09
    • 2012-01-23
    • 1970-01-01
    • 2020-08-29
    • 2022-11-29
    • 2010-12-13
    相关资源
    最近更新 更多