Skip to content

Conversation

@juho-hanhimaki
Copy link
Contributor

Component render would occur between lines 95 and 96 if the AuthenticationState task had already been completed before it was awaited on line 95.

protected override async Task OnParametersSetAsync()
{
// We allow 'ChildContent' for convenience in basic cases, and 'Authorized' for symmetry
// with 'NotAuthorized' in other cases. Besides naming, they are equivalent. To avoid
// confusion, explicitly prevent the case where both are supplied.
if (ChildContent != null && Authorized != null)
{
throw new InvalidOperationException($"Do not specify both '{nameof(Authorized)}' and '{nameof(ChildContent)}'.");
}
if (AuthenticationState == null)
{
throw new InvalidOperationException($"Authorization requires a cascading parameter of type Task<{nameof(AuthenticationState)}>. Consider using {typeof(CascadingAuthenticationState).Name} to supply this.");
}
// First render in pending state
// If the task has already completed, this render will be skipped
currentAuthenticationState = null;
// Then render in completed state
// Importantly, we *don't* call StateHasChanged between the following async steps,
// otherwise we'd display an incorrect UI state while waiting for IsAuthorizedAsync
currentAuthenticationState = await AuthenticationState;
isAuthorized = await IsAuthorizedAsync(currentAuthenticationState.User);
}

That leads to rendering of NotAuthorized state in BuildRenderTree function which is unexpected behaviour (currentAuthenticationState is set but authorization is still in progress). NotAuthorized state should only ever be rendered after IsAuthorizedAsync has been completed.

protected override void BuildRenderTree(RenderTreeBuilder builder)
{
// We're using the same sequence number for each of the content items here
// so that we can update existing instances if they are the same shape
if (currentAuthenticationState == null)
{
builder.AddContent(0, Authorizing);
}
else if (isAuthorized)
{
var authorized = Authorized ?? ChildContent;
builder.AddContent(0, authorized?.Invoke(currentAuthenticationState));
}
else
{
builder.AddContent(0, NotAuthorized?.Invoke(currentAuthenticationState));
}
}

If the AuthenticationState had not been completed before it was awaited the rendering would have occurred before line 95 and no render would occur between lines 95 and 96. In that case the AuthorizeViewCore works as expected.

In this fix the isAuthorized field is made nullable and it is used to determine the state to display in the BuildRenderTree function. Until the authorization has completed the isAuthorized is null the Authorizing state is displayed as expected.

Addresses #24381

Fixes #24381

Component render would occur between lines 95 and 96 if the AuthenticationState Task had already been completed before it was awaited on line 95. 
https://github.com/dotnet/aspnetcore/blob/d70439cc82253ac32cc3eaf94ce965bab1ac9d37/src/Components/Authorization/src/AuthorizeViewCore.cs#L73-97

That leads to rendering of NotAuthorized state in BuildRenderTree function which is unexpected behaviour. NotAuthorized state should only ever be rendered after IsAuthorizedAsync has been completed.
https://github.com/dotnet/aspnetcore/blob/d70439cc82253ac32cc3eaf94ce965bab1ac9d37/src/Components/Authorization/src/AuthorizeViewCore.cs#L53-70

If the AuthenticationState had not been completed before it was awaited the rendering would have occurred before line 95 and no render would occur between lines 95 and 96. In that case the AuthorizeViewCore works as expected.

In this fix the isAuthorized field is made nullable and it is used to determine the state to display in the BuildRenderTree function. Until the authorization has completed the isAuthorized is null the Authorizing state is displayed as expected.
@juho-hanhimaki juho-hanhimaki requested a review from a team as a code owner April 14, 2021 10:05
@ghost ghost added area-blazor Includes: Blazor, Razor Components community-contribution Indicates that the PR has been added by a community member labels Apr 14, 2021
@HaoK
Copy link
Member

HaoK commented Apr 14, 2021

@juho-hanhimaki
Copy link
Contributor Author

@HaoK I'll take a look at adding the test.

Took some effort to get the thing building locally.

@dnfadmin
Copy link

dnfadmin commented Apr 14, 2021

CLA assistant check
All CLA requirements met.

@juho-hanhimaki
Copy link
Contributor Author

juho-hanhimaki commented Apr 14, 2021

I added the test that covers the PR/issue.

Because the TestAuthorizationService that is used in other tests completes the authorization synchronously I had to make async version of it. Otherwise authorization would just complete synchronously and component would just render the Authorized state skipping the Authorizing state completely.

@HaoK
Copy link
Member

HaoK commented Apr 15, 2021

Awesome that test looks great, on last small thing, can you confirm locally that without your changes your test fails

@juho-hanhimaki
Copy link
Contributor Author

@HaoK I did, and the test fails as it should without the changes to AuthorizeViewCore.cs.

image

@juho-hanhimaki
Copy link
Contributor Author

image

@HaoK
Copy link
Member

HaoK commented Apr 15, 2021

Thanks for doing this work @juho-hanhimaki !

@HaoK HaoK merged commit 6868d7f into dotnet:main Apr 15, 2021
@ghost ghost added this to the 6.0-preview4 milestone Apr 15, 2021
@juho-hanhimaki
Copy link
Contributor Author

Thank you too for taking the PR!

@ghost
Copy link

ghost commented Apr 15, 2021

Hi @juho-hanhimaki. It looks like you just commented on a closed PR. The team will most probably miss it. If you'd like to bring something important up to their attention, consider filing a new issue and add enough details to build context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-blazor Includes: Blazor, Razor Components community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants