How to extract methods from controller? - asp.net-core

I want to extract "what I can" to the service. I have this method in the controller:
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
LoginViewModel loginViewModel = new LoginViewModel
{
ReturnUrl = returnUrl,
ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList()
};
if (remoteError != null)
{
ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}");
return View("Login", loginViewModel);
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
ModelState.AddModelError(string.Empty, "Error loading external login information.");
return View("Login", loginViewModel);
}
var signInResult = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
if (signInResult.Succeeded)
{
return LocalRedirect(returnUrl);
}
else
{
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
if (email != null)
{
var user = await _userManager.FindByEmailAsync(email);
if (user == null)
{
user = new ApplicationUser
{
UserName = info.Principal.FindFirstValue(ClaimTypes.Email),
Email = info.Principal.FindFirstValue(ClaimTypes.Email)
};
await _userManager.CreateAsync(user);
}
await _userManager.AddLoginAsync(user, info);
await _signInManager.SignInAsync(user, isPersistent: false);
return LocalRedirect(returnUrl);
}
ViewBag.ErrorTitle = $"Nie otrzymano informacji o adresie e-mail od dostawcy: {info.LoginProvider}";
ViewBag.ErrorMessage = "Proszę skontaktować się z supportem fryzjer#aplikacjafryzjer.com";
return View("Error");
}
}
Some properties are available only for classes that inherit from Controller, eg. ModelState or Url.
Can I extract these parts of the code too? A service can inherit from a controller, but won't it become a controller then?
------------------------------------------------------------ EDIT ---------------------------------------------------------------
Ok I tried separate my method from the controller. Below my previous code Controller:
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IEmailService _emailService;
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IEmailService emailService)
{
_userManager = userManager;
_signInManager = signInManager;
_emailService = emailService;
}
[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
//register functionality
var user = new ApplicationUser
{
FirstName = model.FirstName,
LastName = model.LastName,
UserName = model.Email,
Email = model.Email,
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
if (_signInManager.IsSignedIn(User) && User.IsInRole("Admin"))
{
return RedirectToAction("ListUsers", "Administrator");
}
//login user
await _signInManager.SignInAsync(user, isPersistent: false);
//generation of the email token
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var link = Url.Action(nameof(VerifyEmail), "Home", new { userId = user.Id, code }, Request.Scheme, Request.Host.ToString());
await _emailService.SendAsync(user.Email, "Weryfikacja adresu e-mail", $"Potwierdź e-mail", true);
return RedirectToAction("EmailVerification");
}
}
return View(model);
}
}
And my Controller now:
public class AccountController : Controller
{
private readonly IUserManager _userManager;
public AccountController(
IUserManager userManager)
{
_userManager = userManager;
}
[HttpPost]
public async Task<IActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
(string action, string controller) = await _userManager.Register(model);
return RedirectToAction(action, controller);
}
return View(model);
}
}
I am sure, something is wrong. I have no idea how separate this logic from the controller, but on the other forums I heard "U have to separate your logic from the controller! Your controller have to be simple - only get request and send response, thats all!". but now, when I started rebuild my project, I am not sure isn't this more complicated...
In my service I return a tuple (???), because I have not better idea...
public async Task<(string, string)> Register(RegisterViewModel model)
{
//register functionality
var user = new ApplicationUser
{
FirstName = model.FirstName,
LastName = model.LastName,
UserName = model.Email,
Email = model.Email,
};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
if (_signInManager.IsSignedIn(User) && User.IsInRole("Admin"))
{
return ("ListUsers", "Administrator");
}
//login user
await _signInManager.SignInAsync(user, isPersistent: false);
//generation of the email token
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var link = _urlHelper.Action(nameof(VerifyEmail), "Home", new { userId = user.Id, code });
await _emailService.SendAsync(user.Email, "Weryfikacja adresu e-mail", $"Potwierdź e-mail", true);
return ("EmailVerification", "Administrator");
}
return ("Register", "Administrator");
}

