Why does HttpContext.User.Identity.Name contain the wrong name? - asp.net-mvc

I have a .NET 5 MVC application that periodically checks access based on the HttpContext.User.Identity.Name value. The app uses Azure AD. Sometimes as I switch between users (Signing out and Signing in), the HttpContext.User.Identity.Name value will contain the previous user's name and the current user will have the access of the previous user.
Even more strange is the the login shown is correct.
This is just the boiler plate code generated by visual studio:
#using System.Security.Principal
<ul class="navbar-nav">
#if (User.Identity.IsAuthenticated)
{
<li class="nav-item">
<span class="navbar-text text-light">Hello #User.Identity.Name!</span>
</li>
<li class="nav-item">
<a class="nav-link text-light" asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="SignOut">Sign out</a>
</li>
}
else
{
<li class="nav-item">
<a class="nav-link text-light" asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="SignIn">Sign in</a>
</li>
}
</ul>
So #User.Identity.Name on the .cshtml is always correct, but HttpContext.User.Identity.Name on the controller is the previous (cached?) user.
Any clues to why the previous user gets cached?
Here is a sample of how I call it on the controller:
ViewData["User"] = HttpContext.User;
ViewData["Menu"] = await _menuService.GetReportMenuAsync(MenuService.ReportMenuItemOption.Genesis01, User.Identity.Name);
var userAccess = await _userAccessService.GetUserEntityAccessAsync(User.Identity.Name);
var model = new SomeModel
{
SearchDate = DateTime.Now.AddDays(-7),
Banks = userAccess.Banks,
ATMIds = userAccess.ATMs
};
return View(model);
This is in my StartUp:
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();

Related

Why User.Identity.IsAuthenticated is always true even after logout

I am working on AspnetCore 2.1 MVC application , it has windows integrated authentication. I have implemented login, logout functionality using await HttpContext.SignInAsync(). it is cookie bases authentication, the cookie expires after specified time I provide.
My issue is I have to hide logout from nav bar after user logs out. but in _layout.cshtml , #User.Identity.Is authenticated always true even after user hits logout. Any help ? below is the code I am using
<div class="navbar-collapse collapse">
#if(User.Identity.IsAuthenticated)
{
<ul class="nav navbar-nav">
<li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
<li><a asp-area="" asp-controller="InitiatedCases" asp-action="Index">Initiated Cases</a></li>
<li><a asp-area="" asp-controller="SubmittedCases" asp-action="Index">Submitted Cases</a></li>
<li><a asp-area="" asp-controller="UserAccessLogs" asp-action="Index">User Access Log</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a asp-controller="Account" asp-action="Logout">Logout</a></li>
<li><p class="nav navbar-text navbar-right">#User.Identity.Name!</p></li>
</ul>
}
else
{
<p></p>
}
</div>
below is login functionality
var usrRole = _dbContext.UserAccess.Where(r => r.UserId == loginUser.UserId).FirstOrDefault();
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.Name, loginUser.UserId.ToString()));
identity.AddClaim(new Claim("DisplayName", loginUser.UserId));
if (usrRole != null)
{
identity.AddClaim(new Claim(ClaimTypes.Role, usrRole.UserRole.ToString()));
}
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(identity),
new AuthenticationProperties
{
IsPersistent = true,
IssuedUtc = DateTime.Now,
ExpiresUtc = DateTime.Now.AddMinutes(_iconfiguration.GetValue<double>("Session:TimeOutInMinutes")),
AllowRefresh = false
});
I think you may be using windows integrated authentication - although i am not sure.
The browser uses Windows Integrated Authentication - which means it automatically logs the user in using their windows credentials - without asking.
That's why IsAuthenticated is always true.
Additional thoughts:
I have not tried below code, but there is something called as IISServerOptions.
You can try them to check if that works for you.
services.Configure<IISServerOptions>(options =>
{
options.AutomaticAuthentication = false;
});

Dynamic navbar using _LoginPartial with .NET MVC

