Returning active user from IdentityServer4 when login to the MVC .netaspcore client - asp.net-core

I'm sure someone has asked this question or its something easy that i'm just struggling with.
I have going their the Identity Server docs and I've been able to setup as server, api, and MVC Client.
I'm able to login to the MVC Client using the Identity Server.
I'm trying to add currently login user and logout to a _loginPartial this should prove I can authorize view based on login as well. But I keep running into errors that UserManger is not doesn't have type registered.
any help or even a link to and example would be appreciated.

Based on the IdentityServer4 mvc client sample code, you could get current user from user claims.To display all the claims, you could use
#foreach (var claim in User.Claims)
{
<dt>#claim.Type</dt>
<dd>#claim.Value</dd>
}
Try to use below code in _loginPartial.cshtml to get Name claim of user
<ul class="navbar-nav">
#if (User.FindFirst("Name").Value != null)
{
<li class="nav-item">
<a> Hello #User.FindFirst("Name").Value !</a>
</li>
<li class="nav-item">
<form id="logoutForm" class="form-inline" asp-area="" asp-controller="Home" asp-action="Logout"">
<button id="logout" type="submit" class="nav-link btn btn-link text-dark">Logout</button>
</form>
</li>
}
</ul>
Logout action:
public IActionResult Logout()
{
return SignOut("Cookies", "oidc");
}

Related

How to delete from a database in Ajax?

I'm sure there are similar questions, but I searched both here and on Google and found no answer.
I have a list of products in the database. And I have a method that delete the current product by button. And it works great!
The problem starts when I try to make the button not POSTBACK
I know the problem is with the RETURN but if I do not write it, I have an error message because my method is - async Task
For what I need to change the method so that it works without changing the URL / postback?
index.cshtml-
<ul class="list-group list-group-lg list-group-flush list my--4">
#foreach (var prod in Model.ProductList)
{
<li class="list-group-item px-0">
<div class="row align-items-center">
<div class="col-auto">
<span class="avatar avatar-lg">
תמונה
</span>
</div>
<div class="col ml--2">
<h4 class="card-title mb-1">
<a href="#!">
#Html.DisplayFor(modelItem => prod.ProductName)
</a>
</h4>
<p class="card-text small text-muted mb-1">
אימייל
</p>
</div>
<div class="col-auto">
<form method="post">
<input type="submit" asp-page-handler="Delete" value="Delete" asp-route-id="#prod.Id" data-ajax="true" data-ajax-success="deleteItem(this)" />
</form>
</div>
</div>
</li>
}
Index.cshtml.cs-
public async Task<IActionResult> OnPostDeleteAsync(int id)
{
Product currentProduct = new Product();
foreach (var item in _context.Products)
{
if (item.Id==id)
{
currentProduct = item;
break;
}
}
_context.Products.Remove(currentProduct);
_context.SaveChanges();
return RedirectToPage("./Index");
}
Based on your code, we can find that it will return HTTP 302 redirect response status code while you make AJAX request from client side. Please note that AJAX can not automatically redirect to that page, normally we do not return a redirect response to AJAX client.
As #schwartz721 mentioned, if possible, you can try to modify the handler to return JsonResult or OkResult etc rather than RedirectToPage, then you can handle response in success callback.
Besides, if you indeed want to do AJAX submission and redirect user to index page after user delete a product, you may need to do redirection on client side, like below.
window.location.href = url_here;

How to keep the tab active? ASP.NET Core Razor Pages