As far as I know, if you want to use Url in other custom service which is not inherit from controller. You could inject the IActionContextAccessor and use IUrlHelperFactory to create the it. Besides, if you want to use other controller based property or method, you could refer asp.net core source codes and find it work and then you could write the codes by yourself.
More details, you could refer to below codes:
Firstly you could inject the IActionContextAccessor in the startup.cs ConfigureServices method:
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
Then you could use DI and factory to create the URLhelper like below:
public class GetRoute : IGetRoute
{
private IUrlHelper _urlHelper;
private IActionContextAccessor _IActionContextAccessor;
public IUrlHelper Url
{
get
{
if (_urlHelper == null)
{
_urlHelper = _urlHelperFactory.GetUrlHelper(_IActionContextAccessor.ActionContext);
}
return _urlHelper;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_urlHelper = value;
}
}
private readonly IUrlHelperFactory _urlHelperFactory ;
public GetRoute(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider, IUrlHelperFactory urlHelperFactory, IActionContextAccessor actionContextAccessor)
{
_IActionContextAccessor = actionContextAccessor;
_urlHelperFactory = urlHelperFactory;
}
public string Getlink() {
return Url.Link("default", "aaaa");
}
}

Related

How to create a service for user login in Identity

In order to create a token in jwt, I must first check that the received user password is correct. I want to write a service for login and create its interface and use it in the JWTAuthorizationManager class or anywhere, please correct the codes.
public class JWTAuthorizationManager
{
public JwtFeilds Authenticate(string UserName, string PasswordHash)
{
//ایجاد تاریخ انقضای توکن
var tokenExpireTimeStamp = DateTime.Now.AddHours(Constansts.JWT_TOKEN_EXPIRE_TIME);
//ایجاد متغیر از کلاس مشخص شده برای ایجاد توکن و اطلاعات همراه آن
var jwtSecurityTokenHandler = new JwtSecurityTokenHandler();
//ایجاد آرایه ای از بایت ها به عنوان کلید توکن
var tokenKey = Encoding.ASCII.GetBytes(Constansts.JWT_SECURITY_KEY_FOR_TOKEN);
//از این کلاس برای نگهداری ویژگیها و اطلاعات درون توکن استفاده می شود.
var securityTokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new List<Claim>
{
new Claim("username", UserName),
new Claim(ClaimTypes.PrimaryGroupSid,"User Group 01")
}),
Expires = tokenExpireTimeStamp,
//امضا یا اعتبارنامه یا مجوز ورود
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(tokenKey),SecurityAlgorithms.HmacSha256Signature)
};
var securityToken = jwtSecurityTokenHandler.CreateToken(securityTokenDescriptor);
var token = jwtSecurityTokenHandler.WriteToken(securityToken);
return new JwtFeilds
{
token = token,
user_name = UserName,
expire_time = (int)tokenExpireTimeStamp.Subtract(DateTime.Now).TotalSeconds
};
}
}
public class loginService
{
private readonly SignInManager<User> _signInManager;
public loginService(SignInManager<User> signInManager)
{
_signInManager = signInManager;
}
public async Task<loginService> UserLogin(string UserName, string PasswordHash)
{
var result = await _signInManager.PasswordSignInAsync(UserName, PasswordHash, true,
lockoutOnFailure: false);
if (result.Succeeded)
{
return null;
}
return null;
}
}
interface IuserLogin
{
}
[HttpPost]
public IActionResult Login([FromForm] User model)
{
var jwtAuthorizationManager = new JWTAuthorizationManager();
var result = jwtAuthorizationManager.Authenticate(model.UserName, model.PasswordHash);
if (result == null)
return Unauthorized();
else
return Ok(result);
}
The token creation is done successfully, but I want the user to be identified before creating the token
The easiest way, you can choose to return a boolean Task:
public interface ILoginService
{
//If your model has other attributes, pass them in together
Task<bool> UserLogin(string UserName, string PasswordHash);
}
public class Login : ILogin
{
private readonly SignInManager<IdentityUser> _signInManager;
private readonly UserManager<IdentityUser> _userManager;
public Login(SignInManager<IdentityUser> signInManager,UserManager<IdentityUser> userManager)
{
_signInManager = signInManager;
_userManager = userManager;
}
public async Task<bool> UserLogin(string UserName, string PasswordHash)
{
var user = await _userManager.FindByEmailAsync(UserName);
if (user != null && !user.EmailConfirmed)
{
return false;
}
if (await _userManager.CheckPasswordAsync(user, PasswordHash) == false)
{
return false;
}
var result = await _signInManager.PasswordSignInAsync(UserName, PasswordHash, true);
if (result.Succeeded)
{
return true;
}
else
{
return false;
}
}
}
In the Login Action:
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login(UserLoginDto model)
{
if (ModelState.IsValid)
{
//Call the ILoginService interface
var result = _loginService.UserLogin(model.Email,model.Password);
if (result.Result)
{
var jwtAuthorizationManager = new JWTAuthorizationManager();
var result = jwtAuthorizationManager.Authenticate(model.UserName, model.PasswordHash);
//do something
}
else
{
ModelState.AddModelError("message", "Invalid login attempt");
return View(model);
}
}
return View(model);
}
Don't forget to sign up for the service:
builder.Services.AddTransient<ILoginService, LoginService>();
You can also return a string or other types of Task, up to you.
Hope this can help you.

