I am using Blazor web assembly bother server side and web assembly. I am trying to add JWT authentication but it is not working. When I route to InteractiveServer or InteractiveAssembly pages with [Authorize] attribute, I am seeing not found pages in the web browser. If I remove that attribute, everything works fine.
Goal: The goal is to authenticate using JWT. I am using CustomAuthenticationStateProvider which is returning nothing to mimic no user is authenticated. I have no problem with that. I think my configurations to add Authentication and Authorization is missing something.
Here are my Program.cs for both projects:
Server side Program.cs
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.FluentUI.AspNetCore.Components;
using Microsoft.IdentityModel.Tokens;
using OctuFit.Web.Client.ApiServices;
using OctuFit.Web.Client.Services;
using OctuFit.Web.Client.Utils;
using OctuFit.Web.Components;
using Refit;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddFluentUIComponents();
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "asdas",
ValidAudience = "asdasd",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("asdasdasd"))
};
});
builder.Services.AddAuthorizationCore();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<ILocalStorageService, LocalStorageService>();
builder.Services.AddScoped<IAuthService, AuthService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseAntiforgery();
app.MapStaticAssets();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(OctuFit.Web.Client._Imports).Assembly);
app.Run();
Web assembly Program.cs
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using Microsoft.FluentUI.AspNetCore.Components;
using OctuFit.Web.Client.ApiServices;
using OctuFit.Web.Client.Services;
using OctuFit.Web.Client.Utils;
using Refit;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.Services.AddFluentUIComponents();
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<ILocalStorageService, LocalStorageService>();
builder.Services.AddScoped<IAuthService, AuthService>();
// Refit Services
builder.Services.AddScoped<RefitInterceptor>();
builder.Services.AddRefitClient<IApiAuthService>()
.ConfigureHttpClient(c =>
{
c.BaseAddress = new Uri(builder.Configuration["Api:BaseUrl"] ?? throw new ArgumentNullException("Api base url is null."));
})
.AddHttpMessageHandler<RefitInterceptor>();
await builder.Build().RunAsync();
If I don't specify AddJwtBearer so it says that the "Authentication scheme is not provided".
Question:
Any help will be highly appreciated. Or any tutorial to see how authentication and authorization actually works.
Thank you
Update 1: So after reading and researching a lot. I came across some intresting things. The Blazor auth pipelines does not working like server. I am defining my own CustomAuthenticationStateProvider so I don't need "AddAuthentication" and "UseAuthentication, UseAuthorization" in the pipeline. So I have removed
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "asdas",
ValidAudience = "asdasd",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("asdasdasd"))
};
});
and
app.UseAuthentication();
app.UseAuthorization();
The new issue I am facing now is, when I use [Authorize], this exception is happening:
System.InvalidOperationException: Unable to find the required 'IAuthenticationService' service. Please add all the required services by calling 'IServiceCollection.AddAuthentication' in the application startup code.
Any help here?
So what I did this is after searching for so long, created another class
public class AuthorizationMiddlewareResultHandler : IAuthorizationMiddlewareResultHandler
{
public Task HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)
{
return next(context);
}
}
Injected it
builder.Services.AddSingleton<IAuthorizationMiddlewareResultHandler, AuthorizationMiddlewareResultHandler>();
And everything started to work. Can someone please explain me what is happening?