Why do the ASP.NET Identity interfaces use strings for primary and foreign keys?
Asked Answered
S

3

62

I'm looking at the interfaces on the new ASP.NET Identity classes and the database it creates using Entity Framework Code First. I'm using the Visual Studio 2013 RC.

At first glance the database schema looks reasonably normal:

enter image description here

But all the keys are NVARCHAR(128)

And for some crazy reason AspNetUserSecrets.Id is a PK that looks like it could point to more than one record in the AspNetUsers table. Does this mean multiple AspNetUsers will have to share the same password?

When I look at the Looking at the interfaces you're forced to implement, these are all strings...

public class User : IUser
{
    public string Id { get; set; }
    public string UserName { get; set; }
}

public class UserSecret : IUserSecret
{
    public string UserName { get; set; }
    public string Secret { get; set; }
}

public class UserRole : IUserRole
{
    public string UserId { get; set; }
    public string RoleId { get; set; }
}

public class UserClaim : IUserClaim
{
    public string UserId { get; set; }
    public string ClaimType { get; set; }
    public string ClaimValue { get; set; }
}

public class UserManagement : IUserManagement
{
    public string UserId { get; set; }
    public bool DisableSignIn { get; set; }
    public DateTime LastSignInTimeUtc { get; set; }
}

public class Tokens : IToken
{
    public string Id { get; set; }
    public string Value { get; set; }
    public DateTime ValidUntilUtc { get; set; }
}

public class UserLogin : IUserLogin
{
    public string UserId { get; set; }
    public string LoginProvider { get; set; }
    public string ProviderKey { get; set; }
}

public class Role : IRole
{
    public string Id { get; set; }
    public string Name { get; set; }
}

So I'm coming to terms with the fact that I may have to implement this using strings for PK and FK relationships.

But I'd really love to know WHY it's built like this...?

EDIT: Time has passed and there are now articles on how to extend the asp.net identity to use int (or guid) fields:

http://www.asp.net/identity/overview/extensibility/change-primary-key-for-users-in-aspnet-identity

Saltish answered 8/10, 2013 at 3:33 Comment(2)
You're using a beta version. The final version is different from my understanding. I have heard they have removed the AspNetUserSecrets table completely.Chaconne
Well that'll fix one of the problems :) But I still don't understand why all the keys are NVARCHER(128)...Saltish
R
37

The intent was to allow both arbitrary id types (i.e. int, guid, string), but also avoid having serialization/casting issues for the id property.

So you can define your keys however you like and just implement the interface method

public class MyUser : IUser {
  public int Id { get; set; }
  string IUser.Id { get { return Id.ToString(); } }
}
Rayfordrayle answered 9/10, 2013 at 21:54 Comment(6)
Sorry as the answer was provided by a a person on the inside I should have marked this as correct long ago.Saltish
Why if this is the only code needed to use a id as int, we have InEnumerable<BlogPost> like this : typecastexception.com/post/2014/07/13/… all over the place ?Vetter
That's all that's needed from Identity's perspective, there will be a slew of corresponding app code changes. This was pretty overwhelming in general, and we've tried to simplify this a bunch in Identity V3 already.Rayfordrayle
I want the simple life but there is no Roadmap on Identity 3.0 at CodePlex. Hence, can you give us a rough date? aspnetidentity.codeplex.com/wikipage?title=RoadmapBatten
If you're getting "'IUser.Id' in explicit interface declaration is not a member of interface", then change the above code to: string IUser<string>.Id { get { return Id.ToString(); } }Merwyn
My question would be: If I wanted to have a Guid as an actual Guid (with the DB field a UniqueIdentifier instead of a nvarchar(128)), how would I go about this? I am talking about modifying the actual DB field to be a UniqueIdentifier field, setting the Identity to true and handling the Guid as a Guid and not as a string: #36947692Gruff
X
22

Adding to what Hao said:

  1. The Identity runtime prefers strings for the user ID because we don’t want to be in the business of figuring out proper serialization of the user IDs (we use strings for claims as well for the same reason), e.g. all (or most) of the Identity interfaces refer to user ID as a string.
  2. People that customize the persistence layer, e.g. the entity types, can choose whatever type they want for keys, but then they own providing us with a string representation of the keys.
  3. By default we use the string representation of GUIDs for each new user, but that is just because it provides a very easy way for us to automatically generate unique IDs.
Xanthe answered 10/6, 2014 at 22:48 Comment(2)
It's time for c# to implement different method signatures based also on return type. Since this is a limitation, can't microsoft create an Identity.EntityFramework.Int package, a GuiD one, and all the options ?Vetter
@Rick Why is it nvarchar(128) rather than uniqueidentifier?Shelby
R
2

With ASP.NET Core, you have a very simple way to specify the data type you want for Identity's models.

First step, override identity classes from < string> to < data type you want> :

public class ApplicationUser : IdentityUser<Guid>
{
}

public class ApplicationRole : IdentityRole<Guid>
{
}

Declare your database context, using your classes and the data type you want :

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
            // Customize the ASP.NET Identity model and override the defaults if needed.
            // For example, you can rename the ASP.NET Identity table names and more.
            // Add your customizations after calling base.OnModelCreating(builder);
        }
    }

And in your startup class, declare the identity service using your models and declare the data type you want for the primary keys :

services.AddIdentity<ApplicationUser, ApplicationRole>()
            .AddEntityFrameworkStores<ApplicationDbContext, Guid>()
            .AddDefaultTokenProviders();

In ASP.NET identity tables, primary keys will still be in NVARCHAR but in your application it's will be the data type you want. You can check this in a controller :

    [HttpGet]
    public async Task<IActionResult> Test()
    {
        ApplicationUser user = await _userManager.GetUserAsync(HttpContext.User);
        Guid userId = user.Id; // No cast from string, it's a Guid data type
        throw new NotImplementedException();
    }
Remediosremedy answered 17/10, 2016 at 6:32 Comment(2)
There is an article in the official documentation since this answer : learn.microsoft.com/en-us/aspnet/core/security/authentication/…Remediosremedy
What if I do not want to define roles, We are handling roles by ACL.Joshuajoshuah

© 2022 - 2024 — McMap. All rights reserved.