I am trying to implement authentication in my MVC project. The issue is that I have implemented ASP.NET Identity OWIN. I can successfully authenticate and everything looks good.
The issue is that there are other Projects (such as a Service Layer) that retrieves the authenticated user via System.Threading.Thread.CurrentPrincipal.Identity
In this service layer the value is casted to a custom class as following:
public MyCompanyIdentity Identity
{
get { return System.Threading.Thread.CurrentPrincipal.Identity as MyCompanyIdentity ; }
}
The content of the MyCompanyIdentity class is as Follows:
public class MyCompanyIdentity : IIdentity
{
public MyCompanyIdentity ();
public string EndUserName { get; set; }
public string Exception { get; set; }
public string Ticket { get; set; }
public string Company { get; set; }
public string Fullname { get; set; }
public string Email { get; set; }
public bool IsAuthenticated { get; }
public string Name { get; }
public string AuthenticationType { get; }
}
In my controller class I set the authentication as follow:
var userStore = new UserStore<DemoApplicationUser>(new PeopleSaaIdentityContext());
var userManager = new UserManager<DemoApplicationUser>(userStore);
var user = userManager.Find(username, password);
if (user != null)
{
var authManager = HttpContext.Current.GetOwinContext().Authentication;
ClaimsIdentity userIdentity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
authManager.SignIn(new Microsoft.Owin.Security.AuthenticationProperties() { IsPersistent = true }, userIdentity);
//How do I cast/convert the System.Threading.Thread.CurrentPrincipal.Identity to a MyCompanyIdentity?
}
Since I can't change the MyCompanyIdentity to implement IdentityUser as I don't have access to the source code.
How do I "cast/convert" to IIdenityUser? I would like to resuse the same class as it used literally everywhere in the application.
I really hope it makes sense else let me know and I would try to explain further.
It had no choice other than rewritting the logic from scratch.
Related
Recently I seaerched for how to create custom principal and I got the answer but there is one thing that I do not understand, I found this solution on stack overflow
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Security.Principal;
namespace workflow.Authorize
{
interface ICustomPrincipal : IPrincipal
{
int Id { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
}
public class CustomPrincipal : ICustomPrincipal
{
public IIdentity Identity { get; private set; }
public bool IsInRole(string role) {
if(this.Roles.Contains(role))
return true;
else
return false;
}
public CustomPrincipal(string email)
{
this.Identity = new GenericIdentity(email);
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Roles { get; set; }
}
public class CustomPrincipalSerializeModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Roles { get; set; }
}
}
but what I do not understand is why the developer created the ICustomPrincipal interface and force the CustomPrincipal to inherit it why not the CustomPrincipal class inherits directly from the IPrincipal interface
For me if you need to force the users to override additional properties and methods then you have to create a custom interface.
The developer created a custom interface and added new properties like the FirstName property and the LastName property to force you to apply them.
Look here and you will find that the only property that the Iprincipal interface has is the identity property and that could not be enough for you.
I hope that helps you to understand why the developers sometimes create some custom interfaces.
I am a newbie to asp.net and trying to figure out a small issue and could not get through.
what I am trying is to access additional user profile details and display the first name in mylogin partial page and giving an exception, any help to shed something really appreciated.
#
{var manager = new UserManager<MyUser>(new UserStore<MyUser>(new MyDbContext ()));
**var currentUser = manager.FindById(User.Identity.GetUserId());**
var myname = currentUser.UserInfo.FirstName;
}
An exception of type 'System.InvalidOperationException' occurred in mscorlib.dll but was not handled in user code
Additional information: The specified cast from a materialized 'System.Data.Entity.DynamicProxies.ApplicationUser_19A2C4C53E8DE616853C0AA38428987A7CBBC56E57FCCF82F04E81B42A687C20' type to the 'BNTracker.Models.MyUser' type is not valid.
MyUser is already inherited from IdentityUser and its available under models
public class MyUser : IdentityUser
{
public virtual UserInfo UserInfo { get; set; }
}
public class UserInfo
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmployeeCode { get; set; }
public string Email { get; set; }
}
public class MyDbContext : IdentityDbContext<MyUser>
{
public MyDbContext()
: base("DefaultConnection")
{
//Database.SetInitializer(new DropCreateDatabaseIfModelChanges<MyDbContext>());
}
public System.Data.Entity.DbSet<UserInfo> UserInfo { get; set; }
}
Please try
var manager = new UserManager<IdentityUser>(new UserStore<IdentityUser>(new MyDbContext ())
var currentUser = manager.FindById(User.Identity.GetUserId());
I'm trying to modify the default MVC project so that instead of showing a username, I can display their full name. Eg, the default app shows
Hello <username>! Log off
I added a new property FullName to the ApplicationUser class. The code that shows the name currently is:
#Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Manage", "Account", routeValues:=Nothing, htmlAttributes:=New With {.title = "Manage"})
So how can I get lookup that value from the ApplicationUser class and display it here? Additionally, is there a way to cache this? It seems like a waste to perform a lookup for every request.
I also might want to show their email address instead, so I definitely need to use a new property.
I generally like to serialize a user object in the FormsAuthentication cookie when they login and then create a class inheriting from IPrincipal so that my views can read the de-serialized object:
public interface IUserPrincipal : IPrincipal
{
int Id { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
string Username { get; set; }
}
public class UserPrincipal : IUserPrincipal
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public IIdentity Identity { get; private set; }
public UserPrincipal(string Username)
{
this.Identity = new GenericIdentity(Username);
}
}
public class UserPrincipalPoco
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
}
and then when authenticating:
public ActionResult Login(LoginViewModel vm, string ReturnUrl)
{
// Check for valid authentication
if (_authenticationService.Authenticate(vm.Username, vm.Password))
{
// Add forms authentication cookie
Response.Cookies.Add(GetFormsAuthenticationCookie(vm.Username));
// Redirect after authentication
}
// Failed authentication, redirect to unauthorized
}
private HttpCookie GetFormsAuthenticationCookie(string Username)
{
var user = _userService.GetUserByUsername(Username);
UserPrincipalPoco pocoModel = new UserPrincipalPoco();
pocoModel.Id = user.Id.Value;
pocoModel.FirstName = user.FirstName;
pocoModel.LastName = user.LastName;
pocoModel.Username = Username;
JavaScriptSerializer serializer = new JavaScriptSerializer();
string userData = serializer.Serialize(pocoModel);
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1,
Username,
DateTime.Now,
DateTime.Now.AddMinutes(15),
false,
userData);
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
return new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
}
and then in global.asax.cs:
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
JavaScriptSerializer serializer = new JavaScriptSerializer();
UserPrincipalPoco serializeModel = serializer.Deserialize<UserPrincipalPoco>(authTicket.UserData);
UserPrincipal newUser = new UserPrincipal(authTicket.Name);
newUser.Id = serializeModel.Id;
newUser.FirstName = serializeModel.FirstName;
newUser.LastName = serializeModel.LastName;
newUser.Username = serializeModel.Username;
HttpContext.Current.User = newUser;
}
}
Now you need to create a BaseViewPage that inherits from WebViewPage to tell your views to use your UserPrincipal object:
public abstract class BaseViewPage : WebViewPage
{
public virtual new UserPrincipal User
{
get { return base.User as UserPrincipal; }
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual new UserPrincipal User
{
get { return base.User as UserPrincipal; }
}
}
and in your Web.config tell your views to always use this BaseViewPage:
<pages pageBaseType="MyNameSpace.Views.BaseViewPage">
Now in my views I can access the user like:
#User.Username
or
#User.FirstName #User.LastName
few ways you can do it.
if the application is not too big, you may cache the user models that log in for a specific period of time so you can pull the entire user info based on the username.
you may save a list if info in User.Identity, including username, firstname, last name etc, separating them with a comma or etc.
a bad way: every time you need the extra info, hit the database and get them.
my opinion: cache the recent users who have been logged in for a specific amount of time. you will be able to create slick solutions using cashing. let me know if you need info.
I've been trying to implement a custom version of the new Identity features in ASP.NET 4.5 (Microsoft.AspNet.Identity), using Visual Studio 2013. After many hours of playing around with this, I've simplified my code in an effort to get it running without errors. I've listed my code below. When doing a Local Registration, the database tables are created, but the CreateLocalUser method fails. I'm hoping that someone can help me identify the changes needed.
Models/MembershipModel.cs
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace thePulse.web.Models
{
public class PulseUser : IUser
{
public PulseUser() { }
public PulseUser(string userName)
{
UserName = userName;
}
[Key]
public string Id { get; set; }
[Required]
[StringLength(20)]
public string UserName { get; set; }
[StringLength(100)]
public string Email { get; set; }
[Column(TypeName = "Date")]
public DateTime? BirthDate { get; set; }
[StringLength(1)]
public string Gender { get; set; }
}
public class PulseUserClaim : IUserClaim
{
public PulseUserClaim() { }
[Key]
public string Key { get; set; }
public string UserId { get; set; }
public string ClaimType { get; set; }
public string ClaimValue { get; set; }
}
public class PulseUserSecret : IUserSecret
{
public PulseUserSecret() { }
public PulseUserSecret(string userName, string secret)
{
UserName = userName;
Secret = secret;
}
[Key]
public string UserName { get; set; }
public string Secret { get; set; }
}
public class PulseUserLogin : IUserLogin
{
public PulseUserLogin() { }
public PulseUserLogin(string userId, string loginProvider, string providerKey)
{
LoginProvider = LoginProvider;
ProviderKey = providerKey;
UserId = userId;
}
[Key, Column(Order = 0)]
public string LoginProvider { get; set; }
[Key, Column(Order = 1)]
public string ProviderKey { get; set; }
public string UserId { get; set; }
}
public class PulseRole : IRole
{
public PulseRole() { }
public PulseRole(string roleId)
{
Id = roleId;
}
[Key]
public string Id { get; set; }
}
public class PulseUserRole : IUserRole
{
public PulseUserRole() { }
[Key, Column(Order = 0)]
public string RoleId { get; set; }
[Key, Column(Order = 1)]
public string UserId { get; set; }
}
public class PulseUserContext : IdentityStoreContext
{
public PulseUserContext(DbContext db) : base(db)
{
Users = new UserStore<PulseUser>(db);
Logins = new UserLoginStore<PulseUserLogin>(db);
Roles = new RoleStore<PulseRole, PulseUserRole>(db);
Secrets = new UserSecretStore<PulseUserSecret>(db);
UserClaims = new UserClaimStore<PulseUserClaim>(db);
}
}
public class PulseDbContext : IdentityDbContext<PulseUser, PulseUserClaim, PulseUserSecret, PulseUserLogin, PulseRole, PulseUserRole>
{
}
}
Changes to Controllers/AccountController.cs
public AccountController()
{
IdentityStore = new IdentityStoreManager(new PulseUserContext(new PulseDbContext()));
AuthenticationManager = new IdentityAuthenticationManager(IdentityStore);
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
try
{
// Create a profile, password, and link the local login before signing in the user
PulseUser user = new PulseUser(model.UserName);
if (await IdentityStore.CreateLocalUser(user, model.Password))
{
await AuthenticationManager.SignIn(HttpContext, user.Id, isPersistent: false);
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "Failed to register user name: " + model.UserName);
}
}
catch (IdentityException e)
{
ModelState.AddModelError("", e.Message);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
As I said above, this implementation fails when the CreateLocalUser method fails (Microsoft.AspNet.Identity.EntityFramework). I cannot figure out why.
The issue here is that IdentityStoreManager has strong dependency on the default implementation of identity EF models. For example, the CreateLocalUser method will create UserSecret and UserLogin objects and save them to stores, which won't work if the store is not using the default model type. So if you customize the model type, it won't work smoothly with IdentityStoreManager.
Since you only customize the IUser model, I simplified the code to inherit custom user from default identity user and reuse other models from identity EF models.
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Web;
namespace WebApplication11.Models
{
public class PulseUser : User
{
public PulseUser() { }
public PulseUser(string userName) : base(userName)
{
}
[StringLength(100)]
public string Email { get; set; }
[Column(TypeName = "Date")]
public DateTime? BirthDate { get; set; }
[StringLength(1)]
public string Gender { get; set; }
}
public class PulseUserContext : IdentityStoreContext
{
public PulseUserContext(DbContext db) : base(db)
{
this.Users = new UserStore<PulseUser>(this.DbContext);
}
}
public class PulseDbContext : IdentityDbContext<PulseUser, UserClaim, UserSecret, UserLogin, Role, UserRole>
{
}
}
The code above should work with preview version of Identity API.
The IdentityStoreManager API in upcoming release is already aware of this issue and changed all the non-EF dependency code into a base class so that you can customize it by inheriting from it. It should solve all the problems here. Thanks.
PulseUser.Id is defined as a string but doesn't appear to be set to a value. Were you meant to be using a GUID for the Id? If so, initialise it in the constructor.
public PulseUser() : this(String.Empty) { }
public PulseUser(string userName)
{
UserName = userName;
Id = Guid.NewGuid().ToString();
}
You will also want to perform a check that the user name doesn't already exist. Look at overriding DbEntityValidationResult in PulseDbContext. Do a new MVC project in VS2013 to see an example.
Since there are alot of changes on this when going to RTM, i have updated the SPA template that uses a WebApi controller for all the identity signin and such. Its a really cool template , if you havent seen it.
I put all my code here:
https://github.com/s093294/aspnet-identity-rtm/tree/master
(Do note, its only for inspiration. I only made it work and nothing more. Properly have a bug or two also).
Trying to implement IPrincipal (ASP.NET MVC 3) and having problems:
my custom IPrincipal:
interface IDealsPrincipal: IPrincipal
{
int UserId { get; set; }
string Firstname { get; set; }
string Lastname { get; set; }
}
public class DealsPrincipal : IDealsPrincipal
{
public IIdentity Identity { get; private set; }
public bool IsInRole(string role) { return false; }
public DealsPrincipal(string email)
{
this.Identity = new GenericIdentity(email);
}
public int UserId { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
}
To serialize/deserialize i use the following class:
public class DealsPrincipalSerializeModel
{
public int UserId { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
}
The Application authenticate event is as follows (works fine!)
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
//get the forms ticket
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
//instantiate a new Deserializer
JavaScriptSerializer serializer = new JavaScriptSerializer();
//deserialize the model
DealsPrincipalSerializeModel serializeModel = serializer.Deserialize<DealsPrincipalSerializeModel>(authTicket.UserData);
//put the values of the deserialized model into the HttpContext
DealsPrincipal newUser = new DealsPrincipal(authTicket.Name); //this implements IPrincipal
newUser.UserId = serializeModel.UserId;
newUser.Firstname = serializeModel.Firstname;
newUser.Lastname = serializeModel.Lastname;
HttpContext.Current.User = newUser;
}
}
As you can see in the last statement the HttpContext gets assigned this new DealsPrincipal (which works fine).
The problem is that if want to access this User in a Controller(Action) i always get a base class object. If i cast the User as follows:
User as DealsPrincipal
to get for example the UserId (sample:
( User as DealsPrincipal).UserId
this is always null!!! Why? What am i missing?
I would need to investigate more to give you correct answer but look this part of the code and it could help you (part of the source of WindowsAuthenticationModule.cs)
void OnAuthenticate(WindowsAuthenticationEventArgs e) {
////////////////////////////////////////////////////////////
// If there are event handlers, invoke the handlers
if (_eventHandler != null)
_eventHandler(this, e);
if (e.Context.User == null)
{
if (e.User != null)
e.Context.User = e.User;
else if (e.Identity == _anonymousIdentity)
e.Context.SetPrincipalNoDemand(_anonymousPrincipal, false /*needToSetNativePrincipal*/);
else
e.Context.SetPrincipalNoDemand(new WindowsPrincipal(e.Identity), false /*needToSetNativePrincipal*/);
}
}
From this code I would suggest you to check if user is anonymous before assigning instance of your custom IPrincipal inmplementation. Also, not sure if this method is executed before or after "protected void Application_AuthenticateRequest". Will try to take more time to investigate this.
Also, please look at this article:
http://msdn.microsoft.com/en-us/library/ff649210.aspx