Based on that feedback, I have created a solution that works well for me. I started by adding authentication in Startup.cs which takes the connection options from appsettings.json
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(options =>
{
Configuration.Bind("AzureAdB2C", options);
options.SaveTokens = true;
});
I then added an ApplicationUser.cs POCO to hold the information that I was interested in for my users
public class ApplicationUser
{
public string UserId { get; set; }
public string EmailAddress { get; set; }
public string DisplayName { get; set; }
public string Country { get; set; }
}
To populate that user object, I created a _Host.cshtml.cs file in the Pages folder with the following:
public class HostAuthenticationModel : PageModel
{
public IActionResult OnGet()
{
if (User.Identity.IsAuthenticated)
{
ClaimsPrincipal user = HttpContext.User;
ApplicationUser.UserId = user.Claims
.Where(claim => claim.Type == "http://schemas.microsoft.com/identity/claims/objectidentifier")
.FirstOrDefault().Value.ToString();
ApplicationUser.Country = user.Claims
.Where(claim => claim.Type == "country")
.FirstOrDefault().Value.ToString();
ApplicationUser.EmailAddress = user.Claims
.Where(claim => claim.Type == "emails")
.FirstOrDefault().Value.ToString();
ApplicationUser.DisplayName = user.Claims
.Where(claim => claim.Type == "name")
.FirstOrDefault().Value.ToString();
}
return Page();
}
public ApplicationUser ApplicationUser { get; set; } = new ApplicationUser();
}
This code gets the User from the HttpContext and maps the claims to my user object class. This could be extended as needed.
With that in place, I added a model directive to the top of the _Host.cshtml file
@model HostAuthenticationModel
and added a tag helper to the Component
<component
type="typeof(App)"
render-mode="ServerPrerendered"
param-ApplicationUser="Model.ApplicationUser"
/>
That tag helper passes the new ApplicationUser object into the app component as a parameter.
To use that parameter, I needed to get it inside of the App component as follows
@code {
[Parameter]
public ApplicationUser ApplicationUser { get; set; }
}
I then created a cascading parameter with that value in App so I can easily access it from any child component
<CascadingValue Value="@ApplicationUser">
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingValue>
Finally, in the component where I needed the User object, you simply declare the cascading parameter thusly
[CascadingParameter] ApplicationUser ApplicationUser { get; set; }
It is probably possible to add the ApplicationUser object into a state service of some sort and provide that through DI, but I haven't taken it to that extreme just yet (but probably will in the future at some point).