I moved authentication and authorization from identity template of recently updated visual studio to my blazor webassembly (client-server) web application. Unfortunately, I got errors when completed movements of classes and other code elements.Below is list of what I've done.
AccountController.cs: (debugged, here is all ok)
[HttpPost][Route("/api/account/login")]public async Task<SignInResult> Login([FromBody] LoginModel model){ var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, false); return result;}PersistingServerAuthenticationStateProvider.cs: (not enter here on login at all, is it ok?)
internal sealed class PersistingServerAuthenticationStateProvider : ServerAuthenticationStateProvider, IDisposable{ private readonly PersistentComponentState state; private readonly IdentityOptions options; private readonly PersistingComponentStateSubscription subscription; private Task<AuthenticationState>? authenticationStateTask; public PersistingServerAuthenticationStateProvider( PersistentComponentState persistentComponentState, IOptions<IdentityOptions> optionsAccessor) { state = persistentComponentState; options = optionsAccessor.Value; AuthenticationStateChanged += OnAuthenticationStateChanged; subscription = state.RegisterOnPersisting(OnPersistingAsync, RenderMode.InteractiveWebAssembly); } private void OnAuthenticationStateChanged(Task<AuthenticationState> task) { authenticationStateTask = task; } private async Task OnPersistingAsync() { if (authenticationStateTask is null) { throw new UnreachableException($"Authentication state not set in {nameof(OnPersistingAsync)}()."); } var authenticationState = await authenticationStateTask; var principal = authenticationState.User; if (principal.Identity?.IsAuthenticated == true) { var userId = principal.FindFirst(options.ClaimsIdentity.UserIdClaimType)?.Value; var email = principal.FindFirst(options.ClaimsIdentity.EmailClaimType)?.Value; if (userId != null && email != null) { state.PersistAsJson(nameof(UserInfo), new UserInfo { UserId = userId, Email = email, }); } } } public void Dispose() { subscription.Dispose(); AuthenticationStateChanged -= OnAuthenticationStateChanged; }}PersistentAuthenticationStateProvider (on authentication always empty return, on access to site pages, required authorizetion - not enter this class method):
public class PersistentAuthenticationStateProvider : AuthenticationStateProvider{ private static readonly Task<AuthenticationState> defaultUnauthenticatedTask =Task.FromResult(new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()))); private readonly Task<AuthenticationState> authenticationStateTask = defaultUnauthenticatedTask; public PersistentAuthenticationStateProvider(PersistentComponentState state) { if (!state.TryTakeFromJson<UserInfo>(nameof(UserInfo), out var userInfo) || userInfo is null) { return; // always goes here } Claim[] claims = [ new Claim(ClaimTypes.NameIdentifier, userInfo.UserId), new Claim(ClaimTypes.Name, userInfo.Email), new Claim(ClaimTypes.Email, userInfo.Email) ]; authenticationStateTask = Task.FromResult( new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims, authenticationType: nameof(PersistentAuthenticationStateProvider))))); } public override Task<AuthenticationState> GetAuthenticationStateAsync() => authenticationStateTask;}Server persistence provider registered at server Program.cs, another provider registered on client Program.cs. Mismatch with templated project is that Login.razor view in my project placed on client, but in template-generated - on server.Feeling that I've missing something simple - the thing why PersistingServerAuthenticationStateProvider not entering in debugger at all.
Registration code (client):
builder.Services.AddAuthorizationCore();builder.Services.AddCascadingAuthenticationState();builder.Services.AddSingleton<AuthenticationStateProvider, PersistentAuthenticationStateProvider>();Registration code (server):
builder.Services.AddDbContext<IdentityDataContext>((options) =>{ options.UseSqlServer(databaseConfig?.ConnectionString);});builder.Services.AddIdentityCore<User>()//options => options.SignIn.RequireConfirmedAccount = true .AddEntityFrameworkStores<IdentityDataContext>() .AddSignInManager() .AddDefaultTokenProviders();builder.Services.AddScoped<UserManager<User>>();builder.Services.AddScoped<AuthenticationStateProvider, PersistingServerAuthenticationStateProvider>();