HttpContext.SignInAsync sets null to Identity claims data
fabercs opened this issue · comments
Describe the bug
In my project, I want to make use of the cookie based authentication without using the identity infrastructure. I have my own user,role and claim tables. So I was following this.
Our project is in a tfs repo. When any member in the team tries to run the app, we always get a exception caused from HttpContext.User.Identity is filled up with all null
values. But when debug it or run it for several times, mostly values somehow filled up, or not. Details below;
To Reproduce
Steps to reproduce the behavior:
- Using this version of ASP.NET Core 2.1
I have a middleware where I get a header value and and depend on that value get user data, then set these user data to Identity object.
UserSessionMiddleware
public async Task Invoke(HttpContext context, IUserService userService, ISessionHelper sessionHelper,
ILogger<UserSessionMiddleware> logger)
{
if (sessionHelper.IsAppSessionSet)
await _next.Invoke(context);
else
{
var employeeId = context.Request.Headers["headerVal"].ToString();
UserEntity user = userService.GetUserData(employeeId);
List<ClaimEntity> roleClaims = userService.GetUserRoleClaims(user.UserRoles.Select(ur => ur.RoleId).ToArray()).ToList();
if (user == null)
{
await context.Response.WriteAsync($"User with Id: {employeeId} not found in database!");
return;
}
#region Create user claims
ClaimsIdentity claimsIdentity = new ClaimsIdentity(CreateUserClaims(user, roleClaims),
CookieAuthenticationDefaults.AuthenticationScheme);
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
await context.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal,
new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTime.UtcNow.AddDays(7) });
#endregion
sessionHelper.User = user;
sessionHelper.IsAppSessionSet = true;
var sessionId = context.Session.Id;
logger.LogInformation($"SessionId: {sessionId}, {user.EmployeeId}/{user.FullName} has started session");
await _next.Invoke(context);
}
}
As the request reaches to the view component where to get user data and bind to the view elements, it throws null object exception. In fact when I inspect the context's User.Identity
object after context.SignInAsync
, I can see that a new object sits there however all values are null, as seen in the ss.
public class ProfileComponent : ViewComponent
{
private ISessionHelper _sessionHelper;
private IHttpContextAccessor _contextAccessor;
public ProfileComponent(ISessionHelper sessionHelper, IHttpContextAccessor contextAccessor)
{
_sessionHelper = sessionHelper;
_contextAccessor = contextAccessor;
}
public IViewComponentResult Invoke()
{
ClaimsIdentity identity = (ClaimsIdentity)_contextAccessor.HttpContext.User.Identity;
dynamic user = new ExpandoObject();
//this line throws exception
user.EmployeeId = identity.FindFirst(ClaimTypes.NameIdentifier).Value;
user.Name = identity.Name;
user.Email = identity.FindFirst(ClaimTypes.Email).Value;
user.Roles = identity.FindAll(ClaimTypes.Role).Select(i => i.Value).ToList();
ViewBag.User = user;
return View();
}
}
Expected behavior
Expected behavior is to get claims data in view component properly and constantly. For now this code is working on my computer, and same not working on my firend's.
I guess the User in HttpContext is not being set yet in ongoing request. It is filled up on subsequent requests, I saw that by changing the last line in UserSessionMiddleware
await _next.Invoke(context);
to
context.Response.Redirect("Home")
If this is the case, is above approach a sensible way to make this work (making a 302 request) for my case?
Indeed, SignIn only affects future requests. You could set HttpContext.User yourself for the current request.
got it, simply solved, thank you!!