Skip to content

Update Identity Components in Blazor project template #51134

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
Oct 11, 2023

Conversation

halter73
Copy link
Member

@halter73 halter73 commented Oct 4, 2023

Description

This PR addresses a large amount of feedback from #50722 which was merged before they could all be addressed to unblock Accessibility Testing effort. The primary impacts are:

Runtime changes

Template changes

  • Fix compilation error due to missing using in Program.cs when the individual auth option is selected with no interactivity.
  • Add support for global (--all-interactive) instead of just per-page interactivity to the new Identity components.
  • Fix "Apply Migrations" link on the DatabaseErrorPage by calling UseMigrationsEndPoint() when necessary.
  • Add support for non-root base paths to the new Identity components.
  • Improve folder layout by putting most of the additional auth and Identity related files in the same /Account folder.
  • Use the new IEmailSender<ApplicationUser> API instead of IEmailSender for easier customization of emails.
  • Remove usage of IHttpContextAccessor from the template because that is generally regarded as bad practice due to the unnecessary reliance on AsyncLocal.
  • Remove underscore (_) from private field names.
  • Reduce usage of null! and default!.
  • Use normal <button> for logout link in nav bar rather than <a onclick="document.getElementById('logout-form').submit();">, and remove separate LogoutForm.razor.

Customer Impact

This fixes several bugs in the Blazor project template when choosing the individual auth option and makes several runtime fixes that will be beneficial to any global interactive Blazor application that needs to include some components that must always be statically rendered.

Regression?

  • Yes
  • No

Risk

  • High
  • Medium
  • Low

Obviously, we would rather not make such a large change after RC2. Particularly when it's a change that touches public API. Fortunately, the runtime changes are very small, and only to parts of the runtime that were last updated in RC2 (see #50181 and #50946).

