I'm working on a PWA application in Blazor and I'm having two problems that seems correlated. My authentication process isn't working and everything is always loaded in cache (due to the PWA architecture)
I'm using the Negotiate Authentication protocole to login my user and i don't seems to work properyl. Here is my problem : the first time the user try to access the application, a prompt will ask the user to login. When it does, it works fine but after some times, when the user try to access to the app, there is no prompt asking him to login and everything on the app return a 401 not Authorized.
The only way for the user to use the apps properly is to refresh the cache manually by pressing CTRL + Shift + R or by deleting everything on their web history.
My guess is that due to the PWA architecture, the service worker have something going on and think the user is still login.
How can I force the user to login again ?
Do I need to force the refresh of something when accessing my app ? And how ?
Here is the code I'm using
Program.cs
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy.
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromDays(1); // La session est valable une journee.
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseSession();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.MapControllers();
app.MapFallbackToFile("index.html");
app.Run();
UserController
/// <summary>
/// Method to get the current user using his CP
/// </summary>
/// <returns><see cref="User"/> or null if CP absent </returns>
public User GetUser()
{
var u = _userService.AuthUser(CurrentCP);
return u;
}
App.razor
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<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>
CustomAuthentificationStateProvider
public class CustomAuthenticationProvider : AuthenticationStateProvider
{
private readonly HttpClient _http;
private ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity());
public CustomAuthenticationProvider(HttpClient httpClient)
{
_http = httpClient;
}
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
await Task.FromResult(0);
return new AuthenticationState(claimsPrincipal);
}
public async Task LoginNotify()
{
//API Call
GSMR.Shared.User user = await _http.GetFromJsonAsync<GSMR.Shared.User>("User/GetUser");
//If i have a user we login
if (user!=null)
{
Claim claimName = new Claim("Name", string.Format("{0} {1}", user.FirstName, user.Name));
Claim claimCP = new Claim(ClaimTypes.NameIdentifier, user.CP);
Claim claimRole = new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", user.Role.ToString());
Claim claimActor = new Claim(ClaimTypes.Actor, JsonSerializer.Serialize(user));
var identity = new ClaimsIdentity(new[]
{
claimName, claimCP, claimRole, claimActor
},"API AUTH");
claimsPrincipal = new ClaimsPrincipal(identity);
NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
}
}
MainLayout.razor
<AuthorizeView>
<Authorized>
<div class="page">
<div class="sidebar">
<CascadingValue Value="IsOnline">
<CascadingValue Value="AuditsCount">
<NavMenu />
</CascadingValue>
</CascadingValue>
</div>
<main>
<CascadingValue Value="IsOnline">
<CascadingValue Value="AuditsCount">
<article class="content px-4">@Body</article>
</CascadingValue>
</CascadingValue>
</main>
</div>
</Authorized>
<NotAuthorized>
<MudText Typo="Typo.h2">You are not authorized</MudText>
</NotAuthorized>
</AuthorizeView>