17

I am using Asp.net identity for Login,Register,Forgot Password etc and source code is taken from this below link:

http://www.asp.net/mvc/overview/security/create-an-aspnet-mvc-5-web-app-with-email-confirmation-and-password-reset

http://www.asp.net/identity/overview/features-api/account-confirmation-and-password-recovery-with-aspnet-identity.

Now i have 1 table that is UserMaster and during registration i am asking for this following fields: FullName,EmailId,Password,ContactNumber,Gender.

My UserMaster Contains this following fields:Id,FullName,EmailId,ContactNumber,Gender

Now when user will submit registration form this FullName,EmailId,ContactNumber,Gender will be saved in UserMaster along with the Email,Password will be saved in AspnetUser.

My Register Method is same as provided in above 2 links.

Here you might notice that there is no relationship between my UserMaster and AspnetUser so during login when user will enter his email id to login i will use this method await SignInManager.PasswordSignInAsync to verify user and if this method returns success then what i will do is use this email id and check this email in my UserMaster and where match will be found i will fetch that UserId from UserMaster and store it in session and use thorugh out my application in my login method like below:

 public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
            {
                if (!ModelState.IsValid)
                {
                    return View(model);
                }

                // This doesn't count login failures towards account lockout
                // To enable password failures to trigger account lockout, change to shouldLockout: true
                var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
                switch (result)
                {
                    case SignInStatus.Success:
                  using (var context = new MyEntities())
                        {
                            var fetchUSerId = context.UserMaster.Where(t => t.Email == model.Email).Select(t=>t.UserId).SingleOrDefault();
                            Session["UserId"] = fetchUSerId;
                        }
                        return RedirectToLocal(returnUrl);
                    case SignInStatus.LockedOut:
                        return View("Lockout");
                    case SignInStatus.RequiresVerification:
                        return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
                    case SignInStatus.Failure:
                    default:
                        ModelState.AddModelError("", "Invalid login attempt.");
                        return View(model);
                }
            }

I am talking about this in my login method:

 case SignInStatus.Success:
                      using (var context = new MyEntities())
                            {
                                var fetchUSerId = context.UserMaster.Where(t => t.Email == model.Email).Select(t=>t.UserId).SingleOrDefault();
                                Session["UserId"] = fetchUSerId;
                            }

Is this an appropriate way or still a better way and i want to store entire user object instead of just storing User Id.

So can anybody tell me how to do this with aspnet identity??

I Love Stackoverflow
  • 6,738
  • 20
  • 97
  • 216
  • Do you not like the built-in forms authentication? You could trick it out and make it all custom but still leverage the framework? (I might be totally missing something in your code though)... Check out the answer from this post: http://stackoverflow.com/questions/1064271/asp-net-mvc-set-custom-iidentity-or-iprincipal – Kris Oye Oct 01 '15 at 06:30
  • 1
    Youd be better off storing all those values in the ClaimsIdentity that AspNet Identity uses. This is pretty standard stuff. – Brendan Green Oct 01 '15 at 06:37
  • 1
    http://stackoverflow.com/questions/31974228/can-you-extend-httpcontext-current-user-identity-properties/31976327#31976327 – Brendan Green Oct 01 '15 at 06:41
  • @BrendanGreen:So istead of storing Userid and all other information i must store it in claimidentity and how would i use it then?Can you please post any sample code for login method thought which i will maintain my user object in claimidentity?please – I Love Stackoverflow Oct 01 '15 at 06:46
  • 1
    Its not a question of "must", but it is imho a better option. The out of the box aspnet identity experience has method extensions against IIdentity, and in the controller you can just do `User.Identity.GetUserId ()` for example. – Brendan Green Oct 01 '15 at 06:49
  • @BrendanGreen:I dont want userid of AspnetUser.I want userid from UserMaster and as i have specified in my question there is no relation between aspnetuser and my UserMaster and after login i would be using UserId from my UserMaster and not from userid from aspnetuser. – I Love Stackoverflow Oct 01 '15 at 06:52
  • Take a look at the linked answer i provided. You can store whatever you want in the claims. Its pretty trivial to extend. – Brendan Green Oct 01 '15 at 06:54
  • @BrendanGreen:So this claims i can use thoughout my application?Actually i dont know what is claim.i am using this owin first time so – I Love Stackoverflow Oct 01 '15 at 06:58
  • im sorry but how can the accepted answer be the answer if it doesn't even use session. – Seabizkit Mar 03 '20 at 11:21

3 Answers3

30

Since you are using Asp.Net Identity, you want to store session related stuff as claims. This is very easy to extend with customised claims.

As an aside, I think you'd be better off simple extending ApplicationUser to hold the additional data, as detailed here.

That said, here is a complete example of how to add custom claim types to your application.

Step 1 - Define one or more custom claim types to hold your additional information

public static class CustomClaimTypes
{
    public const string MasterFullName = "http://schemas.xmlsoap.org/ws/2014/03/mystuff/claims/masterfullname";
    public const string MasterUserId = "http://schemas.xmlsoap.org/ws/2014/03/mystuff/claims/masteruserid";
}

