I've come accross a situation where the absence of a default authentication scheme leads to an unauthorized state. If possible I want to omit the default scheme, however, it seems that the default scheme is required.
This is my setup:
I am omitting the default scheme, but adding the cookie authentication handler, which per default registers the "Cookies" scheme:
builder.Services.AddAuthentication().AddCookie();
I am adding a policy like this:
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme)
.Build();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("FooBla", policy);
});
During setup I also add the required middleware before a call to MapBlazorHub:
app.UseAuthentication();
app.UseAuthorization();
I have defined a razor page (cshtml) for the login as follows:
@page "/login"
@model BlazorApp1.Pages.LoginModel
@{
}
and a codebehind like this:
public class LoginModel : PageModel
{
public IActionResult OnGet()
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, "John Smith"),
new Claim("sub", "John Smith"),
};
var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme));
var authProperties = new AuthenticationProperties
{
IsPersistent = true,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddHours(12),
AllowRefresh = true,
RedirectUri = this.Url.Content("~/"),
};
this.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal, authProperties);
return this.LocalRedirect(this.Url.Content("~/"));
}
}
And finally a component with an AuthorizeView component:
@page "/counter"
@inject ILoggerFactory LoggerFactory
<PageTitle>Counter</PageTitle>
<AuthorizeView Policy="FooBla">
<Authorized>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</Authorized>
<NotAuthorized>
<h1>Not Authorized</h1>
</NotAuthorized>
</AuthorizeView>
@code {
private int currentCount = 0;
private void IncrementCount() => currentCount++;
}
The App.razor looks like this:
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<h1>Fooooo</h1>
</NotAuthorized>
</AuthorizeRouteView>
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
After the login page the browser stores a cookie. However, the cookie handler seems not to get triggered when I redirect to the blazor component, therefore the AuthorizeView component renders the NotAuthorized Component.
These are my questions:
- Is it mandatory to provide a default scheme?
- Does the this.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal, authProperties); not work for blazor apps?