Authentication and authorization using JWT in asp.net core application
Introduction
Authentication and authorization is a key part of an application. Sometimes we struggle to implement authentication and authorization in a web api application. In this article I will show you, how to implement authentication and authorization using JWT in an asp.net core web application.
Tools and technologies used
- Clean Architecture
- CQRS Pattern
- Visual Studio 2022
- .NET 6.0
- SQLite
- Dapper
- Entity Framework
- AutoMapper
- MediatR
- ASP.NET Core Web API
- C#
Implementation
Step 1: Create solution and projects.
- Create a solution name Ecommerce
- Add a new web api project, name - Ordering.API in the solution.
- Add 3 class library project, name - Ordering.Application, Odering.Core and Ordering.Infrastructure in the solution.
Step 2: Install nuget packages.
- Install following nuget packages in Ordering.Infrastructure Project
PM> Install-Package Dapper
PM> Install-Package Microsoft.Data.Sqlite.Core
PM> Install-Package Microsoft.EntityFrameworkCore
PM> Install-Package Microsoft.EntityFrameworkCore.Design
PM> Install-Package Microsoft.EntityFrameworkCore.Relational
PM> Install-Package Microsoft.EntityFrameworkCore.Tools
PM> Install-Package Microsoft.Extensions.Configuration.Abstractions
PM> Install-Package Microsoft.AspNetCore.Identity
PM> Install-Package Microsoft.AspNetCore.Identity.EntityFrameworkCore
PM> Install-Package Microsoft.EntityFrameworkCore.Sqlite
- Install following nuget packages in Ordering.Application Project
PM> Install-Package AutoMapper
PM> Install-Package MediatR
PM> Install-Package FluentValidation
PM> Install-Package FluentValidation.DependencyInjectionExtensions
PM> Install-Package Microsoft.Extensions.DependencyInjection.Abstractions
PM> Install-Package Microsoft.Extensions.Identity.Core
PM> Install-Package Microsoft.IdentityModel.Tokens
PM> Install-Package System.IdentityModel.Tokens.Jwt
- Install following nuget packages in Ordering.API Project
PM> Install-Package AutoMapper.Extensions.Microsoft.DependencyInjection
PM> Install-Package MediatR
PM> Install-Package MediatR.Extensions.Microsoft.DependencyInjection
PM> Install-Package Microsoft.EntityFrameworkCore.Design
PM> Install-Package Microsoft.EntityFrameworkCore.Sqlite
PM> Install-Package Microsoft.EntityFrameworkCore.Tools
PM> Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
PM> Install-Package System.Configuration.ConfigurationManager
Step 3: Create Entity class in Ordering.Core project
- Create BaseEntity in Ordering.Core/Entities/Base folder
- Create Customer in Ordering.Core/Entities folder
BaseEntity.cs
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Ordering.Core.Entities.Base
{
public class BaseEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Int64 Id { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; private set; }
public BaseEntity()
{
this.ModifiedDate = DateTime.Now;
}
}
}
Customer.cs
using Ordering.Core.Entities.Base;
namespace Ordering.Core.Entities
{
public class Customer : BaseEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string ContactNumber { get; set; }
public string Address { get; set; }
}
}
Step 4: Create Command and Query Interface in Ordering.Core project
- Create ICommandRepository in Ordering.Core/Repositories/Command/Base folder
- Create ICommandCustomerRepository in Ordering.Core/Repositories/Command folder
- Create IQueryRepository in Ordering.Core/Repositories/Query/Base folder
- Create ICustomerQueryRepository in Ordering.Core/Repositories/Query folder
ICommandRepository.cs
using System.Threading.Tasks;
namespace Ordering.Core.Repositories.Command.Base
{
public interface ICommandRepository<T> where T : class
{
Task<T> AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(T entity);
}
}
ICustomerCommandRepository.cs
using Ordering.Core.Entities;
using Ordering.Core.Repositories.Command.Base;
namespace Ordering.Core.Repositories.Command
{
public interface ICustomerCommandRepository : ICommandRepository<Customer>
{
}
}
IQueryRepository.cs
namespace Ordering.Core.Repositories.Query.Base
{
public interface IQueryRepository <T> where T : class
{
// Generic repository for all if any
}
}
ICustomerQueryRepository.cs
using Ordering.Core.Entities;
using Ordering.Core.Repositories.Query.Base;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Ordering.Core.Repositories.Query
{
public interface ICustomerQueryRepository : IQueryRepository<Customer>
{
//Custom operation which is not generic
Task<IReadOnlyList<Customer>> GetAllAsync();
Task<Customer> GetByIdAsync(Int64 id);
Task<Customer> GetCustomerByEmail(string email);
}
}
Step 5: Create DbConnector and OrderingContext in Ordering.Infrastructure project
- Create DbConnector class in Ordering.Infrastructure/Data folder for SQLite connection for query.
DbConnector.cs
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Configuration;
using System.Data;
namespace Ordering.Infrastructure.Data
{
public class DbConnector
{
private readonly IConfiguration _configuration;
protected DbConnector(IConfiguration configuration)
{
_configuration = configuration;
}
public IDbConnection CreateConnection()
{
string _connectionString = _configuration.GetConnectionString("DefaultConnection");
return new SqliteConnection(_connectionString);
}
}
}
- Create OrderingContext class in Ordering.Infrastructure/Data folder for command.
OrderingContext.cs
using Microsoft.EntityFrameworkCore;
using Ordering.Core.Entities;
namespace Ordering.Infrastructure.Data
{
public class OrderingContext : DbContext
{
public OrderingContext(DbContextOptions<OrderingContext> options) : base (options)
{
}
public DbSet<Customer> Customers { get; set; }
}
}
Step 6: Create Command and Query Repository in Ordering.Infrastructure project
- Create CommandRepository in Ordering.Infrastructure/Repositories/Command/Base folder
CommandRepository.cs
using Microsoft.EntityFrameworkCore;
using Ordering.Core.Repositories.Command.Base;
using Ordering.Infrastructure.Data;
using System.Threading.Tasks;
namespace Ordering.Infrastructure.Repository.Command.Base
{
public class CommandRepository<T> : ICommandRepository<T> where T : class
{
protected readonly OrderingContext _context;
public CommandRepository(OrderingContext context)
{
_context = context;
}
public async Task<T> AddAsync(T entity)
{
await _context.Set<T>().AddAsync(entity);
await _context.SaveChangesAsync();
return entity;
}
public async Task UpdateAsync(T entity)
{
_context.Entry(entity).State = EntityState.Modified;
await _context.SaveChangesAsync();
}
public async Task DeleteAsync(T entity)
{
_context.Set<T>().Remove(entity);
await _context.SaveChangesAsync();
}
}
}
- Create CommandCustomerRepository in Ordering.Infrastructure/Repositories/Command folder
CustomerCommandRepository.cs
using Ordering.Core.Entities;
using Ordering.Core.Repositories.Command;
using Ordering.Infrastructure.Data;
using Ordering.Infrastructure.Repository.Command.Base;
namespace Ordering.Infrastructure.Repository.Command
{
public class CustomerCommandRepository : CommandRepository<Customer>, ICustomerCommandRepository
{
public CustomerCommandRepository(OrderingContext context) : base(context)
{
}
}
}
- Create QueryRepository in Ordering.Infrastructure/Repositories/Query/Base folder
QueryRepository.cs
using Microsoft.Extensions.Configuration;
using Ordering.Core.Repositories.Query.Base;
using Ordering.Infrastructure.Data;
namespace Ordering.Infrastructure.Repository.Query.Base
{
public class QueryRepository<T> : DbConnector, IQueryRepository<T> where T : class
{
public QueryRepository(IConfiguration configuration)
: base(configuration)
{
}
}
}
- Create CustomerQueryRepository in Ordering.Infrastructure/Repositories/Query folder
CustomerQueryRepository.cs
using Dapper;
using Microsoft.Extensions.Configuration;
using Ordering.Core.Entities;
using Ordering.Core.Repositories.Query;
using Ordering.Infrastructure.Repository.Query.Base;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
namespace Ordering.Infrastructure.Repository.Query
{
public class CustomerQueryRepository : QueryRepository<Customer>, ICustomerQueryRepository
{
public CustomerQueryRepository(IConfiguration configuration)
: base(configuration)
{
}
public async Task<IReadOnlyList<Customer>> GetAllAsync()
{
try
{
var query = "SELECT * FROM CUSTOMERS";
using (var connection = CreateConnection())
{
return (await connection.QueryAsync<Customer>(query)).ToList();
}
}
catch (Exception exp)
{
throw new Exception(exp.Message, exp);
}
}
public async Task<Customer> GetByIdAsync(long id)
{
try
{
var query = "SELECT * FROM CUSTOMERS WHERE Id = @Id";
var parameters = new DynamicParameters();
parameters.Add("Id", id, DbType.Int64);
using (var connection = CreateConnection())
{
return (await connection.QueryFirstOrDefaultAsync<Customer>(query, parameters));
}
}
catch (Exception exp)
{
throw new Exception(exp.Message, exp);
}
}
public async Task<Customer> GetCustomerByEmail(string email)
{
try
{
var query = "SELECT * FROM CUSTOMERS WHERE Email = @email";
var parameters = new DynamicParameters();
parameters.Add("Email", email, DbType.String);
using (var connection = CreateConnection())
{
return (await connection.QueryFirstOrDefaultAsync<Customer>(query, parameters));
}
}
catch (Exception exp)
{
throw new Exception(exp.Message, exp);
}
}
}
}
- Add a property in ApplicationUser class in Ordering.Infrastructure/Identity/
ApplicationUser.cs
using Microsoft.AspNetCore.Identity;
namespace Ordering.Infrastructure.Identity
{
public class ApplicationUser : IdentityUser
{
public string? FullName { get; set; }
}
}
- Add IdentityService and TokenGenerator class in Ordering.Infrastructure/Services/
IdentityService.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Ordering.Application.Common.Exceptions;
using Ordering.Application.Common.Interfaces;
using Ordering.Infrastructure.Identity;
namespace Ordering.Infrastructure.Services
{
public class IdentityService : IIdentityService
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly RoleManager<IdentityRole> _roleManager;
public IdentityService(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager, RoleManager<IdentityRole> roleManager)
{
_userManager = userManager;
_signInManager = signInManager;
_roleManager = roleManager;
_roleManager = roleManager;
}
public async Task<bool> AssignUserToRole(string userName, IList<string> roles)
{
var user = await _userManager.Users.FirstOrDefaultAsync(x => x.UserName == userName);
if (user == null)
{
throw new NotFoundException("User not found");
}
var result = await _userManager.AddToRolesAsync(user, roles);
return result.Succeeded;
}
public async Task<bool> CreateRoleAsync(string roleName)
{
var result = await _roleManager.CreateAsync(new IdentityRole(roleName));
if(!result.Succeeded)
{
throw new ValidationException(result.Errors);
}
return result.Succeeded;
}
// Return multiple value
public async Task<(bool isSucceed, string userId)> CreateUserAsync(string userName, string password, string email, string fullName, List<string> roles)
{
var user = new ApplicationUser()
{
FullName = fullName,
UserName = userName,
Email = email
};
var result = await _userManager.CreateAsync(user, password);
if (!result.Succeeded)
{
throw new ValidationException(result.Errors);
}
var addUserRole = await _userManager.AddToRolesAsync(user, roles);
if (!addUserRole.Succeeded)
{
throw new ValidationException(addUserRole.Errors);
}
return (result.Succeeded, user.Id);
}
public async Task<bool> DeleteRoleAsync(string roleId)
{
var roleDetails = await _roleManager.FindByIdAsync(roleId);
if (roleDetails == null)
{
throw new NotFoundException("Role not found");
}
if (roleDetails.Name == "Administrator")
{
throw new BadRequestException("You can not delete Administrator Role");
}
var result = await _roleManager.DeleteAsync(roleDetails);
if (!result.Succeeded)
{
throw new ValidationException(result.Errors);
}
return result.Succeeded;
}
public async Task<bool> DeleteUserAsync(string userId)
{
var user = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == userId);
if (user == null)
{
throw new NotFoundException("User not found");
//throw new Exception("User not found");
}
if (user.UserName == "system" || user.UserName == "admin")
{
throw new Exception("You can not delete system or admin user");
//throw new BadRequestException("You can not delete system or admin user");
}
var result = await _userManager.DeleteAsync(user);
return result.Succeeded;
}
public async Task<List<(string id, string fullName, string userName, string email)>> GetAllUsersAsync()
{
var users = await _userManager.Users.Select(x => new
{
x.Id,
x.FullName,
x.UserName,
x.Email
}).ToListAsync();
return users.Select(user => (user.Id, user.FullName, user.UserName, user.Email)).ToList();
}
public Task<List<(string id, string userName, string email, IList<string> roles)>> GetAllUsersDetailsAsync()
{
throw new NotImplementedException();
//var roles = await _userManager.GetRolesAsync(user);
//return (user.Id, user.UserName, user.Email, roles);
//var users = _userManager.Users.ToListAsync();
}
public async Task<List<(string id, string roleName)>> GetRolesAsync()
{
var roles = await _roleManager.Roles.Select(x => new
{
x.Id,
x.Name
}).ToListAsync();
return roles.Select(role => (role.Id, role.Name)).ToList();
}
public async Task<(string userId, string fullName, string UserName, string email, IList<string> roles)> GetUserDetailsAsync(string userId)
{
var user = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == userId);
if (user == null)
{
throw new NotFoundException("User not found");
}
var roles = await _userManager.GetRolesAsync(user);
return (user.Id, user.FullName, user.UserName, user.Email, roles);
}
public async Task<(string userId, string fullName, string UserName, string email, IList<string> roles)> GetUserDetailsByUserNameAsync(string userName)
{
var user = await _userManager.Users.FirstOrDefaultAsync(x => x.UserName == userName);
if (user == null)
{
throw new NotFoundException("User not found");
}
var roles = await _userManager.GetRolesAsync(user);
return (user.Id, user.FullName, user.UserName, user.Email, roles);
}
public async Task<string> GetUserIdAsync(string userName)
{
var user = await _userManager.Users.FirstOrDefaultAsync(x => x.UserName == userName);
if (user == null)
{
throw new NotFoundException("User not found");
//throw new Exception("User not found");
}
return await _userManager.GetUserIdAsync(user);
}
public async Task<string> GetUserNameAsync(string userId)
{
var user = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == userId);
if (user == null)
{
throw new NotFoundException("User not found");
//throw new Exception("User not found");
}
return await _userManager.GetUserNameAsync(user);
}
public async Task<List<string>> GetUserRolesAsync(string userId)
{
var user = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == userId);
if (user == null)
{
throw new NotFoundException("User not found");
}
var roles = await _userManager.GetRolesAsync(user);
return roles.ToList();
}
public async Task<bool> IsInRoleAsync(string userId, string role)
{
var user = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == userId);
if(user == null)
{
throw new NotFoundException("User not found");
}
return await _userManager.IsInRoleAsync(user, role);
}
public async Task<bool> IsUniqueUserName(string userName)
{
return await _userManager.FindByNameAsync(userName) == null;
}
public async Task<bool> SigninUserAsync(string userName, string password)
{
var result = await _signInManager.PasswordSignInAsync(userName, password, true, false);
return result.Succeeded;
}
public async Task<bool> UpdateUserProfile(string id, string fullName, string email, IList<string> roles)
{
var user = await _userManager.FindByIdAsync(id);
user.FullName = fullName;
user.Email = email;
var result = await _userManager.UpdateAsync(user);
return result.Succeeded;
}
public async Task<(string id, string roleName)> GetRoleByIdAsync(string id)
{
var role = await _roleManager.FindByIdAsync(id);
return (role.Id, role.Name);
}
public async Task<bool> UpdateRole(string id, string roleName)
{
if (roleName != null)
{
var role = await _roleManager.FindByIdAsync(id);
role.Name = roleName;
var result = await _roleManager.UpdateAsync(role);
return result.Succeeded;
}
return false;
}
public async Task<bool> UpdateUsersRole(string userName, IList<string> usersRole)
{
var user = await _userManager.FindByNameAsync(userName);
var existingRoles = await _userManager.GetRolesAsync(user);
var result = await _userManager.RemoveFromRolesAsync(user, existingRoles);
result = await _userManager.AddToRolesAsync(user, usersRole);
return result.Succeeded;
}
}
}
TokenGenerator.cs
using Microsoft.IdentityModel.Tokens;
using Ordering.Application.Common.Interfaces;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
namespace Ordering.Infrastructure.Services
{
public class TokenGenerator : ITokenGenerator
{
private readonly string _key;
private readonly string _issuer;
private readonly string _audience;
private readonly string _expiryMinutes;
public TokenGenerator(string key, string issueer, string audience, string expiryMinutes)
{
_key = key;
_issuer = issueer;
_audience = audience;
_expiryMinutes = expiryMinutes;
}
public string GenerateJWTToken((string userId, string userName, IList<string> roles) userDetails)
{
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_key));
var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var (userId, userName, roles) = userDetails;
var claims = new List<Claim>()
{
new Claim(JwtRegisteredClaimNames.Sub, userName),
new Claim(JwtRegisteredClaimNames.Jti, userId),
new Claim(ClaimTypes.Name, userName),
new Claim("UserId", userId)
};
claims.AddRange(roles.Select(role => new Claim(ClaimTypes.Role, role)));
var token = new JwtSecurityToken(
issuer: _issuer,
audience: _audience,
claims: claims,
expires: DateTime.Now.AddMinutes(Convert.ToDouble(_expiryMinutes)),
signingCredentials: signingCredentials
);
var encodedToken = new JwtSecurityTokenHandler().WriteToken(token);
return encodedToken;
}
}
}
- Create DependencyInjection class in Ordering.Application root folder
DependencyInjection.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ordering.Application.Common.Interfaces;
using Ordering.Core.Repositories.Command;
using Ordering.Core.Repositories.Command.Base;
using Ordering.Core.Repositories.Query;
using Ordering.Core.Repositories.Query.Base;
using Ordering.Infrastructure.Data;
using Ordering.Infrastructure.Identity;
using Ordering.Infrastructure.Repository.Command;
using Ordering.Infrastructure.Repository.Command.Base;
using Ordering.Infrastructure.Repository.Query;
using Ordering.Infrastructure.Repository.Query.Base;
using Ordering.Infrastructure.Services;
namespace Ordering.Infrastructure
{
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(this IServiceCollection services,
IConfiguration configuration)
{
services.AddDbContext<OrderingContext>(options => options.UseSqlite(configuration.GetConnectionString("DefaultConnection"),
b => b.MigrationsAssembly(typeof(OrderingContext).Assembly.FullName)
));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<OrderingContext>()
.AddDefaultTokenProviders();
services.Configure<IdentityOptions>(options =>
{
// Default Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// Default Password settings.
options.Password.RequireDigit = false;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = false; // For special character
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 1;
// Default SignIn settings.
options.SignIn.RequireConfirmedEmail = false;
options.SignIn.RequireConfirmedPhoneNumber = false;
options.User.RequireUniqueEmail = true;
});
services.AddScoped<IIdentityService, IdentityService>();
services.AddScoped(typeof(IQueryRepository<>), typeof(QueryRepository<>));
services.AddTransient<ICustomerQueryRepository, CustomerQueryRepository>();
services.AddScoped(typeof(ICommandRepository<>), typeof(CommandRepository<>));
services.AddTransient<ICustomerCommandRepository, CustomerCommandRepository>();
return services;
}
}
}
Step 7: Now organize Ordering.Application layer
- Create DTO classes in Ordering.Application/DTOs folder
AuthResponseDTO.cs
namespace Ordering.Application.DTOs
{
public class AuthResponseDTO
{
public string UserId { get; set; }
public string Name { get; set; }
public string Token { get; set; }
}
}
CustomerResponse.cs
namespace Ordering.Application.DTOs
{
// Customer response or DTO class
public class CustomerResponse
{
public Int64 Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string ContactNumber { get; set; }
public string Address { get; set; }
}
}
RoleResponseDTO.cs
namespace Ordering.Application.DTOs
{
// Customer response or DTO class
public class CustomerResponse
{
public Int64 Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string ContactNumber { get; set; }
public string Address { get; set; }
}
}
UserDetailsResponseDTO.cs
namespace Ordering.Application.DTOs
{
public class UserDetailsResponseDTO
{
public string Id { get; set; }
public string FullName { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public IList<string> Roles { get; set; }
}
}
UserResponseDTO.cs
namespace Ordering.Application.DTOs
{
public class UserResponseDTO
{
public string Id { get; set; }
public string FullName { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
}
}
- Create Custom Exceptions classes in Ordering.Application/Common/Exceptions folder
BadRequestException.cs
namespace Ordering.Application.Common.Exceptions
{
public class BadRequestException : Exception
{
public BadRequestException() : base()
{
}
public BadRequestException(string message) : base(message)
{
}
public BadRequestException(string message, Exception exp) : base(message, exp)
{
}
}
}
ForbiddenAccessException.cs
namespace Ordering.Application.Common.Exceptions
{
public class ForbiddenAccessException : Exception
{
public ForbiddenAccessException() : base() { }
}
}
NotFoundException.cs
namespace Ordering.Application.Common.Exceptions
{
public class NotFoundException : Exception
{
public NotFoundException() : base()
{
}
public NotFoundException(string message) : base(message)
{
}
public NotFoundException(string message, Exception exp) : base(message, exp)
{
}
public NotFoundException(string name, object key)
: base($"Entity \"{name}\" ({key}) was not found.")
{
}
}
}
ValidationException.cs
using FluentValidation.Results;
using Microsoft.AspNetCore.Identity;
namespace Ordering.Application.Common.Exceptions
{
public class ValidationException : Exception
{
public ValidationException()
: base("One or more validation failures have occurred.")
{
Errors = new Dictionary<string, string[]>();
}
public ValidationException(IEnumerable<ValidationFailure> failures)
: this()
{
Errors = failures
.GroupBy(e => e.PropertyName, e => e.ErrorMessage)
.ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray());
}
public ValidationException(IEnumerable<IdentityError> errors) : this()
{
Errors = errors
.GroupBy(e => e.Code, e => e.Description)
.ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray());
}
public IDictionary<string, string[]> Errors { get; }
}
}
- Create some interface in Ordering.Application/Common/Interfaces
IIdentityService.cs
namespace Ordering.Application.Common.Interfaces
{
public interface IIdentityService
{
// User section
Task<(bool isSucceed, string userId)> CreateUserAsync(string userName, string password, string email, string fullName, List<string> roles);
Task<bool> SigninUserAsync(string userName, string password);
Task<string> GetUserIdAsync(string userName);
Task<(string userId, string fullName, string UserName, string email, IList<string> roles)> GetUserDetailsAsync(string userId);
Task<(string userId, string fullName, string UserName, string email, IList<string> roles)> GetUserDetailsByUserNameAsync(string userName);
Task<string> GetUserNameAsync(string userId);
Task<bool> DeleteUserAsync(string userId);
Task<bool> IsUniqueUserName(string userName);
Task<List<(string id, string fullName, string userName, string email)>> GetAllUsersAsync();
Task<List<(string id, string userName, string email, IList<string> roles)>> GetAllUsersDetailsAsync();
Task<bool> UpdateUserProfile(string id, string fullName, string email, IList<string> roles);
// Role Section
Task<bool> CreateRoleAsync(string roleName);
Task<bool> DeleteRoleAsync(string roleId);
Task<List<(string id, string roleName)>> GetRolesAsync();
Task<(string id, string roleName)> GetRoleByIdAsync(string id);
Task<bool> UpdateRole(string id, string roleName);
// User's Role section
Task<bool> IsInRoleAsync(string userId, string role);
Task<List<string>> GetUserRolesAsync(string userId);
Task<bool> AssignUserToRole(string userName, IList<string> roles);
Task<bool> UpdateUsersRole(string userName, IList<string> usersRole);
}
}
ITokenGenerator.cs
namespace Ordering.Application.Common.Interfaces
{
public interface ITokenGenerator
{
//public string GenerateToken(string userName, string password);
public string GenerateJWTToken((string userId, string userName, IList<string> roles) userDetails);
}
}
- Create Auth Command and Command Handler in Ordering.Application/Commands/Auth folder
AuthCommand.cs
using MediatR;
using Ordering.Application.Common.Exceptions;
using Ordering.Application.Common.Interfaces;
using Ordering.Application.DTOs;
namespace Ordering.Application.Commands.Auth
{
public class AuthCommand : IRequest<AuthResponseDTO>
{
public string UserName { get; set; }
public string Password { get; set; }
}
public class AuthCommandHandler : IRequestHandler<AuthCommand, AuthResponseDTO>
{
private readonly ITokenGenerator _tokenGenerator;
private readonly IIdentityService _identityService;
public AuthCommandHandler(IIdentityService identityService, ITokenGenerator tokenGenerator)
{
_identityService = identityService;
_tokenGenerator = tokenGenerator;
}
public async Task<AuthResponseDTO> Handle(AuthCommand request, CancellationToken cancellationToken)
{
var result = await _identityService.SigninUserAsync(request.UserName, request.Password);
if (!result)
{
throw new BadRequestException("Invalid username or password");
}
var (userId, fullName, userName, email, roles) = await _identityService.GetUserDetailsAsync(await _identityService.GetUserIdAsync(request.UserName));
string token = _tokenGenerator.GenerateJWTToken((userId: userId, userName: userName, roles: roles));
return new AuthResponseDTO()
{
UserId = userId,
Name = userName,
Token = token
};
}
}
}
- Create CreateCustomerCommand and Handler in Ordering.Application/Commands/Customers/Create folder
CreateCustomerCommand.cs
using MediatR;
using Ordering.Application.DTOs;
using Ordering.Application.Mapper;
using Ordering.Core.Entities;
using Ordering.Core.Repositories.Command;
namespace Ordering.Application.Commands.Customers.Create
{
// Customer create command with CustomerResponse
public class CreateCustomerCommand : IRequest<CustomerResponse>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string ContactNumber { get; set; }
public string Address { get; set; }
public DateTime CreatedDate { get; set; }
public CreateCustomerCommand()
{
this.CreatedDate = DateTime.Now;
}
}
public class CreateCustomerCommandHandler : IRequestHandler<CreateCustomerCommand, CustomerResponse>
{
private readonly ICustomerCommandRepository _customerCommandRepository;
public CreateCustomerCommandHandler(ICustomerCommandRepository customerCommandRepository)
{
_customerCommandRepository = customerCommandRepository;
}
public async Task<CustomerResponse> Handle(CreateCustomerCommand request, CancellationToken cancellationToken)
{
var customerEntity = CustomerMapper.Mapper.Map<Customer>(request);
if (customerEntity is null)
{
throw new ApplicationException("There is a problem in mapper");
}
var newCustomer = await _customerCommandRepository.AddAsync(customerEntity);
var customerResponse = CustomerMapper.Mapper.Map<CustomerResponse>(newCustomer);
return customerResponse;
}
}
}
- Create EditCustomerCommand and Handler in Ordering.Application/Commands/Customers/Update folder
EditCustomerCommand.cs
using MediatR;
using Ordering.Application.DTOs;
using Ordering.Application.Mapper;
using Ordering.Core.Entities;
using Ordering.Core.Repositories.Command;
using Ordering.Core.Repositories.Query;
namespace Ordering.Application.Commands.Customers.Update
{
// Customer create command with CustomerResponse
public class EditCustomerCommand : IRequest<CustomerResponse>
{
public Int64 Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string ContactNumber { get; set; }
public string Address { get; set; }
}
public class EditCustomerCommandHandler : IRequestHandler<EditCustomerCommand, CustomerResponse>
{
private readonly ICustomerCommandRepository _customerCommandRepository;
private readonly ICustomerQueryRepository _customerQueryRepository;
public EditCustomerCommandHandler(ICustomerCommandRepository customerRepository, ICustomerQueryRepository customerQueryRepository)
{
_customerCommandRepository = customerRepository;
_customerQueryRepository = customerQueryRepository;
}
public async Task<CustomerResponse> Handle(EditCustomerCommand request, CancellationToken cancellationToken)
{
var customerEntity = CustomerMapper.Mapper.Map<Customer>(request);
if (customerEntity is null)
{
throw new ApplicationException("There is a problem in mapper");
}
try
{
await _customerCommandRepository.UpdateAsync(customerEntity);
}
catch (Exception exp)
{
throw new ApplicationException(exp.Message);
}
var modifiedCustomer = await _customerQueryRepository.GetByIdAsync(request.Id);
var customerResponse = CustomerMapper.Mapper.Map<CustomerResponse>(modifiedCustomer);
return customerResponse;
}
}
}
- Create DeleteCustomerCommand and Handler in Ordering.Application/Commands/Customers/Delete folder
DeleteCustomerCommand.cs
using MediatR;
using Ordering.Core.Repositories.Command;
using Ordering.Core.Repositories.Query;
namespace Ordering.Application.Commands.Customers.Delete
{
// Customer create command with string response
public class DeleteCustomerCommand : IRequest<String>
{
public Int64 Id { get; private set; }
public DeleteCustomerCommand(Int64 Id)
{
this.Id = Id;
}
}
// Customer delete command handler with string response as output
public class DeleteCustomerCommmandHandler : IRequestHandler<DeleteCustomerCommand, String>
{
private readonly ICustomerCommandRepository _customerCommandRepository;
private readonly ICustomerQueryRepository _customerQueryRepository;
public DeleteCustomerCommmandHandler(ICustomerCommandRepository customerRepository, ICustomerQueryRepository customerQueryRepository)
{
_customerCommandRepository = customerRepository;
_customerQueryRepository = customerQueryRepository;
}
public async Task<string> Handle(DeleteCustomerCommand request, CancellationToken cancellationToken)
{
try
{
var customerEntity = await _customerQueryRepository.GetByIdAsync(request.Id);
await _customerCommandRepository.DeleteAsync(customerEntity);
}
catch (Exception exp)
{
throw (new ApplicationException(exp.Message));
}
return "Customer information has been deleted!";
}
}
}
- Create RoleCreateCommand and Handler in Ordering.Application/Commands/Role/Create folder
RoleCreateCommand.cs
using MediatR;
using Ordering.Application.Common.Interfaces;
namespace Ordering.Application.Commands.Role.Create
{
public class RoleCreateCommand : IRequest<int>
{
public string RoleName { get; set; }
}
public class RoleCreateCommandHandler : IRequestHandler<RoleCreateCommand, int>
{
private readonly IIdentityService _identityService;
public RoleCreateCommandHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<int> Handle(RoleCreateCommand request, CancellationToken cancellationToken)
{
var result = await _identityService.CreateRoleAsync(request.RoleName);
return result ? 1 : 0;
}
}
}
- Create DeleteRoleCommand and Handler in Ordering.Application/Commands/Role/Delete folder
DeleteRoleCommand.cs
using MediatR;
using Ordering.Application.Common.Interfaces;
namespace Ordering.Application.Commands.Role.Delete
{
public class DeleteRoleCommand : IRequest<int>
{
public string RoleId { get; set; }
}
public class DeleteRoleCommandHandler : IRequestHandler<DeleteRoleCommand, int>
{
private readonly IIdentityService _identityService;
public DeleteRoleCommandHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<int> Handle(DeleteRoleCommand request, CancellationToken cancellationToken)
{
var result = await _identityService.DeleteRoleAsync(request.RoleId);
return result ? 1 : 0;
}
}
}
- Create UpdateRoleCommand and Handler in Ordering.Application/Commands/Role/Update folder
UpdateRoleCommand.cs
using MediatR;
using Ordering.Application.Common.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ordering.Application.Commands.Role.Update
{
public class UpdateRoleCommand : IRequest<int>
{
public string Id { get; set; }
public string RoleName { get; set; }
}
public class UpdateRoleCommandHandler : IRequestHandler<UpdateRoleCommand, int>
{
private readonly IIdentityService _identityService;
public UpdateRoleCommandHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<int> Handle(UpdateRoleCommand request, CancellationToken cancellationToken)
{
var result = await _identityService.UpdateRole(request.Id, request.RoleName);
return result ? 1 : 0;
}
}
}
- Create CreateUserCommand and Handler in Ordering.Application/Commands/User/Create folder
CreateUserCommand.cs
using MediatR;
using Ordering.Application.Common.Interfaces;
namespace Ordering.Application.Commands.User.Create
{
public class CreateUserCommand : IRequest<int>
{
public string FullName { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string ConfirmationPassword { get; set; }
public List<string> Roles { get; set; }
}
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, int>
{
private readonly IIdentityService _identityService;
public CreateUserCommandHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<int> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
var result = await _identityService.CreateUserAsync(request.UserName, request.Password, request.Email, request.FullName, request.Roles);
return result.isSucceed ? 1 : 0;
}
}
}
- Create DeleteUserCommandd and Handler in Ordering.Application/Commands/User/Delete folder
DeleteUserCommand.cs
using MediatR;
using Ordering.Application.Common.Interfaces;
namespace Ordering.Application.Commands.User.Delete
{
public class DeleteUserCommand : IRequest<int>
{
public string Id { get; set; }
}
public class DeleteUserCommandHandler : IRequestHandler<DeleteUserCommand, int>
{
private readonly IIdentityService _identityService;
public DeleteUserCommandHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<int> Handle(DeleteUserCommand request, CancellationToken cancellationToken)
{
var result = await _identityService.DeleteUserAsync(request.Id);
return result ? 1 : 0;
}
}
}
- Create AssignUsersRoleCommand, EditUserProfileCommand, UpdateUserRolesCommand and Handler in Ordering.Application/Commands/User/Update folder
AssignUsersRoleCommand.cs
using MediatR;
using Ordering.Application.Common.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Ordering.Application.Commands.User.Update
{
public class AssignUsersRoleCommand : IRequest<int>
{
public string UserName { get; set; }
public IList<string> Roles { get; set;}
}
public class AssignUsersRoleCommandHandler : IRequestHandler<AssignUsersRoleCommand, int>
{
private readonly IIdentityService _identityService;
public AssignUsersRoleCommandHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<int> Handle(AssignUsersRoleCommand request, CancellationToken cancellationToken)
{
var result = await _identityService.AssignUserToRole(request.UserName, request.Roles);
return result ? 1 : 0;
}
}
}
EditUserProfileCommand.cs
using MediatR;
using Ordering.Application.Common.Interfaces;
namespace Ordering.Application.Commands.User.Update
{
public class EditUserProfileCommand : IRequest<int>
{
public string Id { get; set; }
public string FullName { get; set; }
public string Email { get; set; }
public List<string> Roles { get; set; }
}
public class EditUserProfileCommandHandler : IRequestHandler<EditUserProfileCommand, int>
{
private readonly IIdentityService _identityService;
public EditUserProfileCommandHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<int> Handle(EditUserProfileCommand request, CancellationToken cancellationToken)
{
var result = await _identityService.UpdateUserProfile(request.Id, request.FullName, request.Email, request.Roles);
return result ? 1 : 0;
}
}
}
UpdateUserRolesCommand.cs
using MediatR;
using Ordering.Application.Common.Interfaces;
namespace Ordering.Application.Commands.User.Update
{
public class UpdateUserRolesCommand : IRequest<int>
{
public string userName { get; set; }
public IList<string> Roles { get; set;}
}
public class UpdateUserRolesCommandHandler : IRequestHandler<UpdateUserRolesCommand, int>
{
private readonly IIdentityService _identityService;
public UpdateUserRolesCommandHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<int> Handle(UpdateUserRolesCommand request, CancellationToken cancellationToken)
{
var result = await _identityService.UpdateUsersRole(request.userName, request.Roles);
return result ? 1 : 0;
}
}
}
- Create Customers Query classes and Handlers in Ordering.Application/Queries/Customers folder
GetAllCustomerQuery.cs
using MediatR;
using Ordering.Core.Entities;
using Ordering.Core.Repositories.Query;
namespace Ordering.Application.Queries.Customers
{
// Customer query with List<Customer> response
public record GetAllCustomerQuery : IRequest<List<Customer>>
{
}
public class GetAllCustomerHandler : IRequestHandler<GetAllCustomerQuery, List<Customer>>
{
private readonly ICustomerQueryRepository _customerQueryRepository;
public GetAllCustomerHandler(ICustomerQueryRepository customerQueryRepository)
{
_customerQueryRepository = customerQueryRepository;
}
public async Task<List<Customer>> Handle(GetAllCustomerQuery request, CancellationToken cancellationToken)
{
return (List<Customer>)await _customerQueryRepository.GetAllAsync();
}
}
}
GetCustomerByEmailQuery.cs
using MediatR;
using Ordering.Core.Entities;
namespace Ordering.Application.Queries.Customers
{
// Customer GetCustomerByEmailQuery with Customer response
public class GetCustomerByEmailQuery: IRequest<Customer>
{
public string Email { get; private set; }
public GetCustomerByEmailQuery(string email)
{
this.Email = email;
}
}
public class GetCustomerByEmailHandler : IRequestHandler<GetCustomerByEmailQuery, Customer>
{
private readonly IMediator _mediator;
public GetCustomerByEmailHandler(IMediator mediator)
{
_mediator = mediator;
}
public async Task<Customer> Handle(GetCustomerByEmailQuery request, CancellationToken cancellationToken)
{
var customers = await _mediator.Send(new GetAllCustomerQuery());
var selectedCustomer = customers.FirstOrDefault(x => x.Email.ToLower().Contains(request.Email.ToLower()));
return selectedCustomer;
}
}
}
GetCustomerByIdQuery.cs
using MediatR;
using Ordering.Core.Entities;
using System;
namespace Ordering.Application.Queries.Customers
{
// Customer GetCustomerByIdQuery with Customer response
public class GetCustomerByIdQuery: IRequest<Customer>
{
public Int64 Id { get; private set; }
public GetCustomerByIdQuery(Int64 Id)
{
this.Id = Id;
}
}
public class GetCustomerByIdHandler : IRequestHandler<GetCustomerByIdQuery, Customer>
{
private readonly IMediator _mediator;
public GetCustomerByIdHandler(IMediator mediator)
{
_mediator = mediator;
}
public async Task<Customer> Handle(GetCustomerByIdQuery request, CancellationToken cancellationToken)
{
var customers = await _mediator.Send(new GetAllCustomerQuery());
var selectedCustomer = customers.FirstOrDefault(x => x.Id == request.Id);
return selectedCustomer;
}
}
}
- Create Role Query classes and Handlers in Ordering.Application/Queries/Role folder
GetRoleByIdQuery.cs
using MediatR;
using Ordering.Application.Common.Interfaces;
using Ordering.Application.DTOs;
namespace Ordering.Application.Queries.Role
{
public class GetRoleByIdQuery : IRequest<RoleResponseDTO>
{
public string RoleId { get; set; }
}
public class GetRoleQueryByIdHandler : IRequestHandler<GetRoleByIdQuery, RoleResponseDTO>
{
private readonly IIdentityService _identityService;
public GetRoleQueryByIdHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<RoleResponseDTO> Handle(GetRoleByIdQuery request, CancellationToken cancellationToken)
{
var role = await _identityService.GetRoleByIdAsync(request.RoleId);
return new RoleResponseDTO() { Id = role.id, RoleName = role.roleName};
}
}
}
GetRoleQuery.cs
using MediatR;
using Ordering.Application.Common.Interfaces;
using Ordering.Application.DTOs;
namespace Ordering.Application.Queries.Role
{
public class GetRoleQuery : IRequest<IList<RoleResponseDTO>>
{
}
public class GetRoleQueryHandler : IRequestHandler<GetRoleQuery, IList<RoleResponseDTO>>
{
private readonly IIdentityService _identityService;
public GetRoleQueryHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<IList<RoleResponseDTO>> Handle(GetRoleQuery request, CancellationToken cancellationToken)
{
var roles = await _identityService.GetRolesAsync();
return roles.Select(role => new RoleResponseDTO() { Id = role.id, RoleName = role.roleName}).ToList();
}
}
}
- Create Users Query classes and Handlers in Ordering.Application/Queries/User folder
GetAllUsersDetailsQuery.cs
using MediatR;
using Ordering.Application.Common.Interfaces;
using Ordering.Application.DTOs;
namespace Ordering.Application.Queries.User
{
public class GetAllUsersDetailsQuery : IRequest<List<UserDetailsResponseDTO>>
{
//public string UserId { get; set; }
}
public class GetAllUsersDetailsQueryHandler : IRequestHandler<GetAllUsersDetailsQuery, List<UserDetailsResponseDTO>>
{
private readonly IIdentityService _identityService;
public GetAllUsersDetailsQueryHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<List<UserDetailsResponseDTO>> Handle(GetAllUsersDetailsQuery request, CancellationToken cancellationToken)
{
var users = await _identityService.GetAllUsersAsync();
var userDetails = users.Select(x => new UserDetailsResponseDTO()
{
Id = x.id,
Email = x.email,
UserName = x.userName
//Roles = (IList<string>)_identityService.GetUserRolesAsync(x.id) // Converstion problem
}).ToList();
foreach (var user in userDetails)
{
user.Roles = await _identityService.GetUserRolesAsync(user.Id);
}
return userDetails;
}
}
}
GetUserDetailsByUserNameQuery.cs
using MediatR;
using Ordering.Application.Common.Interfaces;
using Ordering.Application.DTOs;
namespace Ordering.Application.Queries.User
{
public class GetUserDetailsByUserNameQuery : IRequest<UserDetailsResponseDTO>
{
public string UserName { get; set; }
}
public class GetUserDetailsByUserNameQueryHandler : IRequestHandler<GetUserDetailsByUserNameQuery, UserDetailsResponseDTO>
{
private readonly IIdentityService _identityService;
public GetUserDetailsByUserNameQueryHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<UserDetailsResponseDTO> Handle(GetUserDetailsByUserNameQuery request, CancellationToken cancellationToken)
{
var (userId, fullName, userName, email, roles ) = await _identityService.GetUserDetailsByUserNameAsync(request.UserName);
return new UserDetailsResponseDTO() { Id = userId, FullName = fullName, UserName = userName, Email = email, Roles = roles };
}
}
}
GetUserDetailsQuery.cs
using MediatR;
using Ordering.Application.Common.Interfaces;
using Ordering.Application.DTOs;
namespace Ordering.Application.Queries.User
{
public class GetUserDetailsQuery : IRequest<UserDetailsResponseDTO>
{
public string UserId { get; set; }
}
public class GetUserDetailsQueryHandler : IRequestHandler<GetUserDetailsQuery, UserDetailsResponseDTO>
{
private readonly IIdentityService _identityService;
public GetUserDetailsQueryHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<UserDetailsResponseDTO> Handle(GetUserDetailsQuery request, CancellationToken cancellationToken)
{
var (userId, fullName, userName, email, roles ) = await _identityService.GetUserDetailsAsync(request.UserId);
return new UserDetailsResponseDTO() { Id = userId, FullName = fullName, UserName = userName, Email = email, Roles = roles };
}
}
}
GetUserQuery.cs
using MediatR;
using Ordering.Application.Common.Interfaces;
using Ordering.Application.DTOs;
namespace Ordering.Application.Queries.User
{
public class GetUserQuery : IRequest<List<UserResponseDTO>>
{
}
public class GetUserQueryHandler : IRequestHandler<GetUserQuery, List<UserResponseDTO>>
{
private readonly IIdentityService _identityService;
public GetUserQueryHandler(IIdentityService identityService)
{
_identityService = identityService;
}
public async Task<List<UserResponseDTO>> Handle(GetUserQuery request, CancellationToken cancellationToken)
{
var users = await _identityService.GetAllUsersAsync();
return users.Select(x => new UserResponseDTO()
{
Id = x.id,
FullName = x.fullName,
UserName = x.userName,
Email = x.email
}).ToList();
}
}
}
- Create CustomerMapper and OrderingMappingProfile class in Ordering.Application/Mapper folder
CustomerMapper.cs
using AutoMapper;
using System;
namespace Ordering.Application.Mapper
{
public class CustomerMapper
{
private static readonly Lazy<IMapper> Lazy = new Lazy<IMapper>(()=>
{
var config = new MapperConfiguration(cfg =>
{
cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly;
cfg.AddProfile<OrderingMappingProfile>();
});
var mapper = config.CreateMapper();
return mapper;
});
public static IMapper Mapper => Lazy.Value;
}
}
OrderingMappingProfile.cs
using AutoMapper;
using Ordering.Application.Commands;
using Ordering.Application.Response;
using Ordering.Core.Entities;
namespace Ordering.Application.Mapper
{
public class OrderingMappingProfile : Profile
{
public OrderingMappingProfile()
{
CreateMap<Customer, CustomerResponse>().ReverseMap();
CreateMap<Customer, CreateCustomerCommand>().ReverseMap();
CreateMap<Customer, EditCustomerCommand>().ReverseMap();
}
}
}
Step 8: Orgnaize Ordering.API project. This is an web api project.
- Create AuthController in Ordering.API/Controllers folder
AuthController.cs
using MediatR;
using Microsoft.AspNetCore.Mvc;
using Ordering.Application.Commands.Auth;
using Ordering.Application.DTOs;
namespace Ordering.API.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly IMediator _mediator;
public AuthController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost("Login")]
[ProducesDefaultResponseType(typeof(AuthResponseDTO))]
public async Task<IActionResult> Login([FromBody] AuthCommand command)
{
return Ok(await _mediator.Send(command));
}
}
}
- Create CustomerController in Ordering.API/Controllers folder
CustomerController.cs
using MediatR;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using Ordering.Application.Commands.Customers.Create;
using Ordering.Application.Commands.Customers.Delete;
using Ordering.Application.Commands.Customers.Update;
using Ordering.Application.DTOs;
using Ordering.Application.Queries.Customers;
using Ordering.Core.Entities;
namespace Ordering.API.Controllers
{
[EnableCors("CorsPolicy")]
[Route("api/[controller]")]
[ApiController]
//[Authorize(Roles = "Admin,Member")]
//[Authorize]
//[Authorize(AuthenticationSchemes = Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults.AuthenticationScheme, Roles = "Admin,Member")]
// Authorize with a specific scheme
//[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Admin,Member,User")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class CustomerController : ControllerBase
{
private readonly IMediator _mediator;
public CustomerController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet("GetAll")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<List<Customer>> Get()
{
return await _mediator.Send(new GetAllCustomerQuery());
}
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<Customer> Get(Int64 id)
{
return await _mediator.Send(new GetCustomerByIdQuery(id));
}
[HttpGet("email")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<Customer> GetByEmail(string email)
{
return await _mediator.Send(new GetCustomerByEmailQuery(email));
}
[HttpPost("Create")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<CustomerResponse>> CreateCustomer([FromBody] CreateCustomerCommand command)
{
var result = await _mediator.Send(command);
return Ok(result);
}
[HttpPut("Edit/{id}")]
public async Task<ActionResult> Edit(int id, [FromBody] EditCustomerCommand command)
{
try
{
if (command.Id == id)
{
var result = await _mediator.Send(command);
return Ok(result);
}
else
{
return BadRequest();
}
}
catch (Exception exp)
{
return BadRequest(exp.Message);
}
}
[Authorize(Roles = "Admin, Management")]
[HttpDelete("Delete/{id}")]
public async Task<ActionResult> DeleteCustomer(int id)
{
try
{
string result = string.Empty;
result = await _mediator.Send(new DeleteCustomerCommand(id));
return Ok(result);
}
catch (Exception exp)
{
return BadRequest(exp.Message);
}
}
}
}
- Create RoleController in Ordering.API/Controllers folder
RoleController.cs
using MediatR;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Ordering.Application.Commands.Role.Create;
using Ordering.Application.Commands.Role.Delete;
using Ordering.Application.Commands.Role.Update;
using Ordering.Application.DTOs;
using Ordering.Application.Queries.Role;
namespace Ordering.API.Controllers
{
[Route("api/[controller]")]
[ApiController]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Authorize(Roles = "Admin, Management")]
public class RoleController : ControllerBase
{
public readonly IMediator _mediator;
public RoleController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost("Create")]
[ProducesDefaultResponseType(typeof(int))]
public async Task<ActionResult> CreateRoleAsync(RoleCreateCommand command)
{
return Ok(await _mediator.Send(command));
}
[HttpGet("GetAll")]
[ProducesDefaultResponseType(typeof(List<RoleResponseDTO>))]
public async Task<IActionResult> GetRoleAsync()
{
return Ok(await _mediator.Send(new GetRoleQuery()));
}
[HttpGet("{id}")]
[ProducesDefaultResponseType(typeof(RoleResponseDTO))]
public async Task<IActionResult> GetRoleByIdAsync(string id)
{
return Ok(await _mediator.Send(new GetRoleByIdQuery() { RoleId = id }));
}
[HttpDelete("Delete/{id}")]
[ProducesDefaultResponseType(typeof(int))]
public async Task<IActionResult> DeleteRoleAsync(string id)
{
return Ok(await _mediator.Send(new DeleteRoleCommand()
{
RoleId = id
}));
}
[HttpPut("Edit/{id}")]
[ProducesDefaultResponseType(typeof(int))]
public async Task<ActionResult> EditRole(string id, [FromBody] UpdateRoleCommand command)
{
if (id == command.Id)
{
var result = await _mediator.Send(command);
return Ok(result);
}
else
{
return BadRequest();
}
}
}
}
- Create UserController in Ordering.API/Controllers folder
UserController.cs
using MediatR;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Ordering.Application.Commands.User.Create;
using Ordering.Application.Commands.User.Delete;
using Ordering.Application.Commands.User.Update;
using Ordering.Application.DTOs;
using Ordering.Application.Queries.User;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace Ordering.API.Controllers
{
[Route("api/[controller]")]
[ApiController]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Authorize(Roles = "Admin, Management")]
public class UserController : ControllerBase
{
private readonly IMediator _mediator;
public UserController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost("Create")]
[ProducesDefaultResponseType(typeof(int))]
public async Task<ActionResult> CreateUser(CreateUserCommand command)
{
return Ok(await _mediator.Send(command));
}
[HttpGet("GetAll")]
[ProducesDefaultResponseType(typeof(List<UserResponseDTO>))]
public async Task<IActionResult> GetAllUserAsync()
{
return Ok(await _mediator.Send(new GetUserQuery()));
}
[HttpDelete("Delete/{userId}")]
[ProducesDefaultResponseType(typeof(int))]
public async Task<IActionResult> DeleteUser(string userId)
{
var result = await _mediator.Send(new DeleteUserCommand() { Id = userId});
return Ok(result);
}
[HttpGet("GetUserDetails/{userId}")]
[ProducesDefaultResponseType(typeof(UserDetailsResponseDTO))]
public async Task<IActionResult> GetUserDetails(string userId)
{
var result = await _mediator.Send(new GetUserDetailsQuery() { UserId = userId });
return Ok(result);
}
[HttpGet("GetUserDetailsByUserName/{userName}")]
[ProducesDefaultResponseType(typeof(UserDetailsResponseDTO))]
public async Task<IActionResult> GetUserDetailsByUserName(string userName)
{
var result = await _mediator.Send(new GetUserDetailsByUserNameQuery() { UserName = userName });
return Ok(result);
}
[HttpPost("AssignRoles")]
[ProducesDefaultResponseType(typeof(int))]
public async Task<ActionResult> AssignRoles(AssignUsersRoleCommand command)
{
var result = await _mediator.Send(command);
return Ok(result);
}
[HttpPut("EditUserRoles")]
[ProducesDefaultResponseType(typeof(int))]
public async Task<ActionResult> EditUserRoles(UpdateUserRolesCommand command)
{
var result = await _mediator.Send(command);
return Ok(result);
}
[HttpGet("GetAllUserDetails")]
[ProducesDefaultResponseType(typeof(UserDetailsResponseDTO))]
public async Task<IActionResult> GetAllUserDetails()
{
var result = await _mediator.Send(new GetAllUsersDetailsQuery());
return Ok(result);
}
[HttpPut("EditUserProfile/{id}")]
[ProducesDefaultResponseType(typeof(int))]
public async Task<ActionResult> EditUserProfile(string id, [FromBody]EditUserProfileCommand command)
{
if (id == command.Id)
{
var result = await _mediator.Send(command);
return Ok(result);
}
else
{
return BadRequest();
}
}
}
}
- Set connection string in appsettings.json file. I have used here SQLite database for simplicity.
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Data Source=db/ordering.db"
},
"Jwt": {
"Key": "$ecret@Key$shouldbeEncrypted!",
"Issuer": "jwt",
"Audience": "jwt",
"ExpiryMinutes": 120
},
"AllowedHosts": "*"
}
- Modify the Program Class as follows
Program.cs
using MediatR;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Ordering.Application.Commands.Customers.Create;
using Ordering.Application.Commands.User.Create;
using Ordering.Application.Common.Interfaces;
using Ordering.Infrastructure;
using Ordering.Infrastructure.Services;
using System.Reflection;
using System.Text;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
// For authentication
var _key = builder.Configuration["Jwt:Key"];
var _issuer = builder.Configuration["Jwt:Issuer"];
var _audience = builder.Configuration["Jwt:Audience"];
var _expirtyMinutes = builder.Configuration["Jwt:ExpiryMinutes"];
// Configuration for token
builder.Services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidAudience = _audience,
ValidIssuer = _issuer,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_key)),
ClockSkew = TimeSpan.FromMinutes(Convert.ToDouble(_expirtyMinutes))
};
});
// Dependency injection with key
builder.Services.AddSingleton<ITokenGenerator>(new TokenGenerator(_key, _issuer, _audience, _expirtyMinutes));
// Include Infrastructur Dependency
builder.Services.AddInfrastructure(builder.Configuration);
// Configuration for Sqlite
//builder.Services.AddDbContext<OrderingContext>(options => options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));
// Register dependencies
builder.Services.AddMediatR(typeof(CreateCustomerCommandHandler).GetTypeInfo().Assembly);
builder.Services.AddMediatR(typeof(CreateUserCommandHandler).GetTypeInfo().Assembly);
builder.Services.AddCors(c =>
{
c.AddPolicy("CorsPolicy", options => options.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
// To enable authorization using swagger (Jwt)
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "JWT Authorization header using the Bearer scheme. \r\n\r\n Enter 'Bearer' [space] and then your token in the text input below.\r\n\r\nExample: \"Bearer {token}\"",
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
// Must be betwwen app.UseRouting() and app.UseEndPoints()
// maintain middleware order
app.UseCors("CorsPolicy");
// Added for authentication
// Maintain middleware order
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Step 9: Run migration command on package manager console
- In package manager console select Oredering.Infrastructure as defult project.
- Run the following command
PM> Add-Migration initialsqlmig
PM> Update-Database -Verbose
Step 10: Now run your application and test using swagger.