Home > Mobile >  ASP.NET Core Web API - How to create user and validate if not exist during validation
ASP.NET Core Web API - How to create user and validate if not exist during validation

Time:12-02

In ASP.NET Core-6 Web API, I am implementing User Authentication using Identity DB Context and Entity Framework.

I have this code.

Model:

public class AppUser : IdentityUser
{
    public bool IsActive { get; set; }
    public Guid RefreshToken { get; set; }
    public DateTime RefreshTokenExpiryTime { get; set; }
}

Dto:

public class RegisterUserDto
{
    public string Email { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
}

public class LoginResponseDto
{
    public string Id { get; set; }
    public string Token { get; set; }
    public Guid RefreshToken { get; set; }
}

public class LoginDto
{
    public string Email { get; set; }
    public string Password { get; set; }
}

Then I have this Interface Service for Authentication.

IAuthenticationService:

public interface IAuthenticationService
{
    Task<Response<string>> Register(RegisterUserDto userDto);
    Task<Response<LoginResponseDto>> Login(LoginDto loginDto);
}

Then I have the Service implementation for User Registration and User Login.

AuthenticationService:

public class AuthenticationService : IAuthenticationService
{
    private readonly UserManager<AppUser> _userManager;
    private readonly IMapper _mapper;
    private readonly ITokenGeneratorService _tokenGenerator;
    private readonly IUnitOfWork _unitOfWork;
    private readonly ILogger _logger;

    public AuthenticationService(UserManager<AppUser> userManager, IUnitOfWork unitOfWork, ILogger logger,
        IMapper mapper, ITokenGeneratorService tokenGenerator)
    {
        _userManager = userManager;
        _mapper = mapper;
        _tokenGenerator = tokenGenerator;
        _unitOfWork = unitOfWork;
        _logger = logger;

    }

    private async Task<Response<bool>> ValidateUser(LoginDto model)
    {
        var user = await _userManager.FindByEmailAsync(model.Email);
        var response = new Response<bool>();
        if(user == null || !await _userManager.CheckPasswordAsync(user, model.Password))
        {
            response.Message = "Invalid Credentials";
            response.Succeeded = false;
            response.StatusCode = (int)HttpStatusCode.BadRequest;
            return response;
        }
        if(!await _userManager.IsEmailConfirmedAsync(user) && user.IsActive)
        {
            response.Message = "Account not activated";
            response.Succeeded = false;
            response.StatusCode = (int)HttpStatusCode.Forbidden;
            return response;
        }
        else
        {
            response.Succeeded = true;
            return response;
        }
    }

    public async Task<Response<LoginResponseDto>> Login(LoginDto model)
    {
        var response = new Response<LoginResponseDto>();
        var validityResult = await ValidateUser(model);

        if (!validityResult.Succeeded)
        {
            _logger.Error("Login operation failed");
            response.Message = validityResult.Message;
            response.StatusCode = validityResult.StatusCode;
            response.Succeeded = false;
            return response;
        }

        var user = await _userManager.FindByEmailAsync(model.Email);
        var refreshToken = _tokenGenerator.GenerateRefreshToken();
        user.RefreshToken = refreshToken;
        user.RefreshTokenExpiryTime = DateTime.Now.AddDays(7); 
        var result = new LoginResponseDto()
        {
            Id = user.Id,
            Token = await _tokenGenerator.GenerateToken(user),
            RefreshToken = refreshToken
        };
        
        await _userManager.UpdateAsync(user);

        _logger.Information("User successfully logged in");
        response.StatusCode = (int)HttpStatusCode.OK;
        response.Message = "Login Successfully";
        response.Data = result;
        response.Succeeded = true;
        return response;
    }

    public async Task<Response<string>> Register(RegisterUserDto model)
    {
        var user = _mapper.Map<AppUser>(model);
        user.IsActive = true;
        var response = new Response<string>();
        using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
        {
            var result = await _userManager.CreateAsync(user, model.Password);
            if (result.Succeeded)
            {
                await _userManager.AddToRoleAsync(user, UserRoles.Customer);
                var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                var encodedToken = TokenConverter.EncodeToken(token);
                var userRole = await _userManager.GetRolesAsync(user);

                    var customer = new Customer
                    {
                        AppUser = user
                    };
                    await _unitOfWork.Customers.InsertAsync(customer);
                    await _unitOfWork.Save();
                    response.StatusCode = (int)HttpStatusCode.Created;
                    response.Succeeded = true;
                    response.Data = user.Id;
                    response.Message = "User created successfully!";
                    transaction.Complete();
                    return response;
            }
            response.Message = GetErrors(result);
            response.StatusCode = (int)HttpStatusCode.BadRequest;
            response.Succeeded = false;
            transaction.Complete();
            return response;
        };

    }
}

And finally the Controller.

Controller

public class AuthenticationController : ControllerBase
{
    private readonly ILogger _logger;
    private readonly IAuthenticationService _authService;

    public AuthenticationController(ILogger logger, IAuthenticationService authService)
    {
        _logger = logger;
        _authService = authService;
    }