Hi there,
I have a problem with my tabs in navbar.
The result I wish:
See the "Home" tab class active by default
Clicking on another tab must to remove active class from the <a> that is already active (at the starting point it is "Home" tab)
Add the same class active to the tab I click on (which also mean that app redirects me)
Keep the clicked tab active
What I've got so far:
I see the "Home" tab set active by default
Clicking on any tab removes class active as I mentioned above and adds the same class to the clicked tab
Redirection happens and "Home" tab returns its default state (becomes active again)
I share my code below:
HTML
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse" id="navigation">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link active" id="home" asp-area="" asp-page="/home/Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" id="declarations" asp-area="" asp-page="/declarations/Index">Declarations</a>
</li>
<li class="nav-item">
<a class="nav-link" id="topics" asp-area="" asp-page="/topics/Index">List of Topics</a>
</li>
<li class="nav-item">
<a class="nav-link" id="contacts" asp-area="" asp-page="/contacts/Index">Contacts</a>
</li>
<li class="nav-item">
<a class="nav-link" id="faq" asp-area="" asp-page="/faq/Index">FAQ</a>
</li>
</ul>
</div>
jQuery
$("#navigation .navbar-nav a").click(function() {
$("#navigation .navbar-nav").find("a.active").removeClass("active");
$(this).addClass("active");
localStorage.className = "active";
});
$(document).ready(function() {
stayActive();
});
function stayActive() {
$(this).children().addClass(localStorage.className);
}
Since you are using Razor page, everytime user clicks on a tab, the page will be rendered again on server and then sent back. Therefore I suggest that we set the active class on serverside based on the current route.
Add a new HtmlHelper class
public static class HtmlHelperExtensions
{
public static string ActiveClass(this IHtmlHelper htmlHelper, string route)
{
var routeData = htmlHelper.ViewContext.RouteData;
var pageRoute = routeData.Values["page"].ToString();
return route == pageRoute ? "active" : "";
}
}
Go to your _ViewImports.cshtml and add this import.
Without this, it will not recognize the helper we're about to add in step 3.
#using <Namespace of the class you wrote in step 1, if not already here>
Then your cshtml will be
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse" id="navigation">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link #Html.ActiveClass("/home/Index")" id="home" asp-area="" asp-page="/home/Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link #Html.ActiveClass("/declarations/Index")" id="declarations" asp-area="" asp-page="/declarations/Index">Declarations</a>
</li>
...Same for 3 other tabs, make sure that the route you pass in ActiveClass method
...must be the same as the route in asp-page
...If you're afraid of duplication, make some static class to store these routes as constants
</ul>
</div>
In addition to #Nam Le answer I suggest complete solution.
I've found the way how can we use the exception properly.
First of all, as we set active class on serverside based on the current route, we have to add asp-route-routeId="{routeId:int}" in the cshtml we work with.
Secondly, use this jQuery code:
jQuery
$("#navigation .navbar-nav a").click(function() {
$("#navigation .navbar-nav").find("a.active").removeClass("active");
$(this).addClass("active");
localStorage.className = "active";
});
$(document).ready(function() {
stayActive();
});
function stayActive() {
$(this).addClass(localStorage.className);
}
And the last step is to setup "launchUrl": "route/routeId" in the launchSetting.json file to run the application with certain route. This action make our HtmlHelper extension react and add active status to the tab we want.
Hope that here is no any gross mistakes in the solution I suggest :)
UPD: don't forget to add route in cshtml #page "/route/routeId"

How do you display a users full name in the login partial view in ASP.NET Core

