首页 > 解决方案 > 使用 RemoteAuthenticatorView OnLogInSucceeded 提供第二个 AzureAD 登录对话框

问题描述

很难理解发生了什么,我真的可以使用一些想法和投入。拜托,我不太擅长用文字描述问题是什么,所以要温柔。

我有一个 Blazor wasm 前端和一个 Azure Function 后端。
我做了一个标准的、没什么特别的 Azure AD 登录。稍后在代码中,我使用不记名令牌与 AzFunction 连接,它可以工作。AzFunction 评估令牌并识别用户以进行进一步处理。我应该提到,AzFunction 连接到 Graph 和其他 API。

所以一切都很好,工作正常,这是我的问题。
我想在登录时连接到 AzFuntion,以获取一些用户配置文件信息。为了调试,我只是做了一个按钮,再次,一切正常。

然后,我将“按钮代码”移动到 Authenticator.razor 中由 RemoteAuthenticatorView OnLogInSucceeded 调用的方法。

<RemoteAuthenticatorView Action="@Action" LogInFailed="LogInFailedFragment" OnLogInSucceeded="LoginComplete" />

这是我的问题: 使用 OnLogInSucceeded 我获得了 2 个必须响应的 Azure AD 登录。在第一个之后,它直接进入第二个。这一切都只是因为我将代码从一个按钮移到了 OnLogInSucceeded。调试时,我清楚地看到令牌在连接到 AzFunction 之前就已存在。

此外,当我在 Visual Studio 中的从 OnLogInSucceeded 调用的 LoginComplete 函数处设置断点并保持几秒钟时,它会通过一个登录对话框完成登录过程。

谁能帮我理解为什么?

关于更好的“获取用户配置文件”代码的任何指示?我需要的是要运行的代码,所以当登录完成后,只需一次登录即可检索用户配置文件信息。

编辑:
代码似乎有一个更好的解决方案,但是我仍然不明白是什么让第二次登录出现......这就是我的问题的主要原因。

标签: blazorblazor-webassembly

解决方案


多亏了 Raymond,我也得到了很好的指导,并制作了一个组件来加载此类数据。我必须在组件初始化时结合事件和加载。我仍然觉得 M$ 应该并且很可能会找到更好的方法来做到这一点。

对于这个应用程序,我使用 Azure B2C 进行身份验证,并且我使用的是重定向而不是弹出窗口。当我初始化用户状态时,我调用我的 api 来获取用户配置文件,或者如果它是新用户则创建一个。

InitialDataLoader.razor

@implements IDisposable
@inject NavigationManager _navigationManager
@inject UserState _userState
@inject AuthenticationStateProvider _authenticationStateProvider

@ChildContent

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }

    // The event can trigger multiple times when being redirected from the Authentication page to wherever you're going.
    // So we use this to check.
    public bool hasRunInit;

    protected override async Task OnInitializedAsync()
    {
        Console.WriteLine(DateTime.Now.ToString("hh:mm:ss:fff") + " InitialDataLoader -> OnInitializedAsync -> " + _navigationManager.Uri);

        // This component is already loaded and initialized on the Authentication page, so we have to subscribe to this event to
        // check when the user bounces back from Azure B2C and gets logged in. In that case, we do the initial load in on the event
        _authenticationStateProvider.AuthenticationStateChanged += this.OnAuthenticationStateChanged;


        // If the user is authenticated and reloads the page in the browser, the event won't trigger so we can do the initial load here.
        var user = (await _authenticationStateProvider.GetAuthenticationStateAsync()).User;
        Console.WriteLine(DateTime.Now.ToString("hh:mm:ss:fff") + " InitialDataLoader -> OnInitializedAsync -> IsUserAuthenticated: " + user.Identity.IsAuthenticated);
        Console.WriteLine(DateTime.Now.ToString("hh:mm:ss:fff") + " InitialDataLoader -> OnInitializedAsync -> IsStateInitialized: " + _userState.IsInitialized);

        if (user.Identity.IsAuthenticated && !_userState.IsInitialized)
        {
            hasRunInit = true;
            Console.WriteLine(DateTime.Now.ToString("hh:mm:ss:fff") + " InitialDataLoader -> OnInitializedAsync -> InitUser");
            await _userState.InitializeAsync(this);
        }
    }

    void IDisposable.Dispose()
    {
        _authenticationStateProvider.AuthenticationStateChanged -= this.OnAuthenticationStateChanged;
    }

    private async void OnAuthenticationStateChanged(Task<AuthenticationState> task)
    {
        Console.WriteLine(DateTime.Now.ToString("hh:mm:ss:fff") + " InitialDataLoader -> OnAuthenticationStateChanged -> " + _navigationManager.Uri);

        var user = (await task).User;
        Console.WriteLine(DateTime.Now.ToString("hh:mm:ss:fff") + " InitialDataLoader -> OnAuthenticationStateChanged -> IsUserAuthenticated: " + user.Identity.IsAuthenticated);
        Console.WriteLine(DateTime.Now.ToString("hh:mm:ss:fff") + " InitialDataLoader -> OnAuthenticationStateChanged -> IsStateInitialized: " + _userState.IsInitialized);

        if (user.Identity.IsAuthenticated && !_userState.IsInitialized)
        {
            if (!hasRunInit)
            {
                hasRunInit = true;

                Console.WriteLine(DateTime.Now.ToString("hh:mm:ss:fff") + " CascadingAppStateProvider -> OnInitializedAsync -> InitUser");
                await _userState.InitializeAsync(this);
            }
            else
            {
                Console.WriteLine(DateTime.Now.ToString("hh:mm:ss:fff") + " InitialDataLoader -> OnInitializedAsync -> Init has already been triggered!");
            }
        }
    }
}

App.razor

<CascadingAuthenticationState>

    <InitialDataLoader>

        <Router AppAssembly="@typeof(Program).Assembly">
            <Found Context="routeData">
                <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
                    <Authorizing>
                        ------------- Authorizing.. ------------------
                    </Authorizing>
                    <NotAuthorized>
                        @if (!context.User.Identity.IsAuthenticated)
                        {
                            <RedirectToLogin />
                        }
                        else
                        {
                            <p>You are not authorized to access this resource.</p>
                        }
                    </NotAuthorized>
                </AuthorizeRouteView>
            </Found>
            <NotFound>
                <LayoutView Layout="@typeof(MainLayout)">
                    <p>Sorry, there's nothing at this address.</p>
                </LayoutView>
            </NotFound>
        </Router>

    </InitialDataLoader>

</CascadingAuthenticationState>

当用户从网关返回时,组件会在应用程序对用户进行身份验证之前进行初始化。然后该事件触发两次,一次用于身份验证页面,一次用于您正在查看的任何页面。


推荐阅读