I have been trying to get this to work but doesn't seem to work. Basically I need a dynamic navbar. The pre-built .NET template actually has already generated a _LoginPartial.cshtml, which already changes the navbar depending on whether a user is login or not. I have been trying to edit the navbar so that it supports another level of change to check if a user is on a specific page then it will show more or less items depending on whether a boolean (showAll) is set to true or false. However is there a way to set the boolean based on the page? Currently I test it by changing manually and the logic works. I need a dynamic way to control it programmatically. Thank you for your help!
#using Microsoft.AspNetCore.Identity
#inject SignInManager<IdentityUser> SignInManager
#inject UserManager<IdentityUser> UserManager
#{bool showAll = false;}
#if (SignInManager.IsSignedIn(User) && showAll == true)
{
<ul class="nav navbar-nav mr-auto">
<li class="nav-item"><a asp-area="" asp-controller="Home" asp-action="Index" class="nav-link">Home</a></li>
<li class="nav-item"><a asp-area="" asp-controller="Home" asp-action="About" class="nav-link">About</a></li>
<li class="nav-item"><a asp-area="" asp-controller="Home" asp-action="Contact" class="nav-link">Contact</a></li>
</ul>
<form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="#Url.Action("Index", "Home", new { area = "" })" method="post" id="logoutForm" class="navbar-right">
<ul class="nav navbar-nav navbar-right">
<li class="nav-item">
<a asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage" class="nav-link">Hello #UserManager.GetUserName(User)!</a>
</li>
<li class="nav-item">
<button type="submit" class="btn btn-link navbar-btn navbar-link">Logout</button>
</li>
</ul>
</form>
}
else if (SignInManager.IsSignedIn(User) && showAll == false)
{
<ul class="nav navbar-nav mr-auto">
<li class="nav-item"><a asp-area="" asp-controller="Home" asp-action="Index" class="nav-link">Home</a></li>
<li class="nav-item"><a asp-area="" asp-controller="Home" asp-action="About" class="nav-link">About</a></li>
</ul>
<form asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="#Url.Action("Index", "Home", new { area = "" })" method="post" id="logoutForm" class="navbar-right">
<ul class="nav navbar-nav navbar-right">
<li class="nav-item">
<a asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage" class="nav-link">Hello #UserManager.GetUserName(User)!</a>
</li>
<li class="nav-item">
<button type="submit" class="btn btn-link navbar-btn navbar-link">Logout</button>
</li>
</ul>
</form>
}
else
{
<ul class="nav navbar-nav mr-auto">
<li class="nav-item"><a asp-area="" asp-controller="Home" asp-action="Index" class="nav-link">Home</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li class="nav-item"><a asp-area="Identity" asp-page="/Account/Register" class="nav-link">Register</a></li>
<li class="nav-item"><a asp-area="Identity" asp-page="/Account/Login" class="nav-link">Login</a></li>
</ul>
}
It depends a lot on what logic is behind that variable. If you can resolve it just from the login partial, you can certainly just add the logic there. For example, you could look at the current route to figure out whether you want that value to be true or not.
You could also build your own service that you inject there which then contains the logic. This can be useful if you end up doing more complicated stuff, as you should generally try to avoid too much logic inside views.
If the value is being set outside of the partial, you could also just use the ViewData dictionary to set it. This would allow you to set the value within your controllers, and then your partial view could just retrieve the value. Something like this:
#{
bool showAll = false; // default
if (ViewData.TryGetValue("NavigationShowAll", out var value) && value is bool)
{
showAll = (bool)value;
}
}
And then, in your controller, you can just do this:
public IActionResult DoSomething()
{
ViewData["NavigationShowAll"] = true;
return View();
}

Trying to create drop down login for Orchard CMS

I am new to Orchard and to ASP.NET MVC. I am trying to override the login process for users so that instead of going to a dedicated login page (the default process) they get a drop down form from the user menu that logs them in. I have created overrides in my custom theme for LogOn.cshtml and User.cshtml. The code is as follows:
//User.cshtml
#using System.Web.Mvc;
#using Orchard.ContentManagement;
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<ul class="nav navbar-nav pull-right">
#if (WorkContext.CurrentUser != null) {
<li class="dropdown">
<button id="userDropdown" class="btn btn-primary navbar-btn dropdown-toggle" data-toggle="dropdown">
Welcome #Html.ItemDisplayText(WorkContext.CurrentUser) <span class="caret"></span>
</button>
<ul class="dropdown-menu">
<li>
#Html.ActionLink(T("Change Password").ToString(), "ChangePassword", new { Controller = "Account", Area = "Orchard.Users" })
</li>
<li>
#Html.ActionLink(T("Sign Out").ToString(), "LogOff", new { Controller = "Account", Area = "Orchard.Users", ReturnUrl = Context.Request.RawUrl }, new { rel = "nofollow" })
</li>
#if (AuthorizedFor(Orchard.Security.StandardPermissions.AccessAdminPanel)) {
<li class="divider"></li>
<li>
#Html.ActionLink(T("Dashboard").ToString(), "Index", new { Area = "Dashboard", Controller = "Admin" })
</li>
}
</ul>
</li>
}
else {
<li class="dropdown">
<button class="btn btn-primary navbar-btn dropdown-toggle" data-toggle="dropdown">
Sign In <span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li>
#Display.LogOn()
</li>
</ul>
</li>
}
</ul>
</div>
//LogOn.cshtml
#using System.Web.Mvc;
#using Orchard.ContentManagement;
<div>
#using (Html.BeginFormAntiForgeryPost(Url.Action("LogOn", new { ReturnUrl = Request.QueryString["ReturnUrl"] }))) {
<fieldset class="login-form group">
<legend>#T("Account Information")</legend>
<div class="form-group">
<label for="username-email">#T("Username")</label>
#Html.TextBox("userNameOrEmail", "", new { id = "username-email", autofocus = "autofocus", #class = "validate[required]" })
#Html.ValidationMessage("userNameOrEmail")
</div>
<div class="form-group">
<label for="password">#T("Password")</label>
#Html.Password("password", null, new { #class = "validate[required]" })
#Html.ValidationMessage("password")
</div>
<div class="checkbox">
<label class="forcheckbox" for="remember-me">
#Html.CheckBox("rememberMe", new { id = "remember-me" }) #T("Remember Me")
</label>
</div>
<div class="form-group">
<button class="primaryAction" type="submit">#T("Sign In")</button>
</div>
</fieldset>
}
</div>
The form displays fine in the dropdown, but when the submit button is clicked, I get a HTTP 404 error: Requested URL: /OrchardLocal/Contents/Item/LogOn.
I'm afraid that the default login process requires a separate view. I've been trying to figure out which existing override to use, but I'm just not seeing it. Any help would be greatly appreciated. Thanks.
You should be able to login properly by changing your form from
#using (Html.BeginFormAntiForgeryPost(Url.Action("LogOn", new { ReturnUrl = Request.QueryString["ReturnUrl"] })))
to something like this:
#using ( Html.BeginForm("LogOn", "Account", new { area = "Orchard.Users" /*other route values here*/ }, FormMethod.Post) )
{
#Html.AntiForgeryToken()
// ... your stuff here
}
This is the same for every other controller action. If in doubt: specify the area explicitly.

