Quantcast
Channel: Active questions tagged blazor - Stack Overflow
Viewing all articles
Browse latest Browse all 4006

.NET 8 Blazor web app with Web API using JWT authentication

$
0
0

I have two .NET 8 projects, an ASP.NET Core 8 Web API and Blazor web app with interactive render mode set to server.

The Web API handles the authentication and provides a JWT token. The registration and login for getting a token works fine.

The issue arises when I try to use <AuthorizeView>. I added it to the Home.razor component as a basic test but I was then met with an error:

JavaScript interop calls cannot be issued at this time

I have not been able to progress beyond this issue.

Program.cs:

using AuthDemo.Blazor.Server.UI.Components;using AuthDemo.Blazor.Server.UI.Infrastructure.Services.HttpClients;using Blazored.LocalStorage;using AuthDemo.Blazor.Server.UI.Infrastructure.Services.Authentication;using Microsoft.AspNetCore.Components.Authorization;using AuthDemo.Blazor.Server.UI.Infrastructure.Providers.Authentication;var builder = WebApplication.CreateBuilder(args);// Add services to the container.builder.Services.AddRazorComponents()    .AddInteractiveServerComponents();builder.Services.AddCascadingAuthenticationState();builder.Services.AddBlazoredLocalStorage();builder.Services.AddHttpClient<IAuthDemoApiClient, AuthDemoApiClient>(cl => cl.BaseAddress = new Uri("https://localhost:7287"));builder.Services.AddScoped<IAuthenticationService, AuthenticationService>();builder.Services.AddScoped<CustomAuthenticationStateProvider>();builder.Services.AddScoped<AuthenticationStateProvider>(p => p.GetRequiredService<CustomAuthenticationStateProvider>());builder.Services.AddRouting(options =>{    options.LowercaseUrls = true;});var app = builder.Build();// Configure the HTTP request pipeline.if (!app.Environment.IsDevelopment()){    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.UseStaticFiles();app.UseStatusCodePagesWithReExecute("/StatusCode/{0}");app.UseAntiforgery();app.MapRazorComponents<App>()    .AddInteractiveServerRenderMode();app.Run();

App.razor:

<!DOCTYPE html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><base href="/" /><link rel="stylesheet" href="bootstrap/bootstrap.min.css" /><link rel="stylesheet" href="app.css" /><link rel="stylesheet" href="AuthDemo.Blazor.Server.UI.styles.css" /><link rel="icon" type="image/png" href="favicon.png" /><HeadOutlet @rendermode="InteractiveServer" /></head><body><Routes @rendermode="InteractiveServer" /><script src="_framework/blazor.web.js"></script></body></html>

AuthenticationService.cs:

using AuthDemo.Blazor.Server.UI.Infrastructure.Services.HttpClients;using Microsoft.AspNetCore.Identity.Data;namespace AuthDemo.Blazor.Server.UI.Infrastructure.Services.Authentication{    public class AuthenticationService : IAuthenticationService    {        private readonly IAuthDemoApiClient _AuthDemoApiClient;        public AuthenticationService(IAuthDemoApiClient AuthDemoApiClient)        {            _AuthDemoApiClient = AuthDemoApiClient;        }        public async Task<AuthenticationResponseDto?> LoginAsync(LoginDto loginDto)        {            var result = await _AuthDemoApiClient.LoginAsync(loginDto);            return result;        }    }}

CustomAuthenticationStateProvider.cs:

using Blazored.LocalStorage;using Microsoft.AspNetCore.Components.Authorization;using System.IdentityModel.Tokens.Jwt;using System.Security.Claims;namespace AuthDemo.Blazor.Server.UI.Infrastructure.Providers.Authentication{    public class CustomAuthenticationStateProvider : AuthenticationStateProvider    {        private readonly ILocalStorageService _localStorageService;        private ClaimsPrincipal _anonymous = new ClaimsPrincipal(new ClaimsIdentity());        private readonly JwtSecurityTokenHandler _jwtSecurityTokenHandler;        public CustomAuthenticationStateProvider(ILocalStorageService localStorageService)        {            _localStorageService = localStorageService;            _jwtSecurityTokenHandler = new JwtSecurityTokenHandler();        }        public override async Task<AuthenticationState> GetAuthenticationStateAsync()        {            var token = await _localStorageService.GetItemAsync<string>("accessToken");            if (string.IsNullOrEmpty(token))            {                return new AuthenticationState(_anonymous);            }            var tokenContent = _jwtSecurityTokenHandler.ReadJwtToken(token);            var claims = tokenContent.Claims;            var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt"));            return await Task.FromResult(new AuthenticationState(user));        }        public void AuthenticateUser(string token)        {            var tokenContent = _jwtSecurityTokenHandler.ReadJwtToken(token);            var claims = tokenContent.Claims;            var user = new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt"));            var state = new AuthenticationState(user);            NotifyAuthenticationStateChanged(Task.FromResult(state));        }    }}

Login.razor:

@page "/auth/login"@inject IAuthenticationService _authenticationService@inject AuthenticationStateProvider _authenticationStateProvider@inject IAuthDemoApiClient _httpAuthDemoApiClient;@inject ILocalStorageService _localStorageService;@inject NavigationManager _navigationManager;<h3>Login</h3>@if (!string.IsNullOrEmpty(exMessage)){<div class="alert alert-danger"><p>@exMessage</p></div>}<div class="card-body"><EditForm Model="LoginDto" OnValidSubmit="HandleLogin"><DataAnnotationsValidator /><ValidationSummary /><div class="form-group"><label for="EmailAddress">Email Address</label><InputText class="form-control" @bind-Value="LoginDto.EmailAddress" /><ValidationMessage For="@(() => LoginDto.EmailAddress)" /></div><br /><div class="form-group"><label for="Password">Password</label><InputText type="password" class="form-control" @bind-Value="LoginDto.Password" /><ValidationMessage For="@(() => LoginDto.Password)" /></div><button type="submit" class="btn btn-primary btn-block">Login</button></EditForm></div>@code {    LoginDto LoginDto = new LoginDto();    string exMessage = string.Empty;    private async Task HandleLogin()    {        try        {            var authResponse = await _authenticationService.LoginAsync(LoginDto);            if (authResponse != null)            {                await _localStorageService.SetItemAsync("token", authResponse.Token);                ((CustomAuthenticationStateProvider)_authenticationStateProvider).AuthenticateUser(authResponse.Token!);                _navigationManager.NavigateTo("/");            }        }        catch (ApiException ex)        {            exMessage = ex.Response;        }        catch (Exception ex)        {            exMessage = ex.Message;        }    }}

The above works, I can login and I get back a token.

The issue occurs when I add the following:

Home.razor:

@page "/"<PageTitle>Home</PageTitle><h1>Hello, world!</h1>Welcome to your new app.<AuthorizeView><Authorized><h1>logged in</h1></Authorized><NotAuthorized><h1>not logged in</h1></NotAuthorized></AuthorizeView>

When I run the project I get the following error:

enter image description here

I get the same issue if I add <AuthorizeView> to Routes.razor.

I am trying understand what the correct approach is here ...


Viewing all articles
Browse latest Browse all 4006

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>