I can't use ASP.NET Identity and JWT autentication at the same time

Jalza 781 Reputation points
2022-06-29T08:36:14.53+00:00

I'm developing a platform with ASP.NET Core 5 that uses Identity as a membership system. Aside from the web application, this platform also exposes a Web API. I have tried using JWT as Web API authentication. But when I add the JWT configuration I'm no longer able to login to the web application.

This is the
Startup.cs
file:

public class Startup  
{  
    public Startup(IConfiguration configuration)  
    {  
        Configuration = configuration;  
    }  

    public IConfiguration Configuration { get; }  

    private MappingProfile mappingProfile;  

    // This method gets called by the runtime. Use this method to add services to the container.  
    public void ConfigureServices(IServiceCollection services)  
    {  
        services.AddDbContext<ApplicationDbContext>(options =>  
            options.UseSqlServer(  
                Configuration.GetConnectionString("DefaultConnection")));  
        services.AddDatabaseDeveloperPageExceptionFilter();  

        services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)  
            .AddRoles<ApplicationRole>() //Line that can help you  
            .AddEntityFrameworkStores<ApplicationDbContext>();  

        // JWT  
        // If it is commented, I can login to the web app, if not I can't  
        /*services  
            //.AddHttpContextAccessor()  
            //.AddAuthorization()  
            .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)  
            .AddJwtBearer(options =>  
            {  
                options.TokenValidationParameters = new TokenValidationParameters  
                {  
                    ValidateIssuer = true,  
                    ValidateAudience = true,  
                    ValidateLifetime = true,  
                    ValidateIssuerSigningKey = true,  
                    ValidIssuer = Configuration["Jwt:Issuer"],  
                    ValidAudience = Configuration["Jwt:Audience"],  
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))  
                };  
            });*/  

        // AUTOMAPPER  
        this.mappingProfile = new MappingProfile();  
        MapperConfiguration mappingConfig = new MapperConfiguration(mc => {  
            mc.AddProfile(this.mappingProfile);  
        });  
        IMapper mapper = mappingConfig.CreateMapper();  
        services.AddSingleton(mapper);  

        services.AddControllers();  
        services.AddControllersWithViews().AddRazorRuntimeCompilation();  
        services.AddSignalR();  

        // REPOSITORIES  
        services.AddScoped<IExampleRepository, ExampleRepository>();  
          
        // LOCALIZATION  
        services.AddLocalization(/*options => options.ResourcesPath = "Resources"*/);  
        services.AddMvc()  
            .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)  
            .AddDataAnnotationsLocalization();  

        services.Configure<RequestLocalizationOptions>(options =>  
        {  
            var supportedCultures = new[]  
            {  
                new CultureInfo("en"),  
                new CultureInfo("es")  
            };  
            options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");  
            options.SupportedCultures = supportedCultures;  
            options.SupportedUICultures = supportedCultures;  
        });  
    }  

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.  
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ApplicationDbContext context, RoleManager<ApplicationRole> roleManager, IStringLocalizer<MappingProfile> mappingProfileLocalizer)  
    {  
        if (env.IsDevelopment())  
        {  
            app.UseDeveloperExceptionPage();  
            app.UseMigrationsEndPoint();  
        }  
        else  
        {  
            app.UseExceptionHandler("/Home/Error");  
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.  
            app.UseHsts();  
        }  

        app.UseHttpsRedirection();  
        app.UseStaticFiles();  

        app.UseRouting();  
        app.UseAuthentication();  
        app.UseAuthorization();  

        app.UseEndpoints(endpoints =>  
        {  
            endpoints.MapControllerRoute(  
                name: "areas",  
                pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");  

            endpoints.MapControllerRoute(  
                name: "default",  
                pattern: "{controller=Home}/{action=Index}/{id?}");  

            endpoints.MapRazorPages();  
            endpoints.MapControllers();  
            endpoints.MapHub<DcHub>("/dchub");  
        });  

        this.mappingProfile.Localizer = mappingProfileLocalizer;  
        ApplicationDbInitializer.Initialize(context, roleManager);  
    }  
}  

I think that I'm missing something in this configuration.

In case it can be of help, information about the development environment and libraries:

  • Microsoft Visual Studio Professional 2019 (Version 16.11.15)
  • .NET 5
Developer technologies ASP.NET ASP.NET Core
{count} votes

2 answers

Sort by: Most helpful
  1. Jalza 781 Reputation points
    2022-06-30T14:23:06.997+00:00

    After reading the documentation provided by @AgaveJoe and writing some changes in authentication configuration, I found how to solve the problem.

    In
    Startup.ConfigureServices()
    method I configure cookie and JWT bearer authentication schemes:

    services  
        .AddAuthentication() // Cookie by default  
        .AddCookie(options =>  
        {  
            options.LoginPath = "/Account/Unauthorized/";  
            options.AccessDeniedPath = "/Account/Forbidden/";  
        })  
        .AddJwtBearer(options =>  
        {  
            options.TokenValidationParameters = new TokenValidationParameters  
            {  
                ValidateIssuer = true,  
                ValidateAudience = true,  
                ValidateLifetime = true,  
                ValidateIssuerSigningKey = true,  
                ValidIssuer = Configuration["Jwt:Issuer"],  
                ValidAudience = Configuration["Jwt:Issuer"],  
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))  
            };  
        });  
    

    With this all controller actions and pages that have
    [Authorize]
    attribute will use cookie scheme. In Web API controllers you have to specify JWT bearer scheme in the attribute:

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]  
    

    One note. Using the attribute in this way doesn't work:

    [Authorize(JwtBearerDefaults.AuthenticationScheme)]  
    
    1 person found this answer helpful.
    0 comments No comments

  2. AgaveJoe 30,126 Reputation points
    2022-06-30T13:05:07.863+00:00

    Yes, I did. In the complete error, headers are there: Authorization: Bearer hereWasTheToken

    Can you clarify? Requests to the web application are authorized but requests to Web API fail with "The AuthorizationPolicy named: 'Bearer' was not found."? Or accessing the Web application causes this error?

    If a call to the web application is causing this error then you've configured the web application to look for a bearer token.

    If a call to the Web API application causes this error then you did not add the bearer token to the HTTP header.

    Unfortunately, you have not shared all the relevant code. All I can tell you is the link I provided in my first post explains how to setup multiple authentication schemes and it work perfectly on my end. Perhaps create a minimal example with cookie authentication and JWT.


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.