The vast majority of the changes in the PR only affect the Blazor project template when the non-default individual auth option is selected. This was merged very late in RC2 (#50722) with the expectation that we would make major changes prior to GA.

Verification

  • Manual (required)
  • Automated

Packaging changes reviewed?

  • Yes
  • No
  • N/A

@halter73 halter73 requested a review from a team as a code owner October 4, 2023 15:57
@ghost ghost added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Oct 4, 2023
@halter73 halter73 changed the title Update Identity Components Blazor project template Update Identity Components in Blazor project template Oct 4, 2023
@halter73 halter73 force-pushed the halter73/blazor-template-update branch from cdc96b0 to 425bd12 Compare October 5, 2023 09:43
@mkArtakMSFT mkArtakMSFT added area-blazor Includes: Blazor, Razor Components and removed area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates labels Oct 5, 2023
@halter73 halter73 added the Servicing-consider Shiproom approval is required for the issue label Oct 5, 2023
@ghost
Copy link

ghost commented Oct 5, 2023

Hi @halter73. Please make sure you've updated the PR description to use the Shiproom Template. Also, make sure this PR is not marked as a draft and is ready-to-merge.

To learn more about how to prepare a servicing PR click here.

@javiercn
Copy link
Member

javiercn commented Oct 5, 2023

@halter73 took a look at the changes.

Overall looks good, the main questions that I have are:

  • The use of required which is "controversial"
  • Whether the identity pages should live inside an Identity subfolder. It might feel unnecessary but its better for the longer term, as it enables people to use _Import.razor files that target only identity and nothing more.
  • Should we have an IdentityPage base component?
    • There's a lot of repetition on the pages, injecting a bunch of services, getting the HttpContext, etc. all of which can be centralized into a base class.
    • We can then use an @inherits IdentityPage on an _Imports.razor file to apply the base to all the Identity pages in one go.

@halter73
Copy link
Member Author

halter73 commented Oct 5, 2023

  • The use of required which is "controversial"

There was earlier discussion about this in the PR that originally added the Identity components to the project template when I added it the old way without required and instead used = default!. #50722 (comment).

I think

[CascadingParameter]
public required HttpContext HttpContext { get; set; }

looks a lot nicer than

[CascadingParameter]
public HttpContext HttpContext { get; set; } = default!;

And we even got some community agreement. Although they also noted that this goes directly against our guidance:

Don't use the required modifier or init accessor on component parameter properties. Components are usually instantiated and assigned parameter values using reflection, which bypasses the guarantees that init and required are designed to make.

@MackinnonBuck also brought up a good point about how it could prevent code-generated parameter setting from working in the future if we ever decided to do that. We're only using required in situations like static server rendering where we theoretically could ensure the parameters like the HttpContext [CascadingParameter] already exist during component construction meaning it's possible we could make code generation work, but this would require initializing the parameters earlier than before which might break backwards compatibility to some extent.

I'm fine with whatever we decide. We're still using private ApplicationUser user = default!; in a bunch of places elsewhere, so it's not like these required modifiers is allowing us to completely remove default! from the template. I'd like to hear from @SteveSandersonMS before removing the required's though, since he suggested it.

  • Whether the identity pages should live inside an Identity subfolder. It might feel unnecessary but its better for the longer term, as it enables people to use _Import.razor files that target only identity and nothing more.

The Account folder already serves this purpose. The identity-specific _Imports.razor file you propopse already exists too, thanks to @SteveSandersonMS's changes in #50920.

  • Should we have an IdentityPage base component?

Here's the discussion from when you suggested it in the original PR. @SteveSandersonMS mentioined:

While having a base class would work, it is a bit of a constraining solution (people may have their own other base classes they want to use). I'd be in favour of just adding [CascadingParameter] private required HttpContext HttpContext { get; set; } wherever it's needed (and I know it's in about 15 places).

It was actually less than 15 places, but it was pretty close. I agree there is a lot of repetition, but there are also quite a few very simple components like Lockout.razor that gain nothing from a base component.

I'm not sure I buy that a base class is all that constraining. If people have their own base component, couldn't they make Identity base component inherit from their own? But I do think avoiding inheritance where possible is generally a good idea because it shows there's nothing up our sleeves so to speak. All of the services and parameters we use, we declare directly on the page.

@halter73 halter73 requested a review from surayya-MS October 5, 2023 21:18
}

var result = await UserManager.ResetPasswordAsync(user, Input.Code, Input.Password);
if (result.Succeeded)
{
RedirectManager.RedirectTo("/Account/ResetPasswordConfirmation");
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's kind of subtle that RedirectTo() never returns, making the return statement redundant. From reading the code, it might be hard to tell at first that each of these if statements acts as a kind of implicit guard clause. But I don't feel a strong way about this so if you think it's fine to remove the returns then I won't fight it! 🙂

@halter73 halter73 force-pushed the halter73/blazor-template-update branch from e3a6a88 to 13a8e2c Compare October 5, 2023 23:42
- This avoids making HasCallerSpecifiedRenderMode true in the @rendermode="null" case
@mkArtakMSFT mkArtakMSFT removed the Servicing-consider Shiproom approval is required for the issue label Oct 6, 2023
@mkArtakMSFT mkArtakMSFT added the * NO MERGE * Do not merge this PR as long as this label is present. label Oct 6, 2023
@ghost
Copy link

ghost commented Oct 6, 2023

Hi @halter73. This PR was just approved to be included in the upcoming servicing release. Somebody from the @dotnet/aspnet-build team will get it merged when the branches are open. Until then, please make sure all the CI checks pass and the PR is reviewed.

- There's no good place for it currently when it's referenced by the client project

This reverts commit fac57d1.
@halter73
Copy link
Member Author

halter73 commented Oct 7, 2023

You can review the project's generated with these template changes at https://github.com/halter73/BlazorWebTemplateMatrix#readme. You can just install the latest 8.0 SDK from the https://github.com/dotnet/installers repo (8.0.100-rtm.23506.1 currently) and reference that in the global.json to quickly try any of the variations. The -ai (i.e. --all-interactivity) versions compile but will have nullability warnings and throw ArgumentNullExceptions until we get the runtime fixes in this PR and the razor compiler @rendermode nullablity update tracked by #9343 into the SDK.

@danroth27
Copy link
Member

@JeremyLikness @halter73 Are we sure we want to use "auth" in the template? The problem with "auth" is that it's an abbreviation for both authentication & authorization, which makes it ambiguous and it's also pretty informal.

Some alternatives:

  • Authenticated.razor
  • LoginRequired.razor
  • Restricted.razor
  • ...

Comment on lines 31 to 32
navigationManager.NavigateTo(uri);
throw new InvalidOperationException($"{nameof(IdentityRedirectManager)} must be called from the server.");
Copy link
Member

@javiercn javiercn Oct 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for this PR, but in wasm/server navigationManager.NavigateTo(uri); continues executing, which is a visible behavior difference. /cc: @SteveSandersonMS

@javiercn
Copy link
Member

javiercn commented Oct 9, 2023

@halter73 I do not care much about the base class vs non base class, it's unlikely impossible for anyone to have a base class that is broadly used and where everything on the base class is used by every derived implementation, it's always a trade-off of convenience vs repetition.

The other main thing is the required modifier that we agreed we were switching back to = default!.

- Add tests for "--interactivity WebAssembly"
- Add tests for "--interactivity Auto"
- Add tests for "--all-interactive"
- Add tests for "--empty"
- Add tests for "--auth Individual"
- Add tests for "--use-local-db"
- Update="Data\app.db"
- Fix reference to dotnet-ef.dll in PrepareForTest.targets
@halter73 halter73 removed the * NO MERGE * Do not merge this PR as long as this label is present. label Oct 11, 2023
@ghost
Copy link

ghost commented Oct 19, 2023

Hi @Kumima. 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.

@los93sol
Copy link

I routinely put an _Imports.razor at the root of my projects with @Attribute [Authorize] to enforce authorization on all pages, but because @Attribute [AllowAnonymous] is not explicitly defined on the razor pages in Pages/Account that ends up breaking the pages and requires me to go specify it manually.

@ghost
Copy link

ghost commented Oct 23, 2023

Hi @los93sol. 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 Servicing-approved Shiproom has approved the issue
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants