Search code examples
c#.netjwtasp.net-identity.net-7.0

.net 7: "Some services are not able to be constructed" while implementing jwt


I'm trying to implement JWT in my API following this tutorial:

https://medium.com/geekculture/how-to-add-jwt-authentication-to-an-asp-net-core-api-84e469e9f019

but when launching the API I get the following error:

System.AggregateException: 'Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory`1[Microsoft.AspNetCore.Identity.IdentityUser] Lifetime: Scoped ImplementationType: Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory`1[Microsoft.AspNetCore.Identity.IdentityUser]': Unable to resolve service for type 'Microsoft.AspNetCore.Identity.IUserStore`1[Microsoft.AspNetCore.Identity.IdentityUser]' while attempting to activate 'Microsoft.AspNetCore.Identity.UserManager`1[Microsoft.AspNetCore.Identity.IdentityUser]'.) (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Identity.UserManager`1[Microsoft.AspNetCore.Identity.IdentityUser] Lifetime: Scoped ImplementationType: Microsoft.AspNetCore.Identity.UserManager`1[Microsoft.AspNetCore.Identity.IdentityUser]': Unable to resolve service for type 'Microsoft.AspNetCore.Identity.IUserStore`1[Microsoft.AspNetCore.Identity.IdentityUser]' while attempting to activate 'Microsoft.AspNetCore.Identity.UserManager`1[Microsoft.AspNetCore.Identity.IdentityUser]'.)'

Program.cs

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Neo4jClient;
using survey.Data;
using survey.Data.Entities;
using surveys.domain;
using surveys.repository;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

#region jwt data
var validIssuer = builder.Configuration["JWT:ValidIssuer"];
var validAudience = builder.Configuration["JWT:ValidAudience"];
var issuerSigningKey = builder.Configuration["JWT:IssuerSigningKey"];
#endregion

if (validIssuer != null && validAudience != null && issuerSigningKey != null) 
{
    #region jwt
    builder.Services
    .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters()
        {
            ClockSkew = TimeSpan.Zero,
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = validIssuer,
            ValidAudience = validAudience,
            IssuerSigningKey = new SymmetricSecurityKey(
                Encoding.UTF8.GetBytes(issuerSigningKey)
            ),
        };
    });
    #endregion

    #region DbContext
    //builder.Services.AddDbContext<SurveysDbContext>(options =>
    //{
    //    options.UseSqlServer(builder.Configuration.GetConnectionString("SurveyConnection"));
    //});
    //builder.Services.AddDbContext<UsersDbContext>(options =>
    //{
    //    options.UseSqlServer(builder.Configuration.GetConnectionString("SurveyConnection"));
    //});
    #endregion

    #region neo4j data
    var neo4jServer = builder.Configuration["Neo4J:server"];
    var neo4jUser = builder.Configuration["Neo4J:user"];
    var neo4jPwd = builder.Configuration["Neo4J:pwd"];
    #endregion

    if (neo4jServer != null && neo4jUser != null && neo4jPwd != null)
    {
        #region neo4j
        var client = new BoltGraphClient(new Uri(neo4jServer), neo4jUser, neo4jPwd);
        client.ConnectAsync();
        #endregion

        #region services
        //builder.Services.AddTransient<IQuestionsRepository, QuestionsRepository>();
        builder.Services.AddSingleton<QuestionsRepository>();
        builder.Services.AddSingleton<QuestionsDomain>();
        builder.Services.Configure<RouteOptions>(options => options.LowercaseUrls = true);
        builder.Services.AddSingleton<IGraphClient>(client);
        #endregion

        #region users
        builder.Services
            .AddIdentityCore<IdentityUser>(options =>
            {
                options.SignIn.RequireConfirmedAccount = false;
                options.User.RequireUniqueEmail = true;
                options.Password.RequireDigit = false;
                options.Password.RequiredLength = 6;
                options.Password.RequireNonAlphanumeric = false;
                options.Password.RequireUppercase = false;
                options.Password.RequireLowercase = false;
            }).AddRoles<IdentityRole>().AddEntityFrameworkStores<UsersDbContext>();
        #endregion

        var app = builder.Build();

        // Configure the HTTP request pipeline.
        //if (app.Environment.IsDevelopment())
        //{
        app.UseSwagger();
        app.UseSwaggerUI();
        //}

        #region swagger default page in release mode
        if (!app.Environment.IsDevelopment())
        {
            app.UseSwaggerUI(options =>
            {
                options.SwaggerEndpoint("/swagger/v1/swagger.json", "Surveys Web API");
                options.RoutePrefix = string.Empty;
            });
            app.UseHsts();
        }
        #endregion

        app.UseHttpsRedirection();
        app.UseAuthorization();
        app.UseAuthentication();
        app.MapControllers();

        app.Run();
    }
    else
    {
        throw new Exception("Neo4J is not configured!");
    }
}
else
{
    throw new Exception("JWT is not configured!");
}

AuthController:

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using survey.Data.Entities;
using System.Security.Claims;

namespace survey.Controllers;

[ApiController]
[Route("[controller]")]
public class AuthController : ControllerBase
{
    private readonly UserManager<IdentityUser> _userManager;
    public AuthController(UserManager<IdentityUser> userManager)
    {
        _userManager = userManager;
    }

    [HttpGet("Login")]
    public IActionResult Login()
    {
        var identity = User.Identity as ClaimsIdentity;
        return Ok("Hello " + identity?.Name);
    }

    [HttpPost]
    [Route("register")]
    public async Task<IActionResult> Register(User user)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        var result = await _userManager.CreateAsync(
            new IdentityUser { UserName = user.Username, Email = user.Email },
            user.Passwd
        );
        if (result.Succeeded)
        {
            user.Passwd = "";
            return CreatedAtAction(nameof(Register), new { email = user.Email }, user);
        }
        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(error.Code, error.Description);
        }
        return BadRequest(ModelState);
    }
}

UsersDbContext:

using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace survey.Data
{
    public class UsersDbContext : IdentityUserContext<IdentityUser>
    {
        public UsersDbContext()
        {
        }

        public UsersDbContext(DbContextOptions<UsersDbContext> context) : base(context)
        {
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                IConfigurationRoot configuration = new ConfigurationBuilder()
                   .SetBasePath(Directory.GetCurrentDirectory())
                   .AddJsonFile("appsettings.json")
                   .Build();
                var connectionString = configuration.GetConnectionString("SurveyConnection");
                optionsBuilder.UseSqlServer(connectionString);
            }
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
        }
    }
}

Users.cs:

using System.ComponentModel.DataAnnotations;

namespace survey.Data.Entities
{
    public class User
    {
        public int Id { get; set; }
        
        [Required]
        public int IdRole { get; set; }

        [Required]
        public string Email { get; set; } = "";

        [Required]
        public string Username { get; set; } = "";

        [Required]
        public string Passwd { get; set; } = "";

        [Required]
        public string Name { get; set; } = "";
    }
}

I've been reading several similar posts like this:

Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UserManager` while attempting to activate 'AuthController'

to no avail. I always get the same error.

What can I try next?


Solution

  • I forgot to add:

    builder.Services.AddDbContext<UsersDbContext>();
    

    before

    .AddRoles<IdentityRole>().AddEntityFrameworkStores<UsersDbContext>();
    

    It was a simple as that.