How to return a cookie from an ASP.NET Core Auth Cookie in a Controller

I currently have a razor page where I return a cookie and it works great. However, I am developing a SPA which uses VueJS so I have created an API to directly communicate with. I have converted my code from the Razor Page to the controller but I am not sure how I actually return the cookie when the user tries to log in. If there is a match I want it to return the cookie and create the cookie. As of now I get a 400 code as if this request is not working. Any thoughts what could be wrong with this?
public class LoginController : Controller
{
private readonly LoginDBContext _context;
private string connectionString;
public IConfiguration Configuration { get; }
public LoginController(LoginDBContext context, IConfiguration configuration)
{
_context = context;
connectionString = configuration["ConnectionStrings:MMCARMSContext"];
}
// GET: HomeController
public ActionResult Index()
{
return Ok(new { Result = "Test" });
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login([FromForm] string Username, [FromForm] string Password, [FromForm] bool RememberMe)
{
if (!String.IsNullOrEmpty(Username) && !String.IsNullOrEmpty(Password))
{
var users = _context.UsersAccountsTbl
.Where(a => a.Username == Username)
.Select(a => new { a.InternalUserNumber, a.Username, a.Password })
.ToArray();
if (users.Length == 1) //if we have more than 1 result we have security issue so do not allow login
{
var passwordHasher = new PasswordHasher<string>();
//To use you need to has with var hashedPassword = passwordHasher.HashPassword(UserName, Password);
//System.Diagnostics.Debug.WriteLine(passwordHasher.HashPassword("Username", "password"));
var user = users.First();
if (passwordHasher.VerifyHashedPassword(user.Username, user.Password, Password) == PasswordVerificationResult.Success)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.InternalUserNumber.ToString())
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
if (RememberMe)
{
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties
{
IsPersistent = RememberMe,
ExpiresUtc = DateTimeOffset.UtcNow.AddHours(2)
});
}
else
{
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity));
}
return Ok(new { Result = "Cookie has been created!" });
}
else
{
return Ok(new { Result = "Password is incorrect!" });
}
}
return Ok(new { Result = "Username or Password does not exist!" });
}
else
{
return Ok(new { Result = "Username or Password invalid!" });
}
}
}
You could set the cookie in the HttpResponse, this way the cookie gets added when the client receives the response from your controller:
HttpCookie MyCookie = new HttpCookie("LastVisit");
DateTime now = DateTime.Now;
MyCookie.Value = now.ToString();
MyCookie.Expires = now.AddHours(1);
Response.Cookies.Add(MyCookie);
https://learn.microsoft.com/en-us/dotnet/api/system.web.httpresponse.cookies?view=netframework-4.8

Asp.net Core Object reference not set to an instance of an object

