Why User.Identity.IsAuthenticated is always true even after logout - asp.net-mvc

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;
});

Related

ASP.NET Core MVC route based localization with areas

I have created a project in ASP.NET Core and wanted the language to be detected based on the url:
https://localhost:7090/en
If controllers and actions are used in the url, everything works as planned. However, when the registration or login page is called, it does not work (default asp.net identity registration page).
Works: https://localhost:7090/en/Home/Index
Does not work: https://localhost:7090/en/Identity/Account/Register
In the startup, I configured the following for MVC routing:
builder.Services
.AddLocalization()
.AddMvc(options => options.EnableEndpointRouting = false);
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = CultureHelper.GetSupportedCultures();
options.DefaultRequestCulture = new RequestCulturne("en");
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
var provider = new RouteDataRequestCultureProvider
{
RouteDataStringKey = "culture",
UIRouteDataStringKey = "culture",
Options = options
};
options.RequestCultureProviders = new[] { provider };
});
builder.Services.Configure<RouteOptions>(options =>
{
options.ConstraintMap.Add("culture", typeof(LanguageRouteConstraint));
});
var options = app.Services.GetService<IOptions<RequestLocalizationOptions>>();
app.UseRequestLocalization(options.Value);
app.UseMvc(routes =>
{
app.MapRazorPages();
routes.MapRoute(
name: "LocalizedDefault",
template: "{culture:culture}/{controller=Home}/{action=Index}/{id?}"
);
});
The language constraint is then used to set the CurrentCulture and CurrentUICulture.
Here is a code snippet for calling the login/register pages:
<li class="nav-item">
<a class="nav-link" asp-area="Identity" asp-page="/Account/Register">
#Language.register
</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-area="Identity" asp-page="/Account/Login">
#Language.login
</a>
</li>
I've tried pretty much everything I've found on Google, but nothing seems to work....
I just think knowing that it does not work because of the pages
[UPDATE]
I was able to get the url to be valid with the following code:
builder.Services
.AddLocalization(options => options.ResourcesPath = "Resources")
.AddMvc(options => options.EnableEndpointRouting = false)
.AddRazorPagesOptions(options =>
{
options.Conventions.Add(new LanguageRouteModelConversion());
});
public class LanguageRouteModelConversion : IPageRouteModelConvention
{
public void Apply(PageRouteModel pageRouteModel)
{
var selectorModels = new List<SelectorModel>();
foreach (var selector in pageRouteModel.Selectors.ToList())
{
var template = selector.AttributeRouteModel?.Template;
selectorModels.Add(new SelectorModel()
{
AttributeRouteModel = new AttributeRouteModel
{
Template = "/{culture}/" + template
}
});
}
foreach (var model in selectorModels)
pageRouteModel.Selectors.Add(model);
}
}
But i still don't know how to call the page properly
You said you have no Account folder in Identity directory. If so then how you do you expect register,login page router will work?
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<YourDbContextName>();
Then Scaffold Register, Login, LogOut page etc. After scaffolded, you will find a separate Account folder in Pages folder. and Your Account folder actually contains login,register page. It should resolve your issue.
builder.Services
.AddLocalization(options => options.ResourcesPath = "Resources")
.AddMvc(options => options.EnableEndpointRouting = false)
.AddRazorPagesOptions(options =>
{
options.Conventions.AddAreaFolderRouteModelConvention("Identity", "/", pageRouteModel =>
{
foreach (var selectorModel in pageRouteModel.Selectors)
selectorModel.AttributeRouteModel.Template = "{culture:culture}/" + selectorModel.AttributeRouteModel.Template;
});
});
This did the trick for me, i finally figured it out...
Add the culture param to the URL asp-route-culture=...
#{ var culture= System.Globalization.CultureInfo.CurrentCulture.Name }
<li class="nav-item">
<a class="nav-link" asp-area="Identity" asp-page="/Account/Register" asp-route-culture="#culture">
#Language.register
</a>
</li>
<li class="nav-item">
<a class="nav-link" asp-area="Identity" asp-page="/Account/Login" asp-route-culture="#culture">
#Language.login
</a>
</li>

