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

ASP.NET Core Identity Cookie Stored But Not Being authorizing - Blazor Web App

$
0
0

I am trying to authenticate an user from Blazor to access resources on a ASP.NET Core Web API using ASP.NET Core Identity.

I have two authentication methods configured: JWT and Cookies.

When I login from direct API call (Swagger, Postman), I can normally access the resources, with both JWT and cookie authentication. When I login from Blazor, the browser sets the cookie, but I get an Unauthorized response.

Login.razor

@page "/Account/Login"@inject SignInManager<UserModel> SignInManager@inject ILogger<Login> Logger@inject NavigationManager NavigationManager@inject IdentityRedirectManager RedirectManager@inject IAuthService AuthService<PageTitle>Log in</PageTitle><MudText Typo="Typo.h3" GutterBottom="true">Log in</MudText><MudGrid><MudItem md="6"><StatusMessage Message="@errorMessage" /><EditForm Model="Input" method="post" OnValidSubmit="LoginUser" FormName="login"><DataAnnotationsValidator /><MudText GutterBottom="true" Typo="Typo.body1">Use a local account to log in.</MudText><MudGrid><MudItem md="12"><MudStaticTextField For="@(() => Input.Email)" @bind-Value="Input.Email"                    Label="Email" Placeholder="name@example.com"                    UserAttributes="@(new() { { "autocomplete", "username" }, { "aria-required", "true" } } )" /></MudItem><MudItem md="12"><MudStaticTextField For="@(() => Input.Password)" @bind-Value="Input.Password"                    Label="Password" InputType="InputType.Password" Placeholder="password"                    UserAttributes="@(new() { { "autocomplete", "current-password" }, { "aria-required", "true" } } )" /></MudItem><MudItem md="12">                    @* <MudStaticCheckBox For="@(() => Input.RememberMe)" @bind-Value="Input.RememberMe">Remember me</MudStaticCheckBox> *@</MudItem><MudItem md="12"><MudStaticButton Variant="Variant.Filled" Color="Color.Primary" FullWidth="true" FormAction="FormAction.Submit">Log in</MudStaticButton></MudItem></MudGrid></EditForm><MudGrid Class="mt-4"><MudItem md="12"><MudLink Href="Account/ForgotPassword">Forgot your password?</MudLink><br /><MudLink Href="@(NavigationManager.GetUriWithQueryParameters("Account/Register", new Dictionary<string, object?> { ["ReturnUrl"] = ReturnUrl }))">Register as a new user</MudLink><br /><MudLink Href="Account/ResendEmailConfirmation">Resend email confirmation</MudLink></MudItem></MudGrid></MudItem><MudItem md="6"><MudText GutterBottom="true" Typo="Typo.body1">Use another service to log in.</MudText><ExternalLoginPicker /></MudItem></MudGrid>@code {    private string? errorMessage;    [CascadingParameter]    private HttpContext HttpContext { get; set; } = default!;    [SupplyParameterFromForm]    private LoginRequestDto Input { get; set; } = new();    [SupplyParameterFromQuery]    private string? ReturnUrl { get; set; }    protected override async Task OnInitializedAsync()    {        if (HttpMethods.IsGet(HttpContext.Request.Method))        {            // Clear the existing external cookie to ensure a clean login process            await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);        }    }    public async Task LoginUser()    {        await AuthService.Login(Input);        RedirectManager.RedirectTo(ReturnUrl);    }}

AuthService.cs

public async Task<AuthResponseDto?> Login(LoginRequestDto loginDto){    UserModel? userDb = default!;    if (!string.IsNullOrEmpty(loginDto.Email))    {        userDb = await _userManager.FindByEmailAsync(loginDto.Email);    }     else if (!string.IsNullOrEmpty(loginDto.UserName))    {        userDb = await _userManager.FindByNameAsync(loginDto.UserName);    }    if (userDb is null || !(await _signInManager.CheckPasswordSignInAsync(userDb, loginDto.Password, false)).Succeeded)    {        return null;    }    var result = await _signInManager.PasswordSignInAsync(userDb, loginDto.Password, false, false);    if (result.Succeeded)    {        var token = await GenerateAccessToken(userDb);        var response = userDb.ToAuthResponseDto(token);        return response;    }    return null;}

Program.cs (in SocialMedia.Api):

var builder = WebApplication.CreateBuilder(args);// Devlooped servicesbuilder.Services.AddServices();// Add services to the container.// Ignore object cycling on queries with related databuilder.Services.AddControllers().AddJsonOptions(options =>{    options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;});builder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen(c =>{    var securityScheme = new OpenApiSecurityScheme    {        Name = "JWT Authentication",        Description = "Enter your JWT token in this field",        In = ParameterLocation.Header,        Type = SecuritySchemeType.Http,        Scheme = "bearer",        BearerFormat = "JWT",    };    c.AddSecurityDefinition("Bearer", securityScheme);    var securityRequirement = new OpenApiSecurityRequirement()    {        {            new OpenApiSecurityScheme            {                Reference = new OpenApiReference                {                    Type = ReferenceType.SecurityScheme,                    Id = "Bearer",                },            },            Array.Empty<string>()        },    };    c.AddSecurityRequirement(securityRequirement);});builder.Services.AddRazorPages();builder.Services.AddProblemDetails();builder.Services.AddIdentity<UserModel, IdentityRole>(options =>{    options.SignIn.RequireConfirmedAccount = false;    options.Password.RequireNonAlphanumeric = false;    options.Password.RequireDigit = false;    options.Password.RequireUppercase = false;    options.User.RequireUniqueEmail = true;    options.User.AllowedUserNameCharacters ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";})    .AddEntityFrameworkStores<ApplicationDbContext>()    .AddDefaultTokenProviders();builder.Services.AddAuthentication(options =>{    options.DefaultScheme = IdentityConstants.ApplicationScheme;    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(options =>{    options.TokenValidationParameters = new TokenValidationParameters    {        ValidateIssuer = true,        ValidIssuer = builder.Configuration["JWT:Issuer"],        ValidateAudience = true,        ValidAudience = builder.Configuration["JWT:Audience"],        ValidateIssuerSigningKey = true,        IssuerSigningKey = new SymmetricSecurityKey(            System.Text.Encoding.UTF8.GetBytes(builder.Configuration["JWT:SignInKey"] !)),    };    options.IncludeErrorDetails = true;});builder.Services.ConfigureApplicationCookie(options =>{    options.ExpireTimeSpan = TimeSpan.FromHours(1);    options.Cookie.HttpOnly = true;    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;    options.Cookie.SameSite = SameSiteMode.None;    options.Cookie.Name = "APICOOKIE";});builder.Services.AddAuthorization(options =>{    var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(        IdentityConstants.ApplicationScheme,        JwtBearerDefaults.AuthenticationScheme);    defaultAuthorizationPolicyBuilder =        defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();    options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();});builder.Services.AddDbContextFactory<ApplicationDbContext>(options =>    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));builder.Services.AddCors(options =>{    options.AddPolicy("Default", policy =>    {        _ = policy.WithOrigins("https://localhost:7213")                   .AllowAnyMethod()                   .AllowAnyHeader()                   .AllowCredentials();    });});builder.Services.AddHttpContextAccessor();var app = builder.Build();app.UseCors();app.UseHttpsRedirection();app.UseAuthentication();app.UseAuthorization();app.MapControllers();app.MapRazorPages();// Configure the HTTP request pipeline.if (app.Environment.IsDevelopment()){    _ = app.UseSwagger();    _ = app.UseSwaggerUI();    // _ = app.MapControllers().AllowAnonymous();}app.Run();

Program.cs (in SocialMedia.Front):

var builder = WebApplication.CreateBuilder(args);// Devlooped servicesbuilder.Services.AddServices();// Add MudBlazor servicesbuilder.Services.AddMudServices();// Add services to the container.builder.Services.AddRazorComponents()    .AddInteractiveServerComponents()    .AddInteractiveWebAssemblyComponents()    .AddAuthenticationStateSerialization(        options => options.SerializeAllClaims = true);builder.Services.AddCascadingAuthenticationState();builder.Services.AddIdentity<UserModel, IdentityRole>(options =>{    options.SignIn.RequireConfirmedAccount = false;    options.Password.RequireNonAlphanumeric = false;    options.Password.RequireDigit = false;    options.Password.RequireUppercase = false;    options.User.RequireUniqueEmail = true;    options.User.AllowedUserNameCharacters ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+";})    .AddEntityFrameworkStores<ApplicationDbContext>()    .AddDefaultTokenProviders();builder.Services.AddAuthentication(options =>{    options.DefaultScheme = IdentityConstants.ApplicationScheme;    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;});builder.Services.ConfigureApplicationCookie(options =>{    options.ExpireTimeSpan = TimeSpan.FromHours(1);    options.Cookie.HttpOnly = true;    options.Cookie.SecurePolicy = CookieSecurePolicy.Always;    options.Cookie.SameSite = SameSiteMode.None;});builder.Services.AddHttpClient("Auth", options =>{    options.BaseAddress = new Uri(builder.Configuration["ApiUrl"]!);}).AddHttpMessageHandler<CookieHandler>();builder.Services.AddDbContextFactory<ApplicationDbContext>(options =>    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));builder.Services.AddDatabaseDeveloperPageExceptionFilter();var app = builder.Build();// Configure the HTTP request pipeline.if (app.Environment.IsDevelopment()){    app.UseWebAssemblyDebugging();    _ = app.UseMigrationsEndPoint();} 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.MapRazorComponents<App>()    .AddInteractiveServerRenderMode()    .AddInteractiveWebAssemblyRenderMode()    .AddAdditionalAssemblies(typeof(SocialMedia.Front.Client._Imports).Assembly);// Add additional endpoints required by the Identity /Account Razor components.app.MapAdditionalIdentityEndpoints();app.Run();

Program.cs (in SocialMedia.Front.Client):

var builder = WebAssemblyHostBuilder.CreateDefault(args);// Devlooped servicesbuilder.Services.AddServices();builder.Services.AddMudServices();builder.Services.AddAuthorizationCore();builder.Services.AddCascadingAuthenticationState();builder.Services.AddAuthenticationStateDeserialization();builder.Services.AddHttpClient("Auth", options =>{    options.BaseAddress = new Uri(builder.Configuration["ApiUrl"]!);}).AddHttpMessageHandler<CookieHandler>();await builder.Build().RunAsync();

PostsController.cs

namespace SocialMedia.Api.Controllers{    [Route("api/[controller]")]    [ApiController]    [Authorize]    public class PostsController(IPostService postService) : ControllerBase    {        private readonly string _postNotFoundMsg = "The post was not found!";        [HttpPost]        public async Task<IActionResult> Create([FromBody] CreatePostRequestDto postToCreate)        {            var createdPost = await postService.Create(postToCreate);            if (createdPost == null)            {                return NotFound("User not found!");            }            return CreatedAtAction(                nameof(GetById),                new { createdPost.Id },                createdPost);        }        [HttpGet]        public async Task<IActionResult> GetAll()        {            var posts = await postService.GetAll();            return Ok(posts);        }        [HttpGet("{id}")]        public async Task<IActionResult> GetById([FromRoute] int id)        {            var post = await postService.GetById(id);            if (post == null)            {                return NotFound(_postNotFoundMsg);            }            return Ok(post);        }        [HttpPut]        public async Task<IActionResult> Update([FromBody] UpdatePostRequestDto postToUpdate)        {            var updatedPost = await postService.Update(postToUpdate);            if (updatedPost == null)            {                return NotFound(_postNotFoundMsg);            }            return Ok(updatedPost);        }        [HttpDelete("{id}")]        public async Task<IActionResult> Delete([FromRoute] int id)        {            var deletedPost = await postService.Delete(id);            if (deletedPost == null)            {                return NotFound(_postNotFoundMsg);            }            return NoContent();        }    }}

I saw that for cookies to be sent, it should have a CookieHander. I implemented it, but no success.

CookieHander.cs

namespace SocialMedia.Front.Client.Middlewares{    [Service(ServiceLifetime.Transient)]    public class CookieHandler : DelegatingHandler    {        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)        {            _ = request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);            request.Headers.Add("X-Requested-With", ["XMLHttpRequest"]);            return await base.SendAsync(request, cancellationToken);        }    }}

Page printscreeen

Page printscreeen

I already tried using SignInManager instead of my own AuthService class, but it did not work either. Tried to change a lot of things on the Program.cs of both projects, but none of these had effect.


Viewing all articles
Browse latest Browse all 4839

Trending Articles



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