ASP.NET CORE API
The logged in user gives an error in the code below while adding a photo. Can anybody help?
var currentUserId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value)
This code gives an error. Help me
Object reference not set to an instance of an object
PhotosController.cs
[HttpPost]
public ActionResult AddPhotoForCity(int cityId,[FromForm]PhotoForCreationDto photoForCreationDto)
{
var city = _appRepository.GetCityById(cityId);
if (city == null)
{
return BadRequest("Could not find the city.");
}
var currentUserId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value);
karşılaştırmak gibi
if (currentUserId != city.UserId)
{
return Unauthorized();
}
var file = photoForCreationDto.File;
var uploadResult = new ImageUploadResult();
if (file.Length > 0)
{
using (var steam = file.OpenReadStream())
{
var uploadParams = new ImageUploadParams()
{
File = new FileDescription(file.Name,steam)
};
uploadResult = _cloudinary.Upload(uploadParams);
}
}
photoForCreationDto.Url = uploadResult.Url.ToString();
photoForCreationDto.PublicId = uploadResult.PublicId;
var photo = _mapper.Map<Photo>(photoForCreationDto);
photo.City = city;
if (!city.Photos.Any(p => p.IsMain))
{
photo.IsMain = true;
}
city.Photos.Add(photo);
if (_appRepository.SaveAll())
{
//eklenen fotoğrafı döndürüyoruz
var photoToRetun = _mapper.Map<Photo>(photoForCreationDto);
return CreatedAtRoute("GetPhoto", new {id = photo.Id}, photoToRetun);
}
return BadRequest("Cloud not add the photo");
}
AuthController.cs
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private IAuthRepository _authRepository;
private IConfiguration _configuration;
public AuthController(IAuthRepository authRepository, IConfiguration configuration)
{
_authRepository = authRepository;
_configuration = configuration;
}
[HttpPost("register")]
public async Task<IActionResult> Register([FromBody] UserForRegisterDto userForRegisterDto)
{
if (await _authRepository.UserExists(userForRegisterDto.UserName))
{
ModelState.AddModelError("UserName", "Username already exists");
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var userToCreate = new User
{
UserName = userForRegisterDto.UserName
};
var createdUser = await _authRepository.Register(userToCreate, userForRegisterDto.Password);
return StatusCode(201);
}
[HttpPost("login")]
public async Task<ActionResult> Login([FromBody] UserForLoginDto userForLoginDto)
{
var user = await _authRepository.Login(userForLoginDto.UserName, userForLoginDto.Password);
if (user == null)
{
return Unauthorized();
}
var tokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_configuration.GetSection("AppSettings:Token").Value);
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(new Claim[]
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName)
}),
Expires = DateTime.Now.AddDays(1),
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key)
, SecurityAlgorithms.HmacSha512Signature)
};
var token = tokenHandler.CreateToken(tokenDescriptor);
var tokenString = tokenHandler.WriteToken(token);
return Ok(tokenString);
}
}
From the above code snippet you have shared, they mostly likely reason you are getting this error is because the user is not logged in, and hence this line of code
User.FindFirst(ClaimTypes.NameIdentifier).Value
is throwing an exception.
You could either do it like this.
User.FindFirst(ClaimTypes.NameIdentifier)?.Value
Or
[HttpPost]
[Authorize] // make sure you authorize your action method by adding this attribute and
// only allow logged in user to access it.
public ActionResult AddPhotoForCity(int cityId,[FromForm]PhotoForCreationDto photoForCreationDto)
{
}
try this one:
var currentUserId = int.Parse(User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value);

.NET Core 3 Service result to Controller to FE (data or error reason)