Why does HttpContext.User.Identity.Name contain the wrong name?

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();

Redirect to another page from _Layout.cshtml

How to redirect to another page within the _layout.cshtm page in asp.net core razor.
I am doing the verification and user is logged in, if not it will be redirected to another page.
#using Microsoft.AspNetCore.Identity
#using CronoParque.Model
#inject SignInManager<ApplicationUser> SignInManager
#inject UserManager<ApplicationUser> UserManager
#if (SignInManager.IsSignedIn(User))
{
<form asp-controller="Account" asp-action="Logout" method="post" id="logoutForm" class="navbar-right">
<ul class="nav navbar-nav navbar-right">
<li>
<a asp-page="/Account/Manage/Index" title="Manage">Ola #UserManager.GetUserName(User)!</a>
</li>
<li>
<button type="submit" class="btn btn-link navbar-btn navbar-link">Sair</button>
</li>
</ul>
</form>
}
else
{
// targeting here
}
Answer
#if (SignInManager.IsSignedIn(User))
{
// normal stuff
}
else if (!Context.Request.Path.ToString().Contains("/About"))
{
// If we aren't processing a request for the target page,
// then redirect to it.
Context.Response.Redirect("/About");
}
Maybe a Better Answer
The use case for your question might be to redirect unauthorized requests to a log in page. In that case, use the built-in razor pages authorization API.
services
.AddMvc()
.AddRazorPagesOptions(options => {
options.Conventions.AuthorizePage("/Contact");
});
The above code goes into the Startup > ConfigureServices method.
In the above example, the API redirects unauthorized requests for the /Contact.
See also: https://learn.microsoft.com/en-us/aspnet/core/security/authorization/razor-pages-authorization?view=aspnetcore-2.1#require-authorization-to-access-a-page

Render Razor View to string in ASP.NET Core ignores the ASP attributes

When I run the MVC request in a usual way the ASP attributes submerged within the HTML tags are correctly interpreted.
E.g.
This is what I have in the view,
<ul class="nav navbar-nav">
<li><a asp-controller="Home" asp-action="Index">Home</a></li>
<li><a asp-controller="Home" asp-action="About">About</a></li>
<li><a asp-controller="Home" asp-action="Contact">Contact</a></li>
</ul>
This is what it translates to when it comes to the browser,
<ul class="nav navbar-nav">
<li>Home</li>
<li>About</li>
<li>Contact</li>
</ul>
Now, if I render the view through the following function,
public string RenderPartialViewToString(string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
viewName = ActionContext.ActionDescriptor.Name;
ViewData.Model = model;
using (StringWriter sw = new StringWriter())
{
var engine = Resolver.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
ViewEngineResult viewResult = engine.FindPartialView(ActionContext, viewName);
ViewContext viewContext = new ViewContext(ActionContext, viewResult.View,
ViewData, TempData, sw, new HtmlHelperOptions());
var t = viewResult.View.RenderAsync(viewContext);
t.Wait();
return sw.GetStringBuilder().ToString();
}
}
The attributes submerged within the anchor tags remain as-is, looks like they aren't interpreted by the view engine at all.
<ul class="nav navbar-nav">
<li><a asp-controller="Home" asp-action="Index">Home</a></li>
<li><a asp-controller="Home" asp-action="About">About</a></li>
<li><a asp-controller="Home" asp-action="Contact">Contact</a></li>
</ul>
I tried your RenderPartialViewToString code and everything works fine for me. My guess that when you are using RenderPartialViewToString you put your view outside from standart View folder. So please try next code
#addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
<ul class="nav navbar-nav">
<li><a asp-controller="Home" asp-action="Index">Home</a></li>
<li><a asp-controller="Home" asp-action="About">About</a></li>
<li><a asp-controller="Home" asp-action="Contact">Contact</a></li>
</ul>
So if I am right and everything works fine after that you could try add _ViewImports.cshtml file into that folder and write #addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers" there so all view in that folder or subfolders will apply same logic

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>
}

Resources