Search code examples
c#asp.net-corejwtasp.net-core-identity

Asp Core 2.1 Jwt + Identity. userManager store does not implement IUserRoleStore


I am trying to work with Jwt auth and Identity in ASP Net Core 2.1

In my Startup.cs I have:

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.RequireHttpsMetadata = false;
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuer = AuthOptions.ISSUER,
            ValidateAudience = true,
            ValidAudience = AuthOptions.AUDIENCE,
            ValidateLifetime = true,
            IssuerSigningKey = AuthOptions.GetSymmetricSecurityKey(),
            ValidateIssuerSigningKey = true,
        };
    });

var builder = services.AddIdentityCore<User>(options =>
{
    // Password settings
    ...
    // Lockout settings
    ...
    // User settings
    options.User.RequireUniqueEmail = true;
}).AddEntityFrameworkStores<ApplicationDbContext>();

builder = new IdentityBuilder(builder.UserType, typeof(IdentityRole), builder.Services);

Then in SecurityService.cs I am trying to get roles by using this statement

var roles = await _userManager.GetRolesAsync(user);

And its throwing the following exception:

NotSupportedException: Store does not implement IUserRoleStore
Microsoft.AspNetCore.Identity.UserManager.GetUserRoleStore()

I found it because of AddIdentityCore: If I use AddIdentity<User, IdentityRole> instead it works, but then [Authorize] doesn't work

Does anybody faced similar situation, or why it can happen?


Solution

  • When you use AddIdentity<TUser, TRole>, that call configures the default authentication scheme, like so (source):

    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
        options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
        options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
    })
    

    In your Startup.ConfigureServices, you have the following, which also sets the default authentication scheme:

    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    

    Because of the order this is defined (AddIdentity is after AddAuthentication), the default is changing from Jwt to Identity, so that when you use [Authorize], the authentication process is now expecting to use Identity rather than Jwt.

    To resolve this, the simplest option is to switch the order of AddIdentity and AddAuthentication, so the JwtBearer call comes last and therefore "wins". You'll also need to be more explicit and set both DefaultAuthenticateScheme and DefaultChallengeScheme:

    services.AddAuthentication(options =>
    {
        options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    })
    .AddJwtBearer(...);
    

    Another option is to be explicit in the [Authorize] attribute, calling out which authentication scheme you want to use, like either of the following two lines:

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    [Authorize(AuthenticationSchemes = IdentityConstants.ApplicationScheme)]
    

    It seems the first option would be most appropriate for your use-case, but it's good to know that this second option exists should you need it as you go further with Identity (there are more - e.g. using policies).