Search code examples
c#asp.netasp.net-coreasp.net-web-api

I Have Problem For Create User with Identity


I use asp.net core 8 - identity for WebApi

Error Message : System.InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Identity.SignInManager`1[TodoList.Api.Entity.UsersAccount]' while attempting to activate 'TodoList.Api.Controllers.AuthorizeController'. at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired) at lambda_method9(Closure, IServiceProvider, Object[]) at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass6_0.g__CreateController|0(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker) at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

HEADERS

Accept: text/plain Connection: keep-alive Host: localhost:5220 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3 Safari/605.1.15 Accept-Encoding: gzip, deflate Accept-Language: en-US,en;q=0.9 Origin: http://localhost:5220 Referer: http://localhost:5220/swagger/index.html Content-Length: 0 Sec-Fetch-Site: same-origin Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty

AuthorizeController:

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using AutoMapper;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using TodoList.Api.DbContextFolder;
using TodoList.Api.Entity;
using TodoList.Api.Model;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace TodoList.Api.Controllers
{
    [Route("api/Authorize")]
    public class AuthorizeController : Controller
    {
        private readonly TaskDbContext DB;
        private readonly IConfiguration configuration;
        private readonly IMapper _mapper;
        private readonly SignInManager<UsersAccount> _signInManager;

        public AuthorizeController(IConfiguration configuration, TaskDbContext DB, IMapper mapper,
            SignInManager<UsersAccount> signInManager)
        {
            this._mapper = mapper;
            _signInManager = signInManager;
            this.DB = DB;
            this.configuration = configuration;
        }


        [HttpPost("Login")]
        public async Task<IActionResult> AuthoticationAsync(LoginDTO login)
        {
            //return Ok();
            //var userName = DB.UsersAccounts.FirstOrDefaultAsync(a => a.UserName == login.UserName);
            var CheckUser = await _signInManager.PasswordSignInAsync(login.UserName, login.Password, false, false);
            if (CheckUser.Succeeded)
            {
                var user = await _signInManager.UserManager.FindByNameAsync(login.UserName);
                Claim cl;
                var temp = user as UsersAccount;
                if (temp!.IsAdmin)
                {
                    cl = new Claim("Admin", "true");
                }
                else
                {
                    cl = new Claim("Admin", "false");
                }

                var clims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, temp.Name),
                    new Claim("Id", temp.Id!.ToString()),
                    cl
                };

                var ExpireToken = DateTime.UtcNow.AddDays(2);
                return Ok(new
                {
                    access_token = CreateToken(clims, ExpireToken),
                    expire_at = ExpireToken,
                });
            }

            ModelState.AddModelError("UnAuthorized", "Your Not Access This Api Before Cant Login :( ");
            return Unauthorized();

            //var user = _signInManager.UserManager.Users.Include(x => x.IsAdmin);


            // if ()
            // {
            //     
            // }
        

        //return Ok();
        // var checkUser =
        //     DB.UsersAccounts.FirstOrDefault(a => a.UserName == login.UserName && a.Password == login.Password);
        // if (checkUser != null)
        // {
        //     Claim cl;
        //
        //     if (checkUser.IsAdmin == true)
        //     {
        //         cl = new Claim("Admin", "true");
        //     }
        //     else
        //     {
        //         cl = new Claim("Admin", "false");
        //     }
        //
        //     var clims = new List<Claim>
        //     {
        //         new Claim(ClaimTypes.Name, checkUser.Name),
        //         new Claim("Id", checkUser.UserId.ToString()),
        //         cl
        //     };
        //
        //
        //     var ExpireToken = DateTime.UtcNow.AddDays(2);
        //     return Ok(new
        //     {
        //         access_token = CreateToken(clims, ExpireToken),
        //         expire_at = ExpireToken,
        //     });
        // }
        //
        // ModelState.AddModelError("UnAuthorized", "Your Not Access This Api Before Cant Login :( ");
        // return Unauthorized();
    }


private string CreateToken(IEnumerable<Claim> claims, DateTime ExpireDate)
        {
            var SecretKey = Encoding.ASCII.GetBytes(configuration.GetValue<string>("SecretKey") ?? "");
            var jwt = new JwtSecurityToken(
                claims: claims,
                notBefore: DateTime.UtcNow,
                expires: ExpireDate,
                signingCredentials: new SigningCredentials(new SymmetricSecurityKey(SecretKey),
                    SecurityAlgorithms.HmacSha256Signature)
            );
            return new JwtSecurityTokenHandler().WriteToken(jwt);
        }

        [HttpPost("CreateUser")]
        public async Task<ActionResult<UsersAccount>> CreateUserAsync(CreateUserDTO user)
        {

            //var UserMapper = _mapper.Map<UsersAccount>(user);
            
            if (user != null)
            {
                var UserMapper = _mapper.Map<UsersAccount>(user);
                var CreateUser = await _signInManager.UserManager.CreateAsync(UserMapper,user.password);
                //await DB.UsersAccounts.AddAsync(UserMapper);
                if (!CreateUser.Succeeded)
                {
                    string mess = 
                        CreateUser
                            .Errors
                            .Select(x=>x.Description)
                            .Aggregate((x, y) => x + " " + y);

                    return Ok("Error:" + mess);
                }
                
                //DB.SaveChanges();

                return Ok(UserMapper);
            }

            return BadRequest("Somethingwent wrong");
        }

        [Authorize(policy: "AdminOnly")]
        [HttpPut("EditUser")]
        public ActionResult<IsAdminEditDTO> EditUser(int Id, bool value)
        {
            var IdFind = DB.UsersAccounts.Find(Id);
            if (IdFind == null)
            {
                return NotFound();
            }

            ;
            var DataNew = DB.UsersAccounts.Select(x => x.IsAdmin).FirstOrDefault();
            IdFind.IsAdmin = value;
            DB.SaveChanges();

            return Ok(IdFind.IsAdmin);
        }

        // Admin_Part for Create , Edit , Delete for Admin Access 

        


        // GET: api/values
        //[HttpGet]
        //public IEnumerable<string> Get()
        //{
        //    return new string[] { "value1", "value2" };
        //}

        // GET api/values/5
        //[HttpGet("{id}")]
        //public string Get(int id)
        //{
        //    return "value";
        //}

        // POST api/values
        //[HttpPost]
        //public void Post([FromBody]string value)
        //{
        //}

        // PUT api/values/5
        //[HttpPut("{id}")]
        //public void Put(int id, [FromBody]string value)
        //{
        //}

        // DELETE api/values/5
        //[HttpDelete("{id}")]
        //public void Delete(int id)
        //{
        //}
    }
}

program.cs :

using System.Text;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using TodoList.Api.DbContextFolder;


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();
builder.Services.AddAutoMapper(typeof(Program));
builder.Services.AddDbContext<TaskDbContext>(options =>
{
    options.UseSqlite(builder.Configuration["ConnectionStrings:TaskConnection"]);
});

builder.Services.AddIdentity<IdentityUser, IdentityRole>(options =>
{
    options.Password.RequiredLength = 3;
    options.Password.RequireLowercase = false;
    options.Password.RequireUppercase = false;
    options.Lockout.MaxFailedAccessAttempts = 5;
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(6);
    options.User.RequireUniqueEmail = false;
    options.SignIn.RequireConfirmedEmail = false;
}).AddEntityFrameworkStores<TaskDbContext>().AddDefaultTokenProviders();
builder.Services.ConfigureApplicationCookie(options =>
{
    //options.LoginPath = "Account/Login";
    //options.AccessDeniedPath = "Account/AccessDenied";
});

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
        options.JsonSerializerOptions.WriteIndented = true;
    }).AddNewtonsoftJson().AddXmlDataContractSerializerFormatters();
var secretkey = builder.Configuration.GetValue<string>("SecretKey");
builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretkey ?? string.Empty)),
        ValidateLifetime = true,
        ValidateAudience = false,
        ValidateIssuer = false,
        ClockSkew = TimeSpan.Zero
    };
});
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy => policy.RequireClaim("Admin", "true"));
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    //app.UseSwaggerUI();
    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/swagger/v1/swagger.json", "Your API v2");
        //options.RoutePrefix = string.Empty; // Set the Swagger UI at the root URL
    });
    app.UseHttpLogging();
}

app.UseHttpsRedirection();

app.UseAuthentication();

//app.MapControllers();
app.UseRouting();

app.UseAuthorization();
app.MapControllers();

//app.UseEndpoints(endpoint =>
//{

//});
app.Run();

userAccount:

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

namespace TodoList.Api.Entity
{
    [Index(nameof(UserName), IsUnique = true)]
    public class UsersAccount :IdentityUser
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        //public string? UserId { get; set; }

        //[Required] [MaxLength(100)] public string UserName { get; set; } = string.Empty;
        //[Required] [MaxLength(100)] public string password { get; set; } = string.Empty;
        [Required] [MaxLength(100)] public string Name { get; set; } = string.Empty;
        [Required] [MaxLength(100)] public int Age { get; set; }
        [Required] public bool IsAdmin { get; set; } = false;
    }
}

Solution

  • You need to assign your custom IdentityUser class - UsersAccount in DI as follows:

    builder.Services.AddIdentity<UsersAccount, IdentityRole>(options =>
    {
        options.Password.RequiredLength = 3;
        options.Password.RequireLowercase = false;
        options.Password.RequireUppercase = false;
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(6);
        options.User.RequireUniqueEmail = false;
        options.SignIn.RequireConfirmedEmail = false;
    }).AddEntityFrameworkStores<TaskDbContext>().AddDefaultTokenProviders();
    

    Additionally, if you have a custom Role class such as UsersRole you can also assign it like so .AddIdentity<UsersAccount, UsersRole>.