I am trying to explore on the Identity token based authentication in .NET core 8 Web API app. I was able to register user, generate & store token. However when I tried to access authorized api it's failing, redirecting to the /Account/login endpoint. Note I have added [Authorize] attr for my own API/Action. Based on my analysis it's redirecting to /Account/Login because API doesn't have this endpoint , hence it is throwing 404 instead of 401. My concern is why it is even failing to Authenticate.
I tried multiple configurations to get it work but none is helping, Please guide here.
N.B I am successfully able to use Jwt token, I want to use the default Identity for token management rather than JWT. Referenced docs but I am missing it somewhere.
Code: Prog
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<ApplicationDbContext>(
options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))
);
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.SignIn.RequireConfirmedAccount = false;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders(); // Ensures to use default AspNet token providers
builder.Services.AddAuthentication()
.AddBearerToken(IdentityConstants.BearerScheme); // tried few options but same error
builder.Services.AddAuthorization();
/* This is working as expected
var issuer = builder.Configuration.GetSection("Jwt:Issuer").Get<string>();
var audience = builder.Configuration.GetSection("Jwt:Audience").Get<string>();
var secretKey = builder.Configuration.GetSection("Jwt:SecretKey").Get<string>();
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 = issuer,
ValidAudience = audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey))
};
});
*/
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddScoped<IJwtService, JwtService>();
builder.Services.AddScoped<IAspTokenService, AspTokenService>();
builder.Services.AddControllers();
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
db.Database.Migrate();
}
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Controller:
[HttpPost("register")]
[AllowAnonymous]
public async Task<ActionResult> Register([FromBody] RegisterModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var appUser = new ApplicationUser() { UserName = model.UserId, Email = model.Email, NewAddress = model.Address };
var result = await _userManager.CreateAsync(appUser, model.Password);
if (result.Succeeded)
{
//var token = _jwtService.GenerateToken(model.UserId);
var token = await _aspTokenService.GenerateAndStoreTokenAsync(appUser.UserName);
return Ok(token);
}
return BadRequest(result.Errors);
}
[HttpPost("login")]
[AllowAnonymous]
public async Task<ActionResult> Login([FromBody] LoginModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = await _userManager.FindByNameAsync(model.UserId);
if (user != null && await _userManager.CheckPasswordAsync(user, model.Password))
{
//var token = _jwtService.GenerateToken(user.UserName);
var token = await _aspTokenService.GenerateAndStoreTokenAsync(user.UserName);
return Ok(token);
}
return Unauthorized();
}
[HttpGet("securedata")]
[Authorize]
public ActionResult GetSecureData()
{
string token = string.Empty;
var usersCliams = User.Identity as ClaimsIdentity;
foreach (var claim in usersCliams?.Claims)
{
if ("Id".Equals(claim.Type))
{
token = claim.Value.ToString();
break;
}
}
return Ok(new { Message = $"Id {token}" });
}
Service
private UserManager<ApplicationUser> _userManager;
public AspTokenService(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task<TokenDto> GenerateAndStoreTokenAsync(string username)
{
if (string.IsNullOrEmpty(username))
{
return null;
}
var user = await _userManager.FindByNameAsync(username);
if (user == null)
{
return null;
}
var token = await _userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultProvider, user.Id);
await _userManager.SetAuthenticationTokenAsync(user, TokenOptions.DefaultProvider, user.Id, token);
return new TokenDto { Token = token };
}
public async Task<TokenDto> GetTokenAsync(string username)
{
if (string.IsNullOrEmpty(username))
{
return null;
}
var user = await _userManager.FindByNameAsync(username);
if (user == null)
{
return null;
}
var token = await _userManager.GetAuthenticationTokenAsync(user, TokenOptions.DefaultProvider, user.Id);
return new TokenDto { Token = token };
}
Other
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
}
public class ApplicationUser : IdentityUser
{
public string? NewAddress { get; set; }
}
DB table mapping - check user Manu123456, ignore other two
In .net 8, there are built-in jwt endpoints now for asp.net identity.
Install packageMicrosoft.AspNetCore.Identity.EntityFrameworkCore
Instead of AddIdentity
,try use following codes
builder.Services.AddIdentityApiEndpoints<IdentityUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddSignInManager()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddDefaultTokenProviders();
...
app.MapIdentityApi<IdentityUser>();
Reference: https://andrewlock.net/exploring-the-dotnet-8-preview-introducing-the-identity-api-endpoints/
AddIdentity
is commonly used for MVC project.