I'm new to ASP.NET Core, most of my existing experience is with Java/PHP, and a little ASP.NET MVC about 10 years ago.
I'm using a Mac so I'm trying to build a project using Visual Studio Code and ASP.NET Core.
I've created a web application using 'yo'. I've changed the default database connection string to use an MSSQL database I have on a server. I've ran the dotnet ef database update command to create the necessary tables in the database.
I wanted to add firstname and lastname to the user, so I've created the columns in the AspNetUser table, and edited the ApplicationUser class to reflect this;
namespace pabulicms.Models
{
// Add profile data for application users by adding properties to the ApplicationUser class
public class ApplicationUser : IdentityUser
{
public string Firstname { get; set; }
public string Lastname { get; set; }
}
}
I've gone ahead and amended the view model for the registration form to include the firstname and lastname, and I've updated the Signup method so that the firstname and lastname is saved to the database.
By default the _LoginPartial.cshtml view displays the users username(email address), I'd like to change this to display the users full name, but I'm unsure as to how I do this.
This is what the _LoginPartial looks like at the moment;
#using Microsoft.AspNetCore.Identity
#using pabulicms.Models
#inject SignInManager<ApplicationUser> SignInManager
#inject UserManager<ApplicationUser> UserManager
#if (SignInManager.IsSignedIn(User))
{
<form asp-area="" asp-controller="Account" asp-action="Logout" method="post" id="logoutForm" class="navbar-right">
<ul class="nav navbar-nav navbar-right">
<li>
<a asp-area="" asp-controller="Manage" asp-action="Index" title="Manage">Hello #UserManager.GetUserName(User)!</a>
</li>
<li>
<button type="submit" class="btn btn-link navbar-btn navbar-link">Log out</button>
</li>
</ul>
</form>
}
else
{
<ul class="nav navbar-nav navbar-right">
<li><a asp-area="" asp-controller="Account" asp-action="Register">Register</a></li>
<li><a asp-area="" asp-controller="Account" asp-action="Login">Log in</a></li>
</ul>
}
It's obviously this line I need to change;
Hello #UserManager.GetUserName(User)!
However changing it to #UserManager.GetFirstname(User)! doesn't work, as it tells me that the method GetFirstname doesn't exist;
I searched a lot and finally found this solution:
Change this:
#UserManager.GetUserName(User)
To this: #UserManager.GetUserAsync(User).Result.LastName
refer to: Link
ASP.NET Core Identity library uses claims-based approach to Authorization. It means that a logged in user (the one you can access via User object in your views) has some list of claims (name-value pairs) associated with it.
By default, that list contains two claims: for ID and username.
However, it's easy to add to that list any other claim you need (first/last name, the name of the company, current user's balance, etc.).
You will just need to create your own implementation of IUserClaimsPrincipalFactory interface and register it in DI to override the default one.
Here is the article with a step-by-step description how to do it.
You can skip the first part ("zero" part to be more exact) "Preparations" if you already have the additional properties (like FirstName/LastName) in your ApplicationUser class.
In the end I used a view component.
First I created a view component myprojectname/ViewComponents/AccountStatusViewComponent.cs
namespace myprojectname.ViewComponents
{
public class AccountStatusViewComponent : ViewComponent
{
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly UserManager<ApplicationUser> _userManager;
public AccountStatusViewComponent(SignInManager<ApplicationUser> signInManager, UserManager<ApplicationUser> userManager)
{
_signInManager = signInManager;
_userManager = userManager;
}
public async Task<IViewComponentResult> InvokeAsync()
{
AccountStatusModel model = new AccountStatusModel();
model.User = _userManager.GetUserAsync(Request.HttpContext.User).Result;
return View(model);
}
}
}
Next I created the view for the view component myprojectname/Views/Shared/Components/AccountStatus/Default.cshtml
#model AccountStatusModel
#using Microsoft.AspNetCore.Identity
#using myprojectname.Models.ViewComponentModels;
#using myprojectname.Models
#inject SignInManager<ApplicationUser> SignInManager
#inject UserManager<ApplicationUser> UserManager
#if (SignInManager.IsSignedIn(User))
{
<form asp-area="" asp-controller="Account" asp-action="Logout" method="post" id="logoutForm" class="navbar-right">
<ul class="nav navbar-nav navbar-right">
<li>
<a asp-area="" asp-controller="Manage" asp-action="Index" title="Manage">Hello #Model.User.Firstname #Model.User.Lastname</a>
</li>
<li>
<button type="submit" class="btn btn-link navbar-btn navbar-link">Log out</button>
</li>
</ul>
</form>
}
else
{
<ul class="nav navbar-nav navbar-right">
<li><a asp-area="" asp-controller="Account" asp-action="Register">Register</a></li>
<li><a asp-area="" asp-controller="Account" asp-action="Login">Log in</a></li>
</ul>
}
I created a model to hold the data I wanted passing the view in myprojectname/Models/ViewComponentModels/AccountStatusModel.cs
using System;
namespace pabulicms.Models.ViewComponentModels
{
public class AccountStatusModel
{
public ApplicationUser User { get; set; }
}
}
Finally, in the example .NET website I edited the file Views/Shared/_Layout.cshtml and replaced this line;
#await Html.PartialAsync("_LoginPartial")
With;
#await Component.InvokeAsync("AccountStatus")
my simple way :
top of view
#{
#inject UserManager<ApplicationUser> UserManager;
var DisplayName= UserManager.Users.FirstOrDefault(m=>m.UserName==User.Identity.Name).FullName;
}
Hello #DisplayName
ofcourse may it's not be a best way with best performance but worked for me.

Web API authentication turns request.authentication false