A claim type is just a unique string that identifies the specific claim. Here we are just using a similar format as the built in claim types.

Step 2 - During the sign in process, set values for the custom claim types

private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
    var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);

    //Fetch data from the UserMaster table 
    var userdata = GetdatafromUserMaster();

    //Using the UserMaster data, set our custom claim types
    identity.AddClaim(new Claim(CustomClaimTypes.MasterUserId, userdata.UserId));
    identity.AddClaim(new Claim(CustomClaimTypes.MasterFullName, userdata.FullName));

    AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}

Note: we are using custom claim types so that we preserve the existing NameIdentifier and Name claims, and can therefore easily access identity information from both Asp.Net Identity and our custom UserMaster table.

Step 3 - Add extension method(s) to IIdentity so we can easily access our custom claim data

public static class IdentityExtensions
{
    public static string GetMasterUserId(this IIdentity identity)
    {
        if (identity == null)
            return null;

        return (identity as ClaimsIdentity).FirstOrNull(CustomClaimTypes.MasterUserId);
    }

    public static string GetMasterFullName(this IIdentity identity)
    {
        if (identity == null)
            return null;

        return (identity as ClaimsIdentity).FirstOrNull(CustomClaimTypes.MasterFullName);
    }

    internal static string FirstOrNull(this ClaimsIdentity identity, string claimType)
    {
        var val = identity.FindFirst(claimType);

        return val == null ? null : val.Value;
    }
}

Nothing fancy here. We just cast the IIdentity as a ClaimsIdentity and then return the value of either the first claim of the given CustomClaimType that we find, or we return null if a claim doesn't exist.

Step 4 - Now we can access our custom claim data in views and/or controllers really easily. Say you wanted to use the full name from your UserMaster table instead of the ApplicationUser? You can now do this:

<ul class="nav navbar-nav navbar-right">
    <li>
        @Html.ActionLink("Hello " + User.Identity.GetMasterFullName() + "!", "Index", "Manage", routeValues: null, htmlAttributes: new { title = "Manage" })
    </li>
    <li><a href="javascript:document.getElementById('logoutForm').submit()">Log off</a></li>
</ul>

You can also do the same thing from within a Controller.

Brendan Green
  • 11,676
  • 5
  • 44
  • 76
  • Thanks alot for your ans.I will definetely try this and let you know.Once again thank you so much for sparing your valuable time for me and posting this solution – I Love Stackoverflow Oct 08 '15 at 03:39
  • @Brendan Green in the midst of this, it seems that this line of code Session["UserId"] = fetchUSerId; seems to be no more required. Is it bcoz Owin does this internally ? I mean does Owin interanlly creates a Session ? Please guide me here. – Zaker Mar 16 '16 at 05:53
  • @Brendan, I am using same thing, but faces a error 'userid is null' – Neeraj Sharma May 14 '16 at 09:43
  • @NeerajSharma probably best that you ask a new question, with details of your problem and the code that you are using. – Brendan Green May 16 '16 at 23:10
  • @BrendanGreen i have asked a question, this is link http://stackoverflow.com/questions/37226916/claims-unable-to-create-cookies – Neeraj Sharma May 17 '16 at 05:08
  • 1
    Where should the `SignInAsync` method be placed? – Scott Weldon Aug 29 '16 at 18:24
  • Is there a way to store a custom object as a claim? – umutesen Nov 09 '16 at 16:19
  • I guess you could always serialize your object – Brendan Green Nov 09 '16 at 19:28
  • 2
    @ScottWeldon this method resides in `SignInManager`. You usually create your own manager, something like `ApplicationSignInManager`, where you can override this method with Brendan's implementation. Although I'd avoid doing this. Inside your `ApplicationUser` class, there is a method `GenerateUserIdentityAsync`. You can also set your custom claims there. – Alisson Reinaldo Silva Jun 20 '17 at 20:58
  • im correct that this stores the claims in cookie and not session? therefor each claim you add will increse the size of the cookie? also if you holding something which should only have a life of a session rather than cookie... – Seabizkit Mar 03 '20 at 11:23
  • "Since you are using Asp.Net Identity, you want to store session related stuff as claims." Why do I want this? – ErTR May 02 '20 at 16:52
2

You can add as:

var listClaims=new[] { new Claims(ClaimsType.SerialNumber,Id), new Claims(ClaimsType.Name,FullName), new Claims(ClaimsType.HomePhone,ContactNumber), new Claims(ClaimsType.Gender,Gender)};

var oAuthIdentity=new ClaimsIdentity(listClaims, otherparameter ...);

For more details you can check System.Secutity.Claims.ClaimTypes

ErTR
  • 863
  • 1
  • 14
  • 37
JavaScript Linq
  • 2,605
  • 3
  • 13
  • 12
1

you may do this:

var fetchUser = context.UserMaster.Where(t => t.Email == model.Email).SingleOrDefault();
if (null == fetchUser)
    throw new Exception("Not found");
Session["User"] = fetchUser;
Backs
  • 24,430
  • 5
  • 58
  • 85
C.Fasolin
  • 313
  • 1
  • 4
  • 19