I am developing an ASP.NET Core 8.0 project. I use Onion architecture in my project. I use both JWT and Identity as security. When I generate tokens on the API side and send a request from postman, I can access the data, so it works smoothly. On the MVC side, I perform the login process and I can access the data. However, there is a problem like this. If I use the following configuration in Program.cs, it redirects to Account/Login page instead of Login/Index. This is not an index I defined.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddCookie(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/Login/Index";
options.LogoutPath = "/Login/Index";
options.AccessDeniedPath = "/Error/Error404";
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.Name = "AuthToken";
});
If I use it this way, it returns to Login/Index page but does not log in, it goes to Home/Index and returns to Login/Index again
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.LoginPath = "/Login/Index";
options.LogoutPath = "/Login/Index";
options.AccessDeniedPath = "/Error/Error404";
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.Name = "AuthToken";
});
My other codes are as follows;
All Program.cs Configuration,
using Domain.Entities;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Persistence.Context;
using PresentationUI.Handlers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<ApplicationContext>();
builder.Services.AddIdentity<AppUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationContext>()
.AddDefaultTokenProviders();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddCookie(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/Login/Index";
options.LogoutPath = "/Login/Index";
options.AccessDeniedPath = "/Error/Error404";
options.Cookie.SameSite = SameSiteMode.Strict;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.Name = "AuthToken";
});
builder.Services.AddHttpClient();
builder.Services.AddHttpContextAccessor();
builder.Services.AddTransient<AuthorizedHttpClientHandler>();
builder.Services.AddHttpClient("AuthorizedClient")
.AddHttpMessageHandler<AuthorizedHttpClientHandler>();
builder.Services.AddControllersWithViews();
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// 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.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
LoginController.cs,
using Domain.Entities;
using DtoLayer.LoginDtos;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using PresentationUI.Models;
using System.Text.Json;
namespace PresentationUI.Controllers
{
public class LoginController : Controller
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly UserManager<AppUser> _userManager;
private readonly SignInManager<AppUser> _signInManager;
public LoginController(IHttpClientFactory httpClientFactory, UserManager<AppUser> userManager, SignInManager<AppUser> signInManager)
{
_httpClientFactory = httpClientFactory;
_userManager = userManager;
_signInManager = signInManager;
}
[HttpGet]
public IActionResult Index()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Index(CreateLoginDto createLoginDto)
{
var user = await _userManager.FindByNameAsync(createLoginDto.UserName);
if (user != null)
{
var result = await _signInManager.CheckPasswordSignInAsync(user, createLoginDto.Password, true);
if (result.Succeeded)
{
if (!await _userManager.IsEmailConfirmedAsync(user))
{
return RedirectToAction("Index", "Confirmation");
}
else
{
var login = await _signInManager.PasswordSignInAsync(createLoginDto.UserName, createLoginDto.Password, true, true);
if (login.Succeeded)
{
if (await _userManager.IsInRoleAsync(user, "User"))
{
var client = _httpClientFactory.CreateClient();
var content = new StringContent(JsonSerializer.Serialize(createLoginDto), System.Text.Encoding.UTF8, "application/json");
var response = await client.PostAsync("https://localhost:7125/api/Login", content);
if (response.IsSuccessStatusCode)
{
var jsonData = await response.Content.ReadAsStringAsync();
var tokenModel = JsonSerializer.Deserialize<JwtResponseModel>(jsonData, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
if (tokenModel != null && tokenModel.Token != null)
{
HttpContext.Response.Cookies.Append("AuthToken", tokenModel.Token, new CookieOptions
{
HttpOnly = true,
Secure = true,
});
return RedirectToAction("Index", "Home");
}
}
}
else
{
ModelState.AddModelError("", "Bu sayfaya erişim izniniz bulunmamaktadır.");
return View();
}
}
else if (login.IsLockedOut)
{
ModelState.AddModelError("", "Fazla sayıda hatalı giriş yaptığınız için hesabınız kilitlendi. Lütfen daha sonra tekrar deneyiniz. Şifrenizi hatırlamıyorsanız 'Şifremi Unuttum' kısmından yeni bir şifre belirleyebilirsiniz.");
}
else
{
ModelState.AddModelError("", "Kullanıcı Adı veya Şifre Hatalı");
}
}
}
else if (result.IsLockedOut)
{
ModelState.AddModelError("", "Fazla sayıda hatalı giriş yaptığınız için hesabınız kilitlendi. Lütfen daha sonra tekrar deneyiniz. Şifrenizi hatırlamıyorsanız 'Şifremi Unuttum' kısmından yeni bir şifre belirleyebilirsiniz.");
}
else
{
ModelState.AddModelError("", "Kullanıcı Adı veya Şifre Hatalı");
}
}
else
{
ModelState.AddModelError("", "Böyle Bir Hesap Bulunamadı");
}
return View();
}
}
}
HomeController.cs,
using DtoLayer.BrandDtos;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace PresentationUI.Controllers
{
[Authorize]
public class HomeController : Controller
{
private readonly IHttpClientFactory _clientFactory;
public HomeController(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task<IActionResult> Index()
{
var client = _clientFactory.CreateClient("AuthorizedClient");
var response = await client.GetAsync("https://localhost:7125/api/Brand");
if (response.IsSuccessStatusCode)
{
var jsonData = await response.Content.ReadAsStringAsync();
var values = JsonConvert.DeserializeObject<List<ResultBrandDto>>(jsonData);
return View(values);
}
return View();
}
}
}
AuthorizedHttpClientHandler.cs
using System.Net.Http.Headers;
namespace PresentationUI.Handlers
{
public class AuthorizedHttpClientHandler : DelegatingHandler
{
private readonly IHttpContextAccessor _httpContextAccessor;
public AuthorizedHttpClientHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var token = _httpContextAccessor.HttpContext.Request.Cookies["AuthToken"];
if (!string.IsNullOrEmpty(token))
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
return await base.SendAsync(request, cancellationToken);
}
}
}
I kindly request your support on this matter.