I have a project that I created in ASP.NET MVC and now the second part of the work is pass the logic of the ASP.NET MVC application (basically the database) to an ASP.NET Web API and do the connection with them.
The thing is, I already did the connection and I already save values in the Web API database, but a strange thing is happening.
I have markup in my layout.cs.html file:
<body>
#if (User.Identity.IsAuthenticated)
{
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
#Html.ActionLink("MSDiary", "Index", "Home", new { area = "" }, new { #class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>#Html.ActionLink("Despesas", "Index", "Despesas")</li>
<li>#Html.ActionLink("Rendimentos", "Index", "Rendimentos")</li>
<li>#Html.ActionLink("Tipos de Despesa", "Index", "TipoDespesas")</li>
<li>#Html.ActionLink("Tipos de Pagamento", "Index", "TipoPagamentos")</li>
<li>#Html.ActionLink("Tipos de Rendimento", "Index", "TipoRendimentos")</li>
</ul>
#Html.Partial("_LoginPartial")
</div>
</div>
</div>
}
<div class="container body-content">
#RenderBody()
<footer>
#if (Request.IsAuthenticated)
{
<p>#Html.Action("_ObtemSaldo", "Home")</p>
}
</footer>
</div>
#Scripts.Render("~/bundles/bootstrap")
#RenderSection("scripts", required: false)
</body>
</html>
That request is authenticated shows the navbar on the top or not depending if the user is authenticated I cut it off to see if in fact my program is getting the user, and it kinda get the user so the problem is not with the connection, but the request is authenticated don't change in the controller maybe :S
Here is my login controller:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
try
{
var client = WebApiHttpClient.GetClient();
string username = model.Email;
string password = model.Password;
HttpContent content = new StringContent(
"grant_type=password&username=" + username + "&password=" + password,
System.Text.Encoding.UTF8,
"application/x-www-form-urlencoded");
var response = await client.PostAsync("/Token", content);
if (response.IsSuccessStatusCode)
{
TokenResponse tokenResponse =
await response.Content.ReadAsAsync<TokenResponse>();
WebApiHttpClient.storeToken(tokenResponse);
// return Content(tokenResponse.AccessToken);
return RedirectToAction("Index", "Home");
}
else
{
return Content("Ocorreu um erro: " + response.StatusCode);
}
}
catch
{
return Content("Ocorreu um erro.");
}
}
I already tried with user.authenticated still doesn't work if someone can give me a hand I would appreciate it a lot :D
ps: Sorry for my bad English
If I understood, you have an MVC Application and you have passed some logic (database access) to an Web Api project, so when you send a form/request to the server, it will be received by the controller from MVC, and after that the request will be send to the WebApi (at least is what I understood from your code).
Your problem is that the user logs into the application, the MVC Controller goes to the WebApi to authenticate the user and afterwards, even the login and password been correct the MVC (View) still considers that the user is not logged in.
Well, if what I described is correct, seen your code, I would say that the user is indeed being authenticate in the Web Api, however, as the MVC is the direct interface with the user, it is missing set the MVC Application User as authenticated through some authentication method, something like:
FormsAuthentication.SetAuthCookie(username, false);
It´s worth to say that you would have to store the token (from webapi, after user has been authenticated) so that the nexts requests to the WebApi it considers the user authenticated to that specific token.
Hope I´ve helped.
Regards,

Setting specific authentication type with mvc 5

ASP.NET comes default with the external login provider partial page which is set as:
var loginProviders = Context.GetOwinContext().Authentication.GetExternalAuthenticationTypes();
if (loginProviders.Count() == 0)
{
<div>
<p>
There are no external authentication services configured. See this article
for details on setting up this ASP.NET application to support logging in via external services.
</p>
</div>
}
else
{
using (Html.BeginForm("ExternalLogin", "Account", new { ReturnUrl = Model.ReturnUrl }))
{
#Html.AntiForgeryToken()
<div id="socialLoginList">
<p>
#foreach (AuthenticationDescription p in loginProviders)
{
<button type="submit" class="btn btn-default" id="#p.AuthenticationType" name="provider" value="#p.AuthenticationType" title="Log in using your #p.Caption account">#p.AuthenticationType</button>
}
</p>
</div>
}
}
What if i know exactly the authentication type i am going to use, say facebook? How can i set that up?