I have an MVC5 app that uses Individual Authentication, and of course ASP.NET Identity. The point is that I had extended I have a model that inherits from ApplicationUser, it is simply defined like this:
public class NormalUser : ApplicationUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
So, the point is that, first of all I want to check whether there is a logged-in user, and if there is, I want to get his/her FirstName, LastName and Email fields. How can I achieve it?
I think I need to use something like this to check whether there is a logged-in user:
if (Request.IsAuthenticated)
{
...
}
But, how can I get those specific fields' values for the current user?
In MVC5 the user data is stored by default in the session and upon request the data is parsed into a ClaimsPrincipal
which contains the username (or id) and the claims.
This is the way I chose to implement it, it might not be the simplest solution but it definitely makes it easy to use.
Example of usage:
In controller:
public ActionResult Index()
{
ViewBag.ReverseDisplayName = this.User.LastName + ", " + this.User.FirstName;
}
In view or _Layout:
@if(User.IsAuthenticated)
{
<span>@User.DisplayName</span>
}
1. Replace ClaimsIdentityFactory
using System.Security.Claims;
using System.Threading.Tasks;
using Domain.Models;
using Microsoft.AspNet.Identity;
public class AppClaimsIdentityFactory : IClaimsIdentityFactory<User, int>
{
internal const string IdentityProviderClaimType = "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider";
internal const string DefaultIdentityProviderClaimValue = "My Identity Provider";
/// <summary>
/// Constructor
/// </summary>
public AppClaimsIdentityFactory()
{
RoleClaimType = ClaimsIdentity.DefaultRoleClaimType;
UserIdClaimType = ClaimTypes.NameIdentifier;
UserNameClaimType = ClaimsIdentity.DefaultNameClaimType;
SecurityStampClaimType = Constants.DefaultSecurityStampClaimType;
}
/// <summary>
/// Claim type used for role claims
/// </summary>
public string RoleClaimType { get; set; }
/// <summary>
/// Claim type used for the user name
/// </summary>
public string UserNameClaimType { get; set; }
/// <summary>
/// Claim type used for the user id
/// </summary>
public string UserIdClaimType { get; set; }
/// <summary>
/// Claim type used for the user security stamp
/// </summary>
public string SecurityStampClaimType { get; set; }
/// <summary>
/// Create a ClaimsIdentity from a user
/// </summary>
/// <param name="manager"></param>
/// <param name="user"></param>
/// <param name="authenticationType"></param>
/// <returns></returns>
public virtual async Task<ClaimsIdentity> CreateAsync(UserManager<User, int> manager, User user, string authenticationType)
{
if (manager == null)
{
throw new ArgumentNullException("manager");
}
if (user == null)
{
throw new ArgumentNullException("user");
}
var id = new ClaimsIdentity(authenticationType, UserNameClaimType, RoleClaimType);
id.AddClaim(new Claim(UserIdClaimType, user.Id.ToString(), ClaimValueTypes.String));
id.AddClaim(new Claim(UserNameClaimType, user.UserName, ClaimValueTypes.String));
id.AddClaim(new Claim(IdentityProviderClaimType, DefaultIdentityProviderClaimValue, ClaimValueTypes.String));
id.AddClaim(new Claim(ClaimTypes.Email, user.EmailAddress));
if (user.ContactInfo.FirstName != null && user.ContactInfo.LastName != null)
{
id.AddClaim(new Claim(ClaimTypes.GivenName, user.ContactInfo.FirstName));
id.AddClaim(new Claim(ClaimTypes.Surname, user.ContactInfo.LastName));
}
if (manager.SupportsUserSecurityStamp)
{
id.AddClaim(new Claim(SecurityStampClaimType,
await manager.GetSecurityStampAsync(user.Id)));
}
if (manager.SupportsUserRole)
{
user.Roles.ToList().ForEach(r =>
id.AddClaim(new Claim(ClaimTypes.Role, r.Id.ToString(), ClaimValueTypes.String)));
}
if (manager.SupportsUserClaim)
{
id.AddClaims(await manager.GetClaimsAsync(user.Id));
}
return id;
}
2. Change the UserManager
to use it
public static UserManager<User,int> Create(IdentityFactoryOptions<AppUserManager> options, IOwinContext context)
{
var manager = new UserManager<User,int>(new UserStore<User,int>(new ApplicationDbContext()))
{
ClaimsIdentityFactory = new AppClaimsIdentityFactory()
};
// more initialization here
return manager;
}
3. Create a new custom Principal
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Claims;
public class UserPrincipal : ClaimsPrincipal
{
public UserPrincipal(ClaimsPrincipal principal)
: base(principal.Identities)
{
}
public int UserId
{
get { return FindFirstValue<int>(ClaimTypes.NameIdentifier); }
}
public string UserName
{
get { return FindFirstValue<string>(ClaimsIdentity.DefaultNameClaimType); }
}
public string Email
{
get { return FindFirstValue<string>(ClaimTypes.Email); }
}
public string FirstName
{
get { return FindFirstValue<string>(ClaimTypes.GivenName); }
}
public string LastName
{
get { return FindFirstValue<string>(ClaimTypes.Surname); }
}
public string DisplayName
{
get
{
var name = string.Format("{0} {1}", this.FirstName, this.LastName).Trim();
return name.Length > 0 ? name : this.UserName;
}
}
public IEnumerable<int> Roles
{
get { return FindValues<int>(ClaimTypes.Role); }
}
private T FindFirstValue<T>(string type)
{
return Claims
.Where(p => p.Type == type)
.Select(p => (T)Convert.ChangeType(p.Value, typeof(T), CultureInfo.InvariantCulture))
.FirstOrDefault();
}
private IEnumerable<T> FindValues<T>(string type)
{
return Claims
.Where(p => p.Type == type)
.Select(p => (T)Convert.ChangeType(p.Value, typeof(T), CultureInfo.InvariantCulture))
.ToList();
}
}
4. Create an AuthenticationFilter
to use it
using System.Security.Claims;
using System.Web.Mvc;
using System.Web.Mvc.Filters;
public class AppAuthenticationFilterAttribute : ActionFilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
//This method is responsible for setting and modifying the principle for the current request though the filterContext .
//Here you can modify the principle or applying some authentication logic.
var principal = filterContext.Principal as ClaimsPrincipal;
if (principal != null && !(principal is UserPrincipal))
{
filterContext.Principal = new UserPrincipal(principal);
}
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
//This method is responsible for validating the current principal and permitting the execution of the current action/request.
//Here you should validate if the current principle is valid / permitted to invoke the current action. (However I would place this logic to an authorization filter)
//filterContext.Result = new RedirectToRouteResult("CustomErrorPage",null);
}
}
5. Register the auth filter to load globally in the FilterConfig
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new AppAuthenticationFilterAttribute());
}
By now the Principal
is persisted and all we have left to do is expose it in the Controller and View.
6. Create a controller base class
public abstract class ControllerBase : Controller
{
public new UserPrincipal User
{
get { return HttpContext.User as UserPrincipal; }
}
}
7. Create a WebViewPage
base class and modify the web.config
to use it
public abstract class BaseViewPage : WebViewPage
{
public virtual new UserPrincipal User
{
get { return base.User as UserPrincipal; }
}
public bool IsAuthenticated
{
get { return base.User.Identity.IsAuthenticated; }
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual new UserPrincipal User
{
get { return base.User as UserPrincipal; }
}
public bool IsAuthenticated
{
get { return base.User.Identity.IsAuthenticated; }
}
}
And the web.config
inside the Views
folder:
<pages pageBaseType="MyApp.Web.Views.BaseViewPage">
Important!
Do not store too much data on the Principal
since this data is passed back and forth on each request.
Yes, in Identity, if you need additional user information, you just pull the user from the database, since this is all stored on the actual user object now.
if (Request.IsAuthenticated)
{
var user = UserManager.FindById(User.Identity.GetUserId());
}
If GetUserId
isn't on User.Identity
, add the following to your usings:
using Microsoft.AspNet.Identity;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With