How to return a cookie from an ASP.NET Core Auth Cookie in a Controller - asp.net-core

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

Related

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);

How to extract methods from controller?

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");
}
}

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.

IdentityServer4 requesting a JWT / Access Bearer Token using the password grant in asp.net core

I've searched all over on requesting a JWT / Access Bearer Token using the password grant using IdentityServer4 in asp.net core, but I cant seem to find the right way to do it.
Below is the POST Request from which I register my user.
http://localhost:52718/account/register
Below is the Bearer Token GET Request from which I can get JWT Token using IdentityServer4
http://localhost:52718/connect/token
Below is the POST Request from which I Login my user
http://localhost:52718/account/signin
Now, what I'm trying to do is when I login my user then I want a JWT / Bearer Token same as I get from here http://localhost:52718/connect/token. When I hit this URL.
Here is my AccountController Code:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Server.Models;
using Server.Models.AccountViewModels;
using Server.Models.UserViewModels;
namespace Server.Controllers
{
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<IdentityRole> _roleManager;
public AccountController(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager
)
{
_userManager = userManager;
_roleManager = roleManager;
}
[HttpPost]
public async Task<IActionResult> Register([FromBody]RegisterViewModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser { UserName = model.UserName, FirstName = model.FirstName, LastName = model.LastName, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
string role = "Basic User";
if (result.Succeeded)
{
if (await _roleManager.FindByNameAsync(role) == null)
{
await _roleManager.CreateAsync(new IdentityRole(role));
}
await _userManager.AddToRoleAsync(user, role);
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("userName", user.UserName));
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("firstName", user.FirstName));
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("lastName", user.LastName));
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("email", user.Email));
await _userManager.AddClaimAsync(user, new System.Security.Claims.Claim("role", role));
return Ok(new ProfileViewModel(user));
}
return BadRequest(result.Errors);
}
public async Task<IActionResult> Signin([FromBody]LoginViewModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var result = await _userManager.FindByNameAsync(model.UserName);
if (result != null && await _userManager.CheckPasswordAsync(result, model.Password))
{
return Ok(new ProfileViewModel(result));
}
return BadRequest("Invalid username or password.");
}
}
}
When I hit signin method I successfully get the data of user.
But I also need a jwt / access token when user login my app.
Now my actual question is:
What can I do in my signin method so when user login it returns me token along with other user data. I hope I briefly explain my question.
Thanks
I've found my own question answer. Before starting I show you my that code where I'm Defining the client.
public static IEnumerable<Client> GetClients()
{
// client credentials client
return new List<Client>
{
// resource owner password grant client
new Client
{
ClientId = "ro.angular",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.Address,
"api1"
}
}
};
}
Now what I do in my Signin Method is to use the TokenClient class to request the token. To create an instance you need to pass in the token endpoint address, client id and secret.
Next I'm using Requesting a token using the password grant to allows a client to send username and password to the token service and get an access token back that represents that user.
Here is my Signin Code which I need to modify:
public async Task<IActionResult> Signin([FromBody]LoginViewModel model)
{
var disco = await DiscoveryClient.GetAsync("http://localhost:52718");
if (disco.IsError)
{
return BadRequest(disco.Error);
}
var tokenClient = new TokenClient(disco.TokenEndpoint, "ro.angular", "secret");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(model.UserName, model.Password, "api1 openid");
if (tokenResponse.IsError)
{
return BadRequest(tokenResponse.Error);
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = _userManager.FindByNameAsync(model.UserName);
var result = await _userManager.FindByNameAsync(model.UserName);
if (result != null && await _userManager.CheckPasswordAsync(result, model.Password))
{
return Ok(new ProfileViewModel(result, tokenResponse));
}
return BadRequest("Invalid username or password.");
}
Also I modify ProfileViewModel Class and add two new Token & Expiry:
public class ProfileViewModel
{
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Token { get; set; }
public int Expiry { get; set; }
public ProfileViewModel()
{
}
public ProfileViewModel(ApplicationUser user, TokenResponse UToken = null)
{
Id = user.Id;
FirstName = user.FirstName;
LastName = user.LastName;
Email = user.Email;
Token = UToken.AccessToken;
Expiry = UToken.ExpiresIn;
}
public static IEnumerable<ProfileViewModel> GetUserProfiles(IEnumerable<ApplicationUser> users)
{
var profiles = new List<ProfileViewModel> { };
foreach (ApplicationUser user in users)
{
profiles.Add(new ProfileViewModel(user));
}
return profiles;
}
}
Now Here is my desire output. Hope this answer help others.

Why [Authorize] attribute return 401 status code JWT + Asp.net Web Api?

I'm having big trouble finding issue with the JWT token authentication with asp.net web api. This is first time I am dealing with JWT & Web Api authentication & Authorization.
I have implemented the following code.
Startup.cs
public class Startup
{
public void Configuration(IAppBuilder app)
{
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
ConfigureOAuthTokenGeneration(app);
ConfigureOAuthTokenConsumption(app);
}
private void ConfigureOAuthTokenGeneration(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
//For Dev enviroment only (on production should be AllowInsecureHttp = false)
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new OAuthTokenProvider(),
RefreshTokenProvider = new RefreshTokenProvider(),
AccessTokenFormat = new Provider.JwtFormat("http://localhost:49860")
};
// OAuth 2.0 Bearer Access Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
var issuer = "http://localhost:49860";
string audienceId = Config.AudienceId;
byte[] audienceSecret = TextEncodings.Base64Url.Decode(Config.AudienceSecret);
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
}
});
}
}
OAuthTokenProvider.cs
public class OAuthTokenProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// validate client credentials (demo)
// should be stored securely (salted, hashed, iterated)
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
/***Note: Add User validation business logic here**/
if (context.UserName != context.Password)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{ "as:client_id", "Kaushik Thanki" }
});
ClaimsIdentity oAuthIdentity = new ClaimsIdentity("JWT");
var ticket = new AuthenticationTicket(oAuthIdentity, props);
context.Validated(ticket);
}
}
JwtFormat.cs
public class JwtFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string _issuer = string.Empty;
public JwtFormat(string issuer)
{
_issuer = issuer;
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
string audienceId = Config.AudienceId;
string symmetricKeyAsBase64 = Config.AudienceSecret;
var keyByteArray = TextEncodings.Base64Url.Decode(symmetricKeyAsBase64);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
var token = new JwtSecurityToken(_issuer, audienceId, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime);
var handler = new JwtSecurityTokenHandler();
var jwt = handler.WriteToken(token);
return jwt;
}
public AuthenticationTicket Unprotect(string protectedText)
{
throw new NotImplementedException();
}
}
RefreshTokenProvider.cs
public class RefreshTokenProvider : IAuthenticationTokenProvider
{
private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>();
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var guid = Guid.NewGuid().ToString();
// maybe only create a handle the first time, then re-use for same client
// copy properties and set the desired lifetime of refresh token
var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
{
IssuedUtc = context.Ticket.Properties.IssuedUtc,
ExpiresUtc = DateTime.UtcNow.AddYears(1)
};
var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties);
//_refreshTokens.TryAdd(guid, context.Ticket);
_refreshTokens.TryAdd(guid, refreshTokenTicket);
// consider storing only the hash of the handle
context.SetToken(guid);
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
AuthenticationTicket ticket;
if (_refreshTokens.TryRemove(context.Token, out ticket))
{
context.SetTicket(ticket);
}
}
}
Now Once I pass the authentication (Which I kept dummy for initial level matching same username & password) & got the token & refresh token.
When I request for method that is decorated with [Authorize] attribute, I always gets 401 status code.
I testing this method in postman following way
Any help or guidance will be really appreciated. I have invested my two days finding the solution for this but all in vain.