    [HttpPost]
    [Route("register")]
    public async Task<ActionResult<Response<LoginResponseDto>>> Register([FromBody] RegisterUserDto model)
    {
        _logger.Information($"Registration Attempt for {model.Email}");
        var result = await _authService.Register(model);
        return StatusCode(result.StatusCode, result);
    }

    [HttpPost]
    [Route("login")]
    public async Task<ActionResult<Response<string>>> Login([FromBody] LoginDto model)
    {
        _logger.Information($"Login Attempt for {model.Email}");
        var result = await _authService.Login(model);
        return StatusCode(result.StatusCode, result);
    }
}

At the moment, I use the Register Method to register User and the Login Method for user Login and Authentication.

In the Login Service method, the user credentials are validated. Then if successful, he logs in.

However, I want to change this based on demand. If a user tries to login and is validated. If he does not exist, the application should use the provided credentials to Register the user, validate him, and also login the user automatically (note that Email should also be used as UserName).

How do I achieve this using the Login Service method?

Thanks

CodePudding user response:

If i understood correctly you should code to your ValidateUser that checks if user exists or not. If the user dosen't exist it should register it , validate it's email and re-log it properly.

public class AuthenticationService : IAuthenticationService
{
    private readonly UserManager<AppUser> _userManager;
    private readonly IMapper _mapper;
    private readonly ITokenGeneratorService _tokenGenerator;
    private readonly IUnitOfWork _unitOfWork;
    private readonly ILogger _logger;

    public AuthenticationService(UserManager<AppUser> userManager, IUnitOfWork unitOfWork, ILogger logger,
        IMapper mapper, ITokenGeneratorService tokenGenerator)
    {
        _userManager = userManager;
        _mapper = mapper;
        _tokenGenerator = tokenGenerator;
        _unitOfWork = unitOfWork;
        _logger = logger;

    }

    private async Task<Response<bool>> ValidateUser(LoginDto model)
    {
        var user = await _userManager.FindByEmailAsync(model.Email);
        var response = new Response<bool>();
        if (user == null)
        {
            var registerModel = new RegisterUserDto
            {
                Email = model.Email,
                UserName = model.Email,
                Password = model.Password,
            };
           var  resgistration = await Register(registerModel);
            if(resgistration.Succeeded)
            {
                var token = _userManager.GenerateUserTokenAsync(user);
                _userManager.ConfirmEmailAsync(user, token);
            }
            return Login(model);
         }
        if (!await _userManager.CheckPasswordAsync(user, model.Password))
        {
            response.Message = "Invalid Credentials";
            response.Succeeded = false;
            response.StatusCode = (int)HttpStatusCode.BadRequest;
            return response;
        }
        if (!await _userManager.IsEmailConfirmedAsync(user) && user.IsActive)
        {
            response.Message = "Account not activated";
            response.Succeeded = false;
            response.StatusCode = (int)HttpStatusCode.Forbidden;
            return response;
        }
        else
        {
            response.Succeeded = true;
            return response;
        }
    }

    public async Task<Response<LoginResponseDto>> Login(LoginDto model)
    {
        var response = new Response<LoginResponseDto>();
        var validityResult = await ValidateUser(model);

        if (!validityResult.Succeeded)
        {
            _logger.Error("Login operation failed");
            response.Message = validityResult.Message;
            response.StatusCode = validityResult.StatusCode;
            response.Succeeded = false;
            return response;
        }

        var user = await _userManager.FindByEmailAsync(model.Email);
        var refreshToken = _tokenGenerator.GenerateRefreshToken();
        user.RefreshToken = refreshToken;
        user.RefreshTokenExpiryTime = DateTime.Now.AddDays(7);
        var result = new LoginResponseDto()
        {
            Id = user.Id,
            Token = await _tokenGenerator.GenerateToken(user),
            RefreshToken = refreshToken
        };

        await _userManager.UpdateAsync(user);

        _logger.Information("User successfully logged in");
        response.StatusCode = (int)HttpStatusCode.OK;
        response.Message = "Login Successfully";
        response.Data = result;
        response.Succeeded = true;
        return response;
    }

    public async Task<Response<string>> Register(RegisterUserDto model)
    {
        var user = _mapper.Map<AppUser>(model);
        user.IsActive = true;
        var response = new Response<string>();
        using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
        {
            var result = await _userManager.CreateAsync(user, model.Password);
            if (result.Succeeded)
            {
                await _userManager.AddToRoleAsync(user, UserRoles.Customer);
                var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
                var encodedToken = TokenConverter.EncodeToken(token);
                var userRole = await _userManager.GetRolesAsync(user);

                var customer = new Customer
                {
                    AppUser = user
                };
                await _unitOfWork.Customers.InsertAsync(customer);
                await _unitOfWork.Save();
                response.StatusCode = (int)HttpStatusCode.Created;
                response.Succeeded = true;
                response.Data = user.Id;
                response.Message = "User created successfully!";
                transaction.Complete();
                return response;
            }
            response.Message = GetErrors(result);
            response.StatusCode = (int)HttpStatusCode.BadRequest;
            response.Succeeded = false;
            transaction.Complete();
            return response;
        };

    }
}
  • Related