I am trying to get the current authenticated user of my web app but all I get is null for both the username and the id when I trigger my endpoint from postman.
This is what I have tried in my controller :
private readonly IHttpContextAccessor _httpContextAccessor;
public AccountController(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
[HttpGet]
public async Task<ActionResult<User>> GetUser()
{
string? username = _httpContextAccessor.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var id = _httpContextAccessor.HttpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
var user = await _userManager.Users.FirstOrDefaultAsync(o => o.Id == id);
return Ok(user);
}
I even tried to create an extension but got no luck either :
public static class ClaimsPrincipalExtension
{
public static string GetUsername(this ClaimsPrincipal user)
{
return user.FindFirst(ClaimTypes.Name)?.Value;
}
public static string GetUserId(this ClaimsPrincipal user)
{
return user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
}
}
This is my Program.cs (In case I am missing something in the services or middlewares) :
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddIdentityServices(builder.Configuration);
builder.Services.AddApplicationServices(builder.Configuration);
//Handling json:
builder.Services.AddControllers()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
builder.Services.Configure<IdentityOptions>(options =>
options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier);
builder.Services.AddHttpContextAccessor();
builder.Services.ConfigureApplicationCookie(options =>
{
options.Events.OnRedirectToAccessDenied =
options.Events.OnRedirectToLogin = context =>
{
if (context.Request.Method != "GET")
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
return Task.FromResult<object>(null);
}
context.Response.Redirect(context.RedirectUri);
return Task.FromResult<object>(null);
};
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/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.UseCors(builder =>
{
builder.WithOrigins("https://localhost:4200")
.AllowCredentials()
.AllowAnyMethod()
.AllowAnyHeader();
});
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.Run();
And here are my Identity and Application extensions :
public static class ApplicationExtensions
{
public static IServiceCollection AddApplicationServices(this IServiceCollection services, IConfiguration config)
{
services.AddDbContext<DataContext>(opt =>
{
object value = opt.UseSqlServer(config.GetConnectionString("DefaultConnection"));
opt.EnableSensitiveDataLogging();
});
services.AddCors();
services.AddScoped<ITokenService, TokenService>();
services.AddHttpContextAccessor();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<LogUserActivity>();
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
return services;
}
}
public static class IdentityExtensions
{
public static IServiceCollection AddIdentityServices(this IServiceCollection services, IConfiguration config)
{
services.AddIdentity<User, IdentityRole>(
opt =>
{
opt.Password.RequireDigit = true;
opt.Password.RequireLowercase = true;
opt.Password.RequireNonAlphanumeric = true;
opt.Password.RequireUppercase = true;
opt.Password.RequiredLength = 4;
opt.User.RequireUniqueEmail = true;
}
).AddEntityFrameworkStores<DataContext>();
services.AddLogging();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddNegotiate()
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(config["Token:Key"])),
ValidIssuer = config["Token:Issuer"],
ValidateIssuer = false,
ValidateAudience = false
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
{
context.Token = accessToken;
}
return Task.CompletedTask;
},
};
});
services.ConfigureApplicationCookie(options =>
{
options.AccessDeniedPath = "/user/access_denied";
options.Cookie.Name = "leo-coin";
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
options.LoginPath = "/user/login";
options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
options.SlidingExpiration = true;
});
return services;
}
}
I just found out why I was getting null from the claimsPrincipal. I had to replace : AddIdentity<AppUser, IdentityRole> with AddIdentityCore and then add AddRoles. The code of the identity extension should look like this :
public static IServiceCollection AddIdentityServices(this IServiceCollection services,
IConfiguration config)
{
services.AddIdentityCore<AppUser>(opt =>
{
opt.Password.RequireNonAlphanumeric = false;
})
.AddRoles<IdentityRole>()
.AddRoleManager<RoleManager<IdentityRole>>()
.AddEntityFrameworkStores<DataContext>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding
.UTF8.GetBytes(config["Token:Keys"])),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddAuthorization(opt =>
{
opt.AddPolicy("RequireAdminRole", policy => policy.RequireRole("Admin"));
opt.AddPolicy("ModeratePhotoRole", policy => policy.RequireRole("Admin", "Moderator"));
});
return services;
}
}