Using RemoteAuthenticatorView OnLogInSucceeded gives a second AzureAD login dialog box

若如初见. 提交于 2021-01-27 18:16:18

问题


Having trouble to understand what going on and I really could use some thoughts and inputs. Please, I am not very good at describing in text what is the problem, so be gentle.

I have a Blazor wasm frontend and a Azure Function backend.
I do a standard, nothing special, Azure AD login. Later in code, I connect with the AzFunction using the bearer token, and it works. The AzFunction evaluates the token and identifies the user for further processing. I should mention that the AzFunction connects to Graph and other APIs.

So everything is fine and working, here is my problem.
I want to connect to the AzFuntion at login time, to get some user profile information. For debugging I simply made a button, and again, everything works.

Then I moved "the button code" to a method called by RemoteAuthenticatorView OnLogInSucceeded, in Authenticator.razor.

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

Here is my problem: Using the OnLogInSucceeded I get 2 Azure AD logins that I have to respond to. After the first one, It goes directly to the second. All simply because I moved the code from a button to OnLogInSucceeded. When I debug, I clearly see that the token is present before it connects to AzFunction.

Also, when I set a breakpoint in visual studio at the LoginComplete function called from OnLogInSucceeded, and just holds it for a few seconds, it goes throught the login process with just one login dialog.

Could anyone please help me understand why?

Any pointers on a better please to put the "get User profile" code? What I need is the code to run, so when the login is completed, the user profile info is retrived, with just one login.

Edit:
The code seem to have a better solution, however I still do not understand what made the second login to apper... And that was kind of the main reson for my question.


回答1:


Thanks to Zack Blazor Webassembly Authenticated Event, I was sent in a good direction and read Microsoft pages on AuthenticationStateChanged Event. It also simplified my code since I now have the retrival of UserProfile, in the UserProfile component than Authentication.razor.

@inject AuthenticationStateProvider AuthenticationStateProvider

private void OnAuthenticationStateChanged(Task<AuthenticationState> task)
{
    if(task.IsCompletedSuccessfully)
    {
        GetUserProfile();
    }
}

Not sure if this is "the final code", but it sure works and seems to me like a plausable way to go.




回答2:


Well thanks to Raymond, I was also sent i a good direction, and made a component to load this type of data. I had to combine the event and loading when the component initializes. I still feel like M$ should and probobly will find a better way to do this.

For this application I use Azure B2C to auth, and I'm using redirect instead of popup. When I initialize the userstate, I call my api which gets the user profile, or creates one if it's a new user.

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>

When the user bounces back from the gateway, the component initializes before the application authenticates the user. Then the event triggers twice, once for the authentication page, and once for whatever page your are viewing.



来源:https://stackoverflow.com/questions/64617031/using-remoteauthenticatorview-onloginsucceeded-gives-a-second-azuread-login-dial

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!