unable to call LogOff method in controller

I have started a project using MVC6 and can't seem to route to the LogOff method.
The account controller looks like this:
//
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> LogOff()
{
await _signInManager.SignOutAsync();
_logger.LogInformation(4, "User logged out.");
return RedirectToAction(nameof(HomeController.Index), "Home");
}
The calling navigation looks like this:
#if (User.IsSignedIn())
{
<div class="profile-picture">
<div class="stats-label text-color">
<span class="font-extra-bold font-uppercase">#User.GetUserName()</span>
<div class="dropdown">
<a class="dropdown-toggle" href="#" data-toggle="dropdown">
<small class="text-muted">Title <b class="caret"></b></small>
</a>
<ul class="dropdown-menu animated flipInX m-t-xs">
<li>Log off</li>
<li>Link 2</li>
<li>Link 3</li>
</ul>
</div>
</div>
</div>
}
I have applied a break point in the on the first line of the method in the controller however it never breaks on it and the browser just goes blank.
What am I doing wrong?
Instead of
<li>Log off</li>
Try replacing like this:
#using (Html.BeginForm("LogOff", "Account", FormMethod.Post, new { id = "logoutForm" }))
{
#Html.AntiForgeryToken()
<li>Log off</li>
}

Database update notification using SignalR and Entity Framework

I'm trying to make a website using asp.net mvc 4 and EF 6 where I want to update the messagebox of adminpanel if any user sends him any message. I'm using SignalR for live notification but I'm new to SignalR and for some reason I don't get the updates live, I've to refresh the page to get the newly added message. Here are my codes,
Hub Class
public class ChatHub : Hub
{
MyProj.Models.MsgToOwner messages = new MyProj.Models.MsgToOwner();
public void SendNotify(string username, string title, string datetimeNow)
{
var newName = messages.name;
var newTitle = messages.title;
var newTime = messages.addedtime;
IHubContext context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
context.Clients.All.getNotify(newName, newTitle, newTime);
}
}
View for All Messages
#{
var MsgList = (List<MyProj.Models.MsgToOwner>)ViewBag.MsgList;
}
<body>
<script src="~/Scripts/jquery.signalR-1.1.4.min.js"></script>
<script src="~/signalr/hubs"></script>
<script src="~/Scripts/LiveScript.js"></script>
<ul class="nav navbar-top-links navbar-right">
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-envelope fa-fw"></i><i class="fa fa-caret-down"></i>
</a>
<ul class="dropdown-menu dropdown-messages" id="newItem">
#foreach (var item in MsgList)
{
<li>
<a href="#">
<div>
<strong>#Html.DisplayFor(modelItem => item.title)</strong>
<span class="pull-right text-muted">
<em>#Html.DisplayFor(modelItem => item.addedtime)</em>
</span>
</div>
<div>Sent By : <strong>#Html.DisplayFor(modelItem => item.name)</strong></div>
</a>
</li>
<li class="divider"></li>
}
</ul>
</li>
</ul>
</body>
LiveScript.js (script for updating from SignalR)
$(function () {
var chat = $.connection.chatHub;
chat.client.getNotify = function (username, title, datetimeNow) {
$('#newItem').append(
'<li><div><strong>' + htmlEncode(title) + '</strong><span class="pull-right text-muted"><em>' + htmlEncode(datetimeNow) + '</em></span></div><div>Sent By : <strong>' + htmlEncode(username) + '</strong></div></li><li class="divider"></li>');
};
$.connection.hub.start().done(function () {
$('#btnSend').click(function () {
chat.server.SendNotify($('#getName').val(), $('#getTitle').val(), $('#getDate').val());
});
});
});
I must be doing something wrong here but I can't figure it out since I'm new to SignalR. How can I update the messagebox whenever a new message is saved in db? Any guidance will be helpful for me. Thanks.

Resources