building new app on .net core 3 and Angular. Overall all works, but I want to add more intelligence to service/controller part. This is one of the api's, but this logic can be applied to others as as well.
Here's my Login Controller:
[HttpPost]
public async Task<IActionResult> Login([FromBody] UserLoginDto userLogin)
{
var token = await _userService.LoginAsync(userLogin);
if (token != null)
{
return Ok(token);
}
else
{
return BadRequest("Something went wrong");
}
}
And here's my userService:
public async Task<string> LoginAsync(UserLoginDto userLogin)
{
var user = await _userManager.FindByEmailAsync(userLogin.Email);
if (user != null)
{
var result = await _signInManager.PasswordSignInAsync(user, userLogin.Password, false, true);
if (result.Succeeded)
{
var roles = await _userManager.GetRolesAsync(user);
var tokenJson = _jwtManager.getJwtToken(user.Email, roles);
return tokenJson;
}
else
{
return null; // Return BadRequest and result reason (Failed, lockedout, etc)
}
}
else
{
return null; // User not found, return NotFound }
}
Here's my question - how should I return result from userService to Controller so, that I could respond to API call either with Ok(token) or BadRequest/NotFound with the reason.
If I keep all this LoginAsync code in controller, then it's easy, but I want to use service.
One option I was thinking was to introduce new class, something like:
public class BaseResult
{
public object Data { get; set; }
public long ResponseCode { get; set; }
public string ErrorMessage { get; set; }
}
then always return this class from service, but not fully like that idea either.
thanks!
Here is a working demo you could follow:
Model:
public class UserLoginDto
{
public string Email { get; set; }
public string Password { get; set; }
}
IUserService:
public interface IUserService
{
Task<IActionResult> LoginAsync(UserLoginDto userLogin);
}
UserService:
public class UserService: IUserService
{
private readonly UserManager<IdentityUser> _userManager;
private readonly SignInManager<IdentityUser> _signInManager;
private readonly IJwtManager _jwtManager;
public UserService(
UserManager<IdentityUser> userManager,
SignInManager<IdentityUser> signInManager,
IJwtManager jwtManager)
{
_userManager = userManager;
_signInManager = signInManager;
_jwtManager = jwtManager;
}
public async Task<IActionResult> LoginAsync(UserLoginDto userLogin)
{
var user = await _userManager.FindByEmailAsync(userLogin.Email);
if (user != null)
{
var result = await _signInManager.PasswordSignInAsync(user, userLogin.Password, false, true);
if (result.Succeeded)
{
var roles = await _userManager.GetRolesAsync(user);
var tokenJson = _jwtManager.getJwtToken(user.Email, roles);
return new OkObjectResult(tokenJson);
}
else
{
// Return BadRequest and result reason (Failed, lockedout, etc)
if (result.IsNotAllowed)
{
if (!await _userManager.IsEmailConfirmedAsync(user))
{
// Email isn't confirmed.
return new BadRequestObjectResult("Email isn't confirmed.");
}
if (!await _userManager.IsPhoneNumberConfirmedAsync(user))
{
// Phone Number isn't confirmed.
return new BadRequestObjectResult("Phone Number isn't confirmed.");
}
return new BadRequestObjectResult("Login IsNotAllowed");
}
else if (result.IsLockedOut)
{
// Account is locked out.
return new BadRequestObjectResult("Account is locked out.");
}
else if (result.RequiresTwoFactor)
{
// 2FA required.
return new BadRequestObjectResult("2FA required");
}
else
{
// Password is incorrect.
return new BadRequestObjectResult("Password is incorrect.");
}
}
}
else
{
return new NotFoundObjectResult("Username is incorrect"); // User not found, return NotFound }
}
}
}
Controller:
public class HomeController : Controller
{
private readonly IUserService _userService;
public HomeController(IUserService userService)
{
_userService = userService;
}
[HttpPost]
public async Task<IActionResult> Login([FromBody] UserLoginDto userLogin)
{
var result= await _userService.LoginAsync(userLogin);
return result;
}
}
Startup.cs:
Not sure what is _jwtManager.getJwtToken in your code,so I just guess it is an interface and owns a JwtManager class implemented this interface.And it contains a getJwtToken method which generated the token.
services.AddScoped<IUserService, UserService>();
services.AddScoped<IJwtManager, JwtManager>();

Authourize tag does'nt maintain session state properly In Asp.net Core Identity ..

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using HMS_Context;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Http.Features;
using HMS_Service.Services.MasterService;
using HMS_Service.Services.PatientVisitService;
using HMS_Service.UserManagementService;
using HMS_Service.Services.PatientRegistrationService;
using HMS_Service.Services.BloodCampService;
using HMS_Service.Services.AccountService;
using System;
using Microsoft.AspNetCore.Identity;
using HMS_Presentation.Services;
using HMS_Service.Services.InventoryManagementService;
using HMS_Service.Services.SystemManagementService;
using HMS_Service.Services.UserManagementService;
using HMS_Service.Services.LabManagementService;
using HMS_Service.CacheSetting.AppSettings;
using HMS_Service.Services.BarCodeService;
namespace HMS_Presentation
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IAppSettings, AppSettings>();
services.AddMemoryCache();
services.AddOptions();
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
services.AddDbContext<_DbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<HMS_POCO.UserManagement.ApplicationUser, HMS_POCO.UserManagement.ApplicationRole>(
option => {
option.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
option.Lockout.MaxFailedAccessAttempts = 5;
option.Lockout.AllowedForNewUsers = false;
})
.AddEntityFrameworkStores<HMS_Context._DbContext>()
.AddDefaultTokenProviders();
/*Cache Functionality Start*/
/*Cache Functionality End*/
services.AddAuthorization(options =>
{
options.AddPolicy("Administrator", policy => policy.RequireRole("Admin","Superior"));
options.AddPolicy("Doctors", policy => policy.RequireRole("Receptionist","Doctor", "Superior"));
options.AddPolicy("Nurses", policy => policy.RequireRole("Nurse", "Superior"));
options.AddPolicy("Labortory", policy => policy.RequireRole("LabTech", "Superior"));
options.AddPolicy("Accountants", policy => policy.RequireRole("Accountant", "Superior"));
options.AddPolicy("Receptionist", policy => policy.RequireRole("Receptionist", "Superior"));
options.AddPolicy("RecpDoct", policy => policy.RequireRole("Receptionist","Doctor", "Superior"));
options.AddPolicy("RMO", policy => policy.RequireRole("RMO", "Superior"));
options.AddPolicy("RD", policy => policy.RequireRole("RMO", "Doctor","Superior"));
// Access DNL(Doctor,Nurse ,Lab)
options.AddPolicy("AccessDNL", policy => policy.RequireRole("Doctor", "Nurse", "LabTech", "Superior"));
});
services.ConfigureApplicationCookie(options =>
{
options.Cookie.Name = "YourAppCookieName";
options.LoginPath = "/UserAccount/Login";
options.LogoutPath = "/UserAccount/Logout";
options.AccessDeniedPath = "/UserAccount/AccessDenied";
options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
});
services.AddDistributedMemoryCache();
services.AddSession(options=>options.Cookie.HttpOnly=true);
services.AddMvc().AddSessionStateTempDataProvider();
services.AddTransient<IEmailSender, EmailSender>();
services.Configure<FormOptions>(x => {
x.ValueLengthLimit = int.MaxValue;
x.MultipartBodyLengthLimit = long.MaxValue;
x.BufferBodyLengthLimit = long.MaxValue;
x.MemoryBufferThreshold = int.MaxValue;
x.ValueCountLimit = int.MaxValue;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseSession();
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
**User Account Controller**
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using HMS_Presentation.Services;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Authentication;
using HMS_Presentation.Models.AccountViewModels;
using Microsoft.AspNetCore.Mvc.Rendering;
using System.Security.Claims;
using HMS_Service.UserManagementService;
using Microsoft.AspNetCore.Http;
using HMS_Service.Services.UserManagementService;
using HMS_POCO.UserManagement;
namespace HMS_Presentation.Controllers
{
[Authorize(Policy = "Administrator")]
[Route("[controller]/[action]")]
public class UserAccountController : Controller
{
private readonly UserManager<HMS_POCO.UserManagement.ApplicationUser> _userManager;
private readonly SignInManager<HMS_POCO.UserManagement.ApplicationUser> _signInManager;
private readonly IEmailSender _emailSender;
private readonly ILogger _logger;
private readonly RoleManager<HMS_POCO.UserManagement.ApplicationRole> _roleManager;
//Extra Addition
private readonly EmployeeService _employeeService;
private readonly UserAccessLogService _userAccessLogService;
public UserAccountController(
UserManager<HMS_POCO.UserManagement.ApplicationUser> userManager,
SignInManager<HMS_POCO.UserManagement.ApplicationUser> signInManager,
IEmailSender emailSender,
ILogger<UserAccountController> logger,
RoleManager<HMS_POCO.UserManagement.ApplicationRole> roleManager,
EmployeeService employeeService, UserAccessLogService userAccessLogService)
{
_userManager = userManager;
_signInManager = signInManager;
_emailSender = emailSender;
_logger = logger;
_roleManager = roleManager;
_employeeService = employeeService;
_userAccessLogService = userAccessLogService;
}
[TempData]
public string ErrorMessage { get; set; }
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> Login(string returnUrl = null)
{
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
ViewData["ReturnUrl"] = returnUrl;
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
USER_ACCESS_LOG be = new USER_ACCESS_LOG();
be.AccessTime = DateTime.Now.ToString();
be.UserName = model.Email;
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: true);
if (result.Succeeded)
{
var lookupUser = _userManager.Users.Where(c => c.UserName == model.Email).FirstOrDefault();
HttpContext.Session.SetInt32("UserId", lookupUser.EmpId);
_logger.LogInformation("User logged in.");
be.AccessedStatus = true;
_userAccessLogService.Add(be);
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
var lookupUser = _userManager.Users.Where(c => c.UserName == model.Email).FirstOrDefault();
HttpContext.Session.SetInt32("UserId", lookupUser.EmpId);
return RedirectToAction(nameof(LoginWith2fa), new { returnUrl, model.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning("User account locked out.");
return RedirectToAction(nameof(Lockout));
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
be.AccessedStatus = false;
_userAccessLogService.Add(be);
return View(model);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> LoginWith2fa(bool rememberMe, string returnUrl = null)
{
// Ensure the user has gone through the username & password screen first
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
throw new ApplicationException($"Unable to load two-factor authentication user.");
}
var model = new LoginWith2faViewModel { RememberMe = rememberMe };
ViewData["ReturnUrl"] = returnUrl;
return View(model);
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> LoginWith2fa(LoginWith2faViewModel model, bool rememberMe, string returnUrl = null)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var authenticatorCode = model.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty);
var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, model.RememberMachine);
if (result.Succeeded)
{
_logger.LogInformation("User with ID {UserId} logged in with 2fa.", user.Id);
return RedirectToLocal(returnUrl);
}
else if (result.IsLockedOut)
{
_logger.LogWarning("User with ID {UserId} account locked out.", user.Id);
return RedirectToAction(nameof(Lockout));
}
else
{
_logger.LogWarning("Invalid authenticator code entered for user with ID {UserId}.", user.Id);
ModelState.AddModelError(string.Empty, "Invalid authenticator code.");
return View();
}
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> LoginWithRecoveryCode(string returnUrl = null)
{
// Ensure the user has gone through the username & password screen first
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
throw new ApplicationException($"Unable to load two-factor authentication user.");
}
ViewData["ReturnUrl"] = returnUrl;
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> LoginWithRecoveryCode(LoginWithRecoveryCodeViewModel model, string returnUrl = null)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
{
throw new ApplicationException($"Unable to load two-factor authentication user.");
}
var recoveryCode = model.RecoveryCode.Replace(" ", string.Empty);
var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode);
if (result.Succeeded)
{
_logger.LogInformation("User with ID {UserId} logged in with a recovery code.", user.Id);
return RedirectToLocal(returnUrl);
}
if (result.IsLockedOut)
{
_logger.LogWarning("User with ID {UserId} account locked out.", user.Id);
return RedirectToAction(nameof(Lockout));
}
else
{
_logger.LogWarning("Invalid recovery code entered for user with ID {UserId}", user.Id);
ModelState.AddModelError(string.Empty, "Invalid recovery code entered.");
return View();
}
}
[HttpGet]
[AllowAnonymous]
public IActionResult Lockout()
{
return View();
}
[HttpGet]
public IActionResult Register(string returnUrl = null)
{
ViewData["RoleName"] = new SelectList(_roleManager.Roles, "Id", "Name");
ViewData["EmployeeName"] = new SelectList(_employeeService.GetUnRegisteredEmployee(), "Id", "FirstName");
ViewData["ReturnUrl"] = returnUrl;
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var user = new HMS_POCO.UserManagement.ApplicationUser { UserName = model.Email, Email = model.Email, EmpId=model.EmployeeId};
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
IdentityRole res = await _roleManager.FindByIdAsync(model.RoleId);
await _userManager.AddToRoleAsync(user, res.Name);
_logger.LogInformation("User created a new account with password.");
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
ViewData["RoleName"] = new SelectList(_roleManager.Roles, "Id", "Name");
ViewData["EmployeeName"] = new SelectList(_employeeService.GetAll(), "Id", "FirstName");
return View(model);
}
[AllowAnonymous]
public async Task<IActionResult> Logout()
{
HttpContext.Session.Clear();
await _signInManager.SignOutAsync();
_logger.LogInformation("User logged out.");
return RedirectToAction(nameof(HomeController.Index), "Home");
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public IActionResult ExternalLogin(string provider, string returnUrl = null)
{
// Request a redirect to the external login provider.
var redirectUrl = Url.Action(nameof(ExternalLoginCallback), "Account", new { returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return Challenge(properties, provider);
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{
if (remoteError != null)
{
ErrorMessage = $"Error from external provider: {remoteError}";
return RedirectToAction(nameof(Login));
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToAction(nameof(Login));
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
if (result.Succeeded)
{
_logger.LogInformation("User logged in with {Name} provider.", info.LoginProvider);
return RedirectToLocal(returnUrl);
}
if (result.IsLockedOut)
{
return RedirectToAction(nameof(Lockout));
}
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = info.LoginProvider;
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
return View("ExternalLogin", new ExternalLoginViewModel { Email = email });
}
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginViewModel model, string returnUrl = null)
{
if (ModelState.IsValid)
{
// Get the information about the user from the external login provider
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
throw new ApplicationException("Error loading external login information during confirmation.");
}
var user = new HMS_POCO.UserManagement.ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{
result = await _userManager.AddLoginAsync(user, info);
if (result.Succeeded)
{
await _signInManager.SignInAsync(user, isPersistent: false);
_logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
return RedirectToLocal(returnUrl);
}
}
AddErrors(result);
}
ViewData["ReturnUrl"] = returnUrl;
return View(nameof(ExternalLogin), model);
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return RedirectToAction(nameof(HomeController.Index), "Home");
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
throw new ApplicationException($"Unable to load user with ID '{userId}'.");
}
var result = await _userManager.ConfirmEmailAsync(user, code);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
[HttpGet]
[AllowAnonymous]
public IActionResult ForgotPassword()
{
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null || !(await _userManager.IsEmailConfirmedAsync(user)))
{
// Don't reveal that the user does not exist or is not confirmed
return RedirectToAction(nameof(ForgotPasswordConfirmation));
}
// For more information on how to enable account confirmation and password reset please
// visit https://go.microsoft.com/fwlink/?LinkID=532713
var code = await _userManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.ResetPasswordCallbackLink(user.Id, code, Request.Scheme);
await _emailSender.SendEmailAsync(model.Email, "Reset Password",
$"Please reset your password by clicking here: <a href='{callbackUrl}'>link</a>");
return RedirectToAction(nameof(ForgotPasswordConfirmation));
}
// If we got this far, something failed, redisplay form
return View(model);
}
[HttpGet]
[AllowAnonymous]
public IActionResult ForgotPasswordConfirmation()
{
return View();
}
[HttpGet]
[AllowAnonymous]
public IActionResult ResetPassword(string code = null)
{
if (code == null)
{
throw new ApplicationException("A code must be supplied for password reset.");
}
var model = new ResetPasswordViewModel { Code = code };
return View(model);
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null)
{
// Don't reveal that the user does not exist
return RedirectToAction(nameof(ResetPasswordConfirmation));
}
var result = await _userManager.ResetPasswordAsync(user, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction(nameof(ResetPasswordConfirmation));
}
AddErrors(result);
return View();
}
[HttpGet]
[AllowAnonymous]
public IActionResult ResetPasswordConfirmation()
{
return View();
}
[HttpGet]
public IActionResult AccessDenied()
{
return View();
}
#region Helpers
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
private IActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction(nameof(HomeController.Index), "Home");
}
}
#endregion
}
}
This is my code of Startup.cs file. and User Account Controller The authorize tag Does not maintain the session state properly .. Even after passing the session time the application starts without email and password required . How to fix it . i am using .NET CORE and visual studio 2017 and when i logout and then run the application it works Fine and take email and password. but without logout the account and close the tab and then run the application issue appears ...
Use IsPersistent=true in default signin method mentioned below:
var result = await _signInManager.PasswordSignInAsync(user, model.LoginPassword, true, false);
Here the third parameter is IsPersistent, which should be true for persistent login.