Why HttpContext is null? - c#

I've just read more than one and I don't find any solution for my problem.
I've create WebApi + MVC and Unit Test project with FrameWork 4.5.
I have this HomeController.cs with this method:
using Net.Personal.Authentication.FormAuthentication;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
namespace FormAuthenticationProva.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Title = "Home Page";
var guidAccount = "xxxxxxxx-xxxx-xxxx-xxxx-422632e0bd95";
var userData = new CookieUserData(guidAccount) { GuidAccount = guidAccount };
HttpContextBase httpContextBase = this.HttpContext;
AuthenticationProvider _authProvider = new AuthenticationProvider(httpContextBase.ApplicationInstance.Context);
_authProvider.CheckAuthorizationForUrl("http://pippo");
return View();
}
}
}
And the HomeControllerTest.cs with this code:
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using FormAuthenticationProva.Controllers;
using Moq;
using System.Web;
using System.Collections.Specialized;
using System.Web.Routing;
namespace FormAuthenticationProva.Tests.Controllers
{
[TestClass]
public class HomeControllerTest
{
[TestMethod]
public void Index()
{ // Disposizione
var formData = new NameValueCollection { { "id", "test" } };
var request = new Mock<HttpRequestBase>();
request.SetupGet(r => r.Form).Returns(formData);
var context = new Mock<HttpContextBase>();
context.SetupGet(c => c.Request).Returns(request.Object);
var controller = new HomeController();
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
// Azione
ViewResult result = controller.Index() as ViewResult;
// Asserzione
Assert.IsNotNull(result);
Assert.AreEqual("Home Page", result.ViewBag.Title);
}
}
}
And the AuthenticationProvider.cs class is here:
/* AuthenticationProvider.cs code */
using System;
using System.Security.Principal;
using System.Threading;
using System.Web;
using System.Web.Script.Serialization;
using System.Web.Security;
namespace Net.Personal.Authentication.FormAuthentication
{
public class AuthenticationProvider : IFormsAuthentication
{
public AuthContextConfiguration AuthContextConfiguration { get; set; }
public AuthenticationProvider() {}
public AuthenticationProvider(HttpContext context , AuthContextConfiguration authContextConfiguration = null)
{
AuthContextConfiguration = AuthContextConfiguration ?? new AuthContextConfiguration(context);
}
private void SetPrincipal(IPrincipal principal)
{
Thread.CurrentPrincipal = principal;
if (HttpContext.Current != null)
{
HttpContext.Current.User = principal;
}
}
public void SignIn(string userName, bool createPersistentCookie)
{
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
}
public void SignIn(string userName, bool createPersistentCookie, ICookieUserData userData)
{
this.SetAuthCookie<ICookieUserData>(userName, true, userData);
HttpContext.Current.Cache.Insert(userName, userData);
}
public int SetAuthCookie<T>( string name, bool rememberMe, T userData)
{
/// In order to pickup the settings from config, we create a default cookie and use its values to create a
/// new one.
if (string.IsNullOrEmpty(((ICookieUserData)userData).Name)) ((ICookieUserData)userData).Name = name;
var cookie = FormsAuthentication.GetAuthCookie(name, rememberMe);
var ticket = FormsAuthentication.Decrypt(cookie.Value);
var newTicket = new FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration,
ticket.IsPersistent, new JavaScriptSerializer().Serialize(userData), ticket.CookiePath);
var encTicket = FormsAuthentication.Encrypt(newTicket);
/// Use existing cookie. Could create new one but would have to copy settings over...
cookie.Value = encTicket;
HttpContext.Current.Response.Cookies.Add(cookie);
return encTicket.Length;
}
public void SignOut()
{
FormsAuthentication.SignOut();
}
public bool IsAuthorized()
{
return HttpContext.Current.User != null &&
HttpContext.Current.User.Identity != null &&
HttpContext.Current.User.Identity.IsAuthenticated;
}
public void SetUserOnApplication_AuthenticateRequest<T>(HttpContext context)
{
PrincipalUser principal = null;
ICookieUserData userData = null;
// Extract the forms authentication cookie
string cookieName = FormsAuthentication.FormsCookieName;
HttpCookie authCookie = context.Request.Cookies[cookieName];
if (null == authCookie)
{
// There is no authentication cookie.
return;
}
var nameIdentity = HttpContext.Current.User.Identity.Name;
if(string.IsNullOrEmpty(nameIdentity)) {
return;
}
if(HttpContext.Current.Cache[nameIdentity] ==null) {
FormsAuthenticationTicket authTicket = null;
try {
authTicket = FormsAuthentication.Decrypt(authCookie.Value);
}
catch (Exception ex) {
// Log exception details (omitted for simplicity)
return;
}
if (null == authTicket) {
// Cookie failed to decrypt.
return;
}
userData = (ICookieUserData)new JavaScriptSerializer().Deserialize<T>(authTicket.UserData);
// When the ticket was created, the UserData property was assigned a
// pipe delimited string of role names.
string[] roles = authTicket.UserData.Split('|');
// Create an Identity object
//FormsIdentity id = new FormsIdentity(authTicket);
// This principal will flow throughout the request.
//PrincipalUser principal = new PrincipalUser(id, roles);
} else {
userData = (ICookieUserData)HttpContext.Current.Cache[nameIdentity];
}
principal = new PrincipalUser(userData);
// Attach the new principal object to the current HttpContext object
context.User = principal;
}
public void CheckAuthorization()
{
if (!this.IsAuthorized()) throw new Exception("Access not allowed");
}
public void CheckAuthorizationForUrl(string url)
{
AuthContextConfiguration.CheckAuthorizationForUrl(url);
if (AuthContextConfiguration.CheckRequiredAuth() && !this.IsAuthorized()) throw new Exception("Access not allowed");
}
}
public class PrincipalUser : IPrincipal
{
private ICookieUserData _userData;
private GenericIdentity _identity;
public PrincipalUser(ICookieUserData userData)
{
_identity = new GenericIdentity(userData.Name);
_userData = userData;
}
public IIdentity Identity
{
get
{
return _identity;
}
}
public ICookieUserData UserData
{
get
{
return _userData;
}
}
public bool IsInRole(string role)
{
return _userData.Role.Contains(role);
}
}
public interface ICookieUserData
{
string Name { get; set; }
string Role { get; set; }
}
}
And now the problem is when I debug from [TestMethod] and I go into method with debugging (F10) and putted breakpoint in the method in HomeController.cs I see this System.Web.HttpContext.Current is every time null! What's the problem? I use Moq.

System.Web.HttpContext.Current is populated by IIS which is not present/active during unit testing. Hence null. Do not tightly couple your code to HttpContext for that very reason. Instead encapsulate that behind abstractions that can be mocked when testing in isolation.
Bad design aside, for your specific design you are trying to access a static dependency external to the controller. If faking the controller's context as demonstrated in your current test, then access the controller's context instead of calling the static context.
//... code removed for brevity
var _authProvider = new AuthenticationProvider(this.HttpContext);
//... code removed for brevity
With that out of the way, the design of the controller should be refactored to explicitly depend on abstractions instead of concretions.
for example, here is an abstraction for the provider
public interface IAuthenticationProvider : IFormsAuthentication {
void CheckAuthorizationForUrl(string url);
//...other members
}
public class AuthenticationProvider : IAuthenticationProvider {
//...
}
The controller should explicitly depend on this via constructor injection
public class HomeController : Controller {
private readonly IAuthenticationProvider authProvider;
public HomeController(IAuthenticationProvider authProvider) {
this.authProvider = authProvider;
}
public ActionResult Index() {
ViewBag.Title = "Home Page";
var guidAccount = "xxxxxxxx-xxxx-xxxx-xxxx-422632e0bd95";
var userData = new CookieUserData(guidAccount) { GuidAccount = guidAccount };
authProvider.CheckAuthorizationForUrl("http://pippo");
return View();
}
}
The IAuthenticationProvider implementation should be configured to be injected into the controller at run time using the framework's DependencyResolver if using dependency injection, but can now be replaced when testing the controller in isolation so as not to be coupled to framework implementation concerns.
[TestClass]
public class HomeControllerTest {
[TestMethod]
public void Index(){
// Disposizione
var authMock = new Mock<IAuthenticationProvider>();
var controller = new HomeController(authMock.Object);
// Azione
ViewResult result = controller.Index() as ViewResult;
// Asserzione
Assert.IsNotNull(result);
Assert.AreEqual("Home Page", result.ViewBag.Title);
}
}

I will scope my answer only to System.Web.HttpContext.Current.
It is set automatically by IIS.
In order to use it you could use Microsoft fakes (https://learn.microsoft.com/en-us/visualstudio/test/isolating-code-under-test-with-microsoft-fakes).
The article above explains how to implement it. You would then have access to Current without getting null error.

Related

C# System.InvalidOperationException DisplayClass0_0 error while getting token

I need to get token when I register, but it gives me an error
Hello everyone.
I got that error when I was trying to register to my project on Postman:
https://anotepad.com/note/read/tgrka47d
(System.InvalidOperationException: An exception was thrown while attempting to evaluate the LINQ query parameter expression 'value(DataAccess.Concrete.EntityFramework.EfUserDal+<>c__DisplayClass0_0).user.Id'. See the inner exception for more information.)
My UserManager is here:
`
using System.Collections.Generic;
using Business.Abstract;
using Core.Entities.Concrete;
using DataAccess.Abstract;
namespace Business.Concrete
{
public class UserManager : IUserService
{
IUserDal _userDal;
public UserManager(IUserDal userDal)
{
_userDal = userDal;
}
public List<OperationClaim> GetClaims(User user)
{
return _userDal.GetClaims(user);
}
public void Add(User user)
{
_userDal.Add(user);
}
public User GetByMail(string email)
{
return _userDal.Get(u => u.Email == email);
}
}
}
`
My AuthManager is here:
`
using Business.Abstract;
using Business.Constants;
using Core.Entities.Concrete;
using Core.Utilities.Results;
using Core.Utilities.Security.Hashing;
using Core.Utilities.Security.JWT;
using Entities.DTOs;
namespace Business.Concrete
{
public class AuthManager : IAuthService
{
private IUserService _userService;
private ITokenHelper _tokenHelper;
public AuthManager(IUserService userService, ITokenHelper tokenHelper)
{
_userService = userService;
_tokenHelper = tokenHelper;
}
public IDataResult<User> Register(UserForRegisterDto userForRegisterDto, string password)
{
byte[] passwordHash, passwordSalt;
HashingHelper.CreatePasswordHash(password, out passwordHash, out passwordSalt);
var user = new User
{
Email = userForRegisterDto.Email,
FirstName = userForRegisterDto.FirstName,
LastName = userForRegisterDto.LastName,
PasswordHash = passwordHash,
PasswordSalt = passwordSalt,
Status = true
};
_userService.Add(user);
return new SuccessDataResult<User>(user, Messages.UserRegistered);
}
public IDataResult<User> Login(UserForLoginDto userForLoginDto)
{
var userToCheck = _userService.GetByMail(userForLoginDto.Email);
if (userToCheck == null)
{
return new ErrorDataResult<User>(Messages.UserNotFound);
}
if (!HashingHelper.VerifyPasswordHash(userForLoginDto.Password, userToCheck.PasswordHash, userToCheck.PasswordSalt))
{
return new ErrorDataResult<User>(Messages.PasswordError);
}
return new SuccessDataResult<User>(userToCheck, Messages.SuccessfulLogin);
}
public IResult UserExists(string email)
{
if (_userService.GetByMail(email) != null)
{
return new ErrorResult(Messages.UserAlreadyExists);
}
return new SuccessResult();
}
public IDataResult<AccessToken> CreateAccessToken(User user)
{
var claims = _userService.GetClaims(user);
var accessToken = _tokenHelper.CreateToken(user, claims);
return new SuccessDataResult<AccessToken>(accessToken, Messages.AccessTokenCreated);
}
}
}
`
My AuthController is here:
`
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Business.Abstract;
using Entities.DTOs;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace WebAPI.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class AuthController : Controller
{
private IAuthService _authService;
public AuthController(IAuthService authService)
{
_authService = authService;
}
[HttpPost("login")]
public ActionResult Login(UserForLoginDto userForLoginDto)
{
var userToLogin = _authService.Login(userForLoginDto);
if (!userToLogin.Success)
{
return BadRequest(userToLogin.Message);
}
var result = _authService.CreateAccessToken(userToLogin.Data);
if (result.Success)
{
return Ok(result.Data);
}
return BadRequest(result.Message);
}
[HttpPost("register")]
public ActionResult Register(UserForRegisterDto userForRegisterDto)
{
var userExists = _authService.UserExists(userForRegisterDto.Email);
if (!userExists.Success)
{
return BadRequest(userExists.Message);
}
var registerResult = _authService.Register(userForRegisterDto,userForRegisterDto.Password);
var result = _authService.CreateAccessToken(registerResult.Data);
if (result.Success)
{
return Ok(result.Data);
}
return BadRequest(result.Message);
}
}
}
`
My EfUserDal is here:
`
using System.Collections.Generic;
using Core.DataAccess.EntityFramework;
using Core.Entities.Concrete;
using DataAccess.Abstract;
using System.Linq;
namespace DataAccess.Concrete.EntityFramework
{
public class EfUserDal : EfEntityRepositoryBase<User,CarRentalContext>,IUserDal
{
public List<OperationClaim> GetClaims(User user)
{
using (CarRentalContext context = new CarRentalContext())
{
var result = from operationClaim in context.OperationClaims
join userOperationClaim in context.UserOperationClaims
on operationClaim.Id equals userOperationClaim.OperationClaimId
where userOperationClaim.UserId == user.Id
select new OperationClaim {Id = operationClaim.Id, Name = operationClaim.Name};
return result.ToList();
}
}
}
}
`
I need to get a token when I register. How can I fix this?
You picked up the wrong lines from your stacktrace to focus on.
It states the following in the inner exception:
System.NullReferenceException: Object reference not set to an instance of an object.
at DataAccess.Concrete.EntityFramework.EfUserDal.GetClaims(User user) in /home/mert/Desktop/Project/ReCapProject/DataAccess/Concrete/EntityFramework/EfUserDal.cs:line 20
Which means you have a variable on line 20 in your EfUserDal which does not have a value at runtime.
Since the next line mentions ToList, I'm guessing result is null after evaluating the query. Use debugging to see what value each variable in your query is getting before it's evaluated. Probably one of them is null

How to assign an object, for example, the owner's car [duplicate]

I've done this before with MVC5 using User.Identity.GetUserId() but that doesn't seem to work here.
The User.Identity doesn't have the GetUserId() method.
I am using Microsoft.AspNet.Identity.
Update in ASP.NET Core Version >= 2.0
In the Controller:
public class YourControllerNameController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
public YourControllerNameController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public async Task<IActionResult> YourMethodName()
{
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier) // will give the user's userId
var userName = User.FindFirstValue(ClaimTypes.Name) // will give the user's userName
// For ASP.NET Core <= 3.1
ApplicationUser applicationUser = await _userManager.GetUserAsync(User);
string userEmail = applicationUser?.Email; // will give the user's Email
// For ASP.NET Core >= 5.0
var userEmail = User.FindFirstValue(ClaimTypes.Email) // will give the user's Email
}
}
In some other class:
public class OtherClass
{
private readonly IHttpContextAccessor _httpContextAccessor;
public OtherClass(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void YourMethodName()
{
var userId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
}
}
Then you should register IHttpContextAccessor in the Startup class as follows:
public void ConfigureServices(IServiceCollection services)
{
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// Or you can also register as follows
services.AddHttpContextAccessor();
}
For more readability write extension methods as follows:
public static class ClaimsPrincipalExtensions
{
public static T GetLoggedInUserId<T>(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
var loggedInUserId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
if (typeof(T) == typeof(string))
{
return (T)Convert.ChangeType(loggedInUserId, typeof(T));
}
else if (typeof(T) == typeof(int) || typeof(T) == typeof(long))
{
return loggedInUserId != null ? (T)Convert.ChangeType(loggedInUserId, typeof(T)) : (T)Convert.ChangeType(0, typeof(T));
}
else
{
throw new Exception("Invalid type provided");
}
}
public static string GetLoggedInUserName(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirstValue(ClaimTypes.Name);
}
public static string GetLoggedInUserEmail(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirstValue(ClaimTypes.Email);
}
}
Then use as follows:
public class YourControllerNameController : Controller
{
public IActionResult YourMethodName()
{
var userId = User.GetLoggedInUserId<string>(); // Specify the type of your UserId;
var userName = User.GetLoggedInUserName();
var userEmail = User.GetLoggedInUserEmail();
}
}
public class OtherClass
{
private readonly IHttpContextAccessor _httpContextAccessor;
public OtherClass(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void YourMethodName()
{
var userId = _httpContextAccessor.HttpContext.User.GetLoggedInUserId<string>(); // Specify the type of your UserId;
}
}
Until ASP.NET Core 1.0 RC1 :
It's User.GetUserId() from System.Security.Claims namespace.
Since ASP.NET Core 1.0 RC2 :
You now have to use UserManager.
You can create a method to get the current user :
private Task<ApplicationUser> GetCurrentUserAsync() => _userManager.GetUserAsync(HttpContext.User);
And get user information with the object :
var user = await GetCurrentUserAsync();
var userId = user?.Id;
string mail = user?.Email;
Note :
You can do it without using a method writing single lines like this string mail = (await _userManager.GetUserAsync(HttpContext.User))?.Email, but it doesn't respect the single responsibility principle. It's better to isolate the way you get the user because if someday you decide to change your user management system, like use another solution than Identity, it will get painful since you have to review your entire code.
you can get it in your controller:
using System.Security.Claims;
var userId = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
or write an extension method like before .Core v1.0
using System;
using System.Security.Claims;
namespace Shared.Web.MvcExtensions
{
public static class ClaimsPrincipalExtensions
{
public static string GetUserId(this ClaimsPrincipal principal)
{
if (principal == null)
throw new ArgumentNullException(nameof(principal));
return principal.FindFirst(ClaimTypes.NameIdentifier)?.Value;
}
}
}
and get wherever user ClaimsPrincipal is available :
using Microsoft.AspNetCore.Mvc;
using Shared.Web.MvcExtensions;
namespace Web.Site.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return Content(this.User.GetUserId());
}
}
}
I included using System.Security.Claims and I could access the GetUserId() extension method
NB: I had the using Microsoft.AspNet.Identity already but couldn't get the extension method. So I guess both of them have to be used in conjunction with one another
using Microsoft.AspNet.Identity;
using System.Security.Claims;
EDIT:
This answer is now outdated. Look at Soren's or Adrien's answer for a dated way of achieving this in CORE 1.0
For .NET Core 2.0 Only The following is required to fetch the UserID of the logged-in User in a Controller class:
var userId = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
or
var userId = HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
e.g.
contact.OwnerID = this.User.FindFirstValue(ClaimTypes.NameIdentifier);
As stated somewhere in this post, the GetUserId() method has been moved to the UserManager.
private readonly UserManager<ApplicationUser> _userManager;
public YourController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
public IActionResult MyAction()
{
var userId = _userManager.GetUserId(HttpContext.User);
var model = GetSomeModelByUserId(userId);
return View(model);
}
If you started an empty project you might need to add the UserManger to your services in startup.cs. Otherwise this should already be the case.
you have to import Microsoft.AspNetCore.Identity & System.Security.Claims
// to get current user ID
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
// to get current user info
var user = await _userManager.FindByIdAsync(userId);
For ASP.NET Core 2.0, Entity Framework Core 2.0, AspNetCore.Identity 2.0 api (https://github.com/kkagill/ContosoUniversity-Backend):
The Id was changed to User.Identity.Name
[Authorize, HttpGet("Profile")]
public async Task<IActionResult> GetProfile()
{
var user = await _userManager.FindByIdAsync(User.Identity.Name);
return Json(new
{
IsAuthenticated = User.Identity.IsAuthenticated,
Id = User.Identity.Name,
Name = $"{user.FirstName} {user.LastName}",
Type = User.Identity.AuthenticationType,
});
}
Response:
In .net core 3.1 (and other more recent versions), you can use:
private readonly UserManager<IdentityUser> _userManager;
public ExampleController(UserManager<IdentityUser> userManager)
{
_userManager = userManager;
}
Then:
string userId = _userManager.GetUserId(User);
Or async:
var user = await _userManager.GetUserAsync(User);
var userId = user.Id;
At this point, I'm trying to figure out why you'd use one over the other. I know the general benefits of async, but see both of these used frequently. Please post some comments if anyone knows.
For ASP.NET 5.0, I have an extension method as follow:
using System;
using System.ComponentModel;
using System.Security.Claims;
namespace YOUR_PROJECT.Presentation.WebUI.Extensions
{
public static class ClaimsPrincipalExtensions
{
public static TId GetId<TId>(this ClaimsPrincipal principal)
{
if (principal == null || principal.Identity == null ||
!principal.Identity.IsAuthenticated)
{
throw new ArgumentNullException(nameof(principal));
}
var loggedInUserId = principal.FindFirstValue(ClaimTypes.NameIdentifier);
if (typeof(TId) == typeof(string) ||
typeof(TId) == typeof(int) ||
typeof(TId) == typeof(long) ||
typeof(TId) == typeof(Guid))
{
var converter = TypeDescriptor.GetConverter(typeof(TId));
return (TId)converter.ConvertFromInvariantString(loggedInUserId);
}
throw new InvalidOperationException("The user id type is invalid.");
}
public static Guid GetId(this ClaimsPrincipal principal)
{
return principal.GetId<Guid>();
}
}
}
So you can use it like:
using Microsoft.AspNetCore.Mvc;
using YOUR_PROJECT.Presentation.WebUI.Extensions;
namespace YOUR_PROJECT.Presentation.WebUI.Controllers
{
public class YourController :Controller
{
public IActionResult YourMethod()
{
// If it's Guid
var userId = User.GetId();
// Or
// var userId = User.GetId<int>();
return View();
}
}
}
in the APiController
User.FindFirst(ClaimTypes.NameIdentifier).Value
Something like this you will get the claims
Although Adrien's answer is correct, you can do this all in single line. No need for extra function or mess.
It works I checked it in ASP.NET Core 1.0
var user = await _userManager.GetUserAsync(HttpContext.User);
then you can get other properties of the variable like user.Email. I hope this helps someone.
For getting current user id in razor views, we can inject UserManager in the view like this:
#inject Microsoft.AspNetCore.Identity.UserManager<ApplicationUser> _userManager
#{ string userId = _userManager.GetUserId(User); }
I hope you find it useful.
User.Identity.GetUserId();
does not exist in asp.net identity core 2.0. in this regard, i have managed in different way. i have created a common class for use whole application, because of getting user information.
create a common class PCommon & interface IPCommon
adding reference using System.Security.Claims
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace Common.Web.Helper
{
public class PCommon: IPCommon
{
private readonly IHttpContextAccessor _context;
public PayraCommon(IHttpContextAccessor context)
{
_context = context;
}
public int GetUserId()
{
return Convert.ToInt16(_context.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier));
}
public string GetUserName()
{
return _context.HttpContext.User.Identity.Name;
}
}
public interface IPCommon
{
int GetUserId();
string GetUserName();
}
}
Here the implementation of common class
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.Extensions.Logging;
using Pay.DataManager.Concreate;
using Pay.DataManager.Helper;
using Pay.DataManager.Models;
using Pay.Web.Helper;
using Pay.Web.Models.GeneralViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Pay.Controllers
{
[Authorize]
public class BankController : Controller
{
private readonly IUnitOfWork _unitOfWork;
private readonly ILogger _logger;
private readonly IPCommon _iPCommon;
public BankController(IUnitOfWork unitOfWork, IPCommon IPCommon, ILogger logger = null)
{
_unitOfWork = unitOfWork;
_iPCommon = IPCommon;
if (logger != null) { _logger = logger; }
}
public ActionResult Create()
{
BankViewModel _bank = new BankViewModel();
CountryLoad(_bank);
return View();
}
[HttpPost, ActionName("Create")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Insert(BankViewModel bankVM)
{
if (!ModelState.IsValid)
{
CountryLoad(bankVM);
//TempData["show-message"] = Notification.Show(CommonMessage.RequiredFieldError("bank"), "Warning", type: ToastType.Warning);
return View(bankVM);
}
try
{
bankVM.EntryBy = _iPCommon.GetUserId();
var userName = _iPCommon.GetUserName()();
//_unitOfWork.BankRepo.Add(ModelAdapter.ModelMap(new Bank(), bankVM));
//_unitOfWork.Save();
// TempData["show-message"] = Notification.Show(CommonMessage.SaveMessage(), "Success", type: ToastType.Success);
}
catch (Exception ex)
{
// TempData["show-message"] = Notification.Show(CommonMessage.SaveErrorMessage("bank"), "Error", type: ToastType.Error);
}
return RedirectToAction(nameof(Index));
}
}
}
get userId and name in insert action
_iPCommon.GetUserId();
Thanks,
Maksud
TLDR:
In the Controler add:
using System.Security.Claims;
and then you can use:
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
endof TLDR;
Just an easy way in dot net 6 to test how to get the userID and test it in the default Blazor WebAssembly Core Hosted:
I added a String in WeatherForecast class named userId
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string userId { get; set; } = "nope";
}
Then in the WeatherForecastController
I add
using System.Security.Claims;
In the GET method I set WeatherForecast.userId to User.FindFirstValue(ClaimTypes.NameIdentifier):
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)],
userId = User.FindFirstValue(ClaimTypes.NameIdentifier)
})
.ToArray();
}
And finally in the FetchData.razor I modify the table to:
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
<th>User Id</th>
</tr>
</thead>
<tbody>
#foreach (var forecast in forecasts)
{
<tr>
<td>#forecast.Date.ToShortDateString()</td>
<td>#forecast.TemperatureC</td>
<td>#forecast.TemperatureF</td>
<td>#forecast.Summary</td>
<td>#forecast.userId</td>
</tr>
}
</tbody>
</table>
And then finally I get:
I hope it helps because in net core 6 sometimes it's quite difficult to find the answers
If you are using JWT tokens this code works:
User.FindFirstValue("sub");
use can use
string userid = User.FindFirst("id").Value;
for some reason NameIdentifier now retrieve the username (.net core 2.2)
Make sure that you have enable windows authentication. If you have anonymous authentication enabled you may be getting a null string.
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-3.1&tabs=visual-studio
I know there are many answers posted already, but maybe it will help someone as it did for me.
I mixed two solutions into one, and I am able to get the logged-in User and its Data.
I was using DotNet 5.
Following code, help to get the logged-in User.
var user = await _userManager.FindByNameAsync(HttpContext.User.Identity.Name);
I used the following package for _userManager
using Microsoft.AspNetCore.Identity;
And for HttpContext, I inherit my Controller from ControllerBase, and for ControllerBase Class I was using the following package
using Microsoft.AspNetCore.Mvc;
As an administrator working on other people's profile and you need to get the Id of the profile you are working on, you can use a ViewBag to capture the Id e.g ViewBag.UserId = userId; while userId is the string Parameter of the method you are working on.
[HttpGet]
public async Task<IActionResult> ManageUserRoles(string userId)
{
ViewBag.UserId = userId;
var user = await userManager.FindByIdAsync(userId);
if (user == null)
{
ViewBag.ErrorMessage = $"User with Id = {userId} cannot be found";
return View("NotFound");
}
var model = new List<UserRolesViewModel>();
foreach (var role in roleManager.Roles)
{
var userRolesViewModel = new UserRolesViewModel
{
RoleId = role.Id,
RoleName = role.Name
};
if (await userManager.IsInRoleAsync(user, role.Name))
{
userRolesViewModel.IsSelected = true;
}
else
{
userRolesViewModel.IsSelected = false;
}
model.Add(userRolesViewModel);
}
return View(model);
}
If you want this in ASP.NET MVC Controller, use
using Microsoft.AspNet.Identity;
User.Identity.GetUserId();
You need to add using statement because GetUserId() won't be there without it.

Role Based Authentication for web API not working

I'm facing an issue while working with Role-Based authentication for web APi.
I have a controller class where the controller has a custom authorize attribute called Myauthorize.
I have a method inside the controller which can be accessed only with Admin access.
But the same method has been calling with QA access as well.
Could anyone please help with the below?
Please find the code below.
Controller :
namespace Hosiptal.Controllers.office
{
[MyAuthorize(Constants.Roles.Admin)]
public class UserRolesController : ApiController
{
private readonly IRepository<EntityModels.Role> rolesRepository;
public UserRolesController(IRepository<EntityModels.Role> rolesRepository)
{
this.rolesRepository = rolesRepository;
}
// GET: Users
[HttpGet]
[Route("")]
public IEnumerable<Role> GetAll()
{
return this.rolesRepository.GetAll()
.ToArray()
.Select(r => Mapper.Current.Get<Role>(r));
}
}
}
MyAuthorize has followed.
namespace Hospital.Web.Filters.WebApi
{
public class MyAuthorize: AuthorizeAttribute
{
private readonly string[] allowedroles;
private static IUserProfileRepository UserProfileRepository
{
get { return IoC.Current.Resolve<IUserProfileRepository>(); }
}
public MyAuthorize(params string[] roles)
{
this.allowedroles = roles;
}
public override Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken
cancellationToken)
{
var claimsIdentity = actionContext.RequestContext.Principal.Identity as ClaimsIdentity;
var alias = claimsIdentity.Name.Split('#')[0];
if (alias == null)
{
throw new ArgumentNullException(nameof(actionContext));
}
user(alias);
return base.OnAuthorizationAsync(actionContext, cancellationToken);
}
public static GenericPrincipal user(string userName)
{
userName = userName.ToUpper();
var userProfile = UserProfileRepository.Get(userName) ?? new UserProfile()
{
UserName = userName,
Roles = new List<Role>(),
FirstLoginDateUtc = DateTime.UtcNow
};
return CreatePrincipal(userProfile);
}
public static GenericPrincipal CreatePrincipal(UserProfile user)
{
var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name,
user.UserName) }, "Custom");
return new GenericPrincipal(identity, user.Roles.Select(i =>
i.Name).ToArray());
}
}
}
How can restrict the user here based on access level?
If you review the source code for the AuthorizeAttribute class, you will see that it uses the controller context request's principal to perform authorization, so override the IsAuthorized method instead, move your code there and assign the principal you create to the context request's principal:
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var claimsIdentity = actionContext.RequestContext.Principal.Identity as ClaimsIdentity;
var alias = claimsIdentity.Name.Split('#')[0];
if (alias == null)
{
throw new ArgumentNullException(nameof(actionContext));
}
//This sets the context's principal so the base class code can validate
actionContext.ControllerContext.RequestContext.Principal = user(alias);
//Call the base class and let it work its magic
return base.IsAuthorized(actionContext);
}
I will refrain from commenting on the design itself. This should fix your issue.
This is what's working for me
public class AdminAuthorizeAttributee : AuthorizeAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
if (AuthorizeRequest(actionContext))
{
return;
}
HandleUnauthorizedRequest(actionContext);
}
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{
base.HandleUnauthorizedRequest(actionContext);
}
private bool AuthorizeRequest(HttpActionContext actionContext)
{
try
{
var username = HttpContext.Current.User.Identity.Name;
var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
var user = userManager.Users.Where(a => a.UserName == username).FirstOrDefault();
var rolesForUser = userManager.GetRoles(user.Id);
var role = "Admin";
if (rolesForUser.Contains(role))
{
return true;
}
return false;
}
catch (Exception ex)
{
return false;
}
}
}
and in Controller
[AdminAuthorizeAttributee]
public class YOUR_Controller : ApiController
You don't need to create your own authorize filter for this.
Use the built-in [Authorize(Roles = "Admin")] - which will check if the user has a claim called "http://schemas.microsoft.com/ws/2008/06/identity/claims/role" and if that value matches to the one you put in that authorize attribute, the authorization will succeed.
So in your case just make sure, when you log in the user to set his claim with the role like this:
var claims = new List<Claim>()
{
new Claim(ClaimTypes.Role, "Admin"), //here set the users role
// ... other claims
};
(ClaimTypes class is from the namespace System.Security.Claims)
And then the [Authorize(Roles = "Admin")] should work just fine

C# MVC controller breaks on Login

InvalidOperationException: Unable to resolve service for type 'Echelon.Data.IAssignmentRepository' while attempting to activate 'Echelon.Controllers.SessionController'.
I keep getting this Error whenever I try to login or click something
in my navbar, I'm confused as to why it's erroring out, can someone
please help? This started when I got email verification working, then
whenever I try to login or click anything I get the error?
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Echelon.Models.SessionViewModels;
using Echelon.Classes;
using Microsoft.AspNetCore.Identity;
using Echelon.Models;
using Echelon.Data;
namespace Echelon.Controllers
{
public class SessionController : Controller
{
StoredProcedure storedProcedure = new StoredProcedure();
private IAssignmentRepository repository;
private readonly AssignmentDbContext _context;
public SessionController(IAssignmentRepository repo, AssignmentDbContext context)
{
repository = repo;
_context = context;
}
[Authorize]
public IActionResult Home()
{
return View();
}
public IActionResult Courses()
{
var Courses = storedProcedure.getCourseNames(User.Identity.Name);
var CoursesView = new CoursesViewModel();
foreach (var Course in Courses.ToList())
{
Course course = new Course();
course.CourseName = Course;
CoursesView.Courses.Add(course);
}
return View(CoursesView);
}
public IActionResult CreateCourse()
{
return View();
}
[HttpPost]
public IActionResult CreateCourse(CreateCourseModel course)
{
if (ModelState.IsValid)
{
storedProcedure.addCourse(User.Identity.Name, course.CourseName, course.CourseDesc, course.StartDate, course.EndDate);
return RedirectToAction("Courses");
}
else
{
return View();
}
}
public IActionResult Assignments(string courseName)
{
var assignments = storedProcedure.getAssignments(User.Identity.Name, courseName);
var AssignmentsView = new AssignmentsViewModel { CourseName = courseName };
foreach (var Assignment in assignments.ToList())
{
AssignmentsView.Assignments.Add(Assignment);
}
return View(AssignmentsView);
}
public IActionResult CreateAssignment(string courseName)
{
CreateAssignment assignmentModel = new CreateAssignment();
assignmentModel.CourseName = courseName;
assignmentModel.UserName = User.Identity.Name;
return View(assignmentModel);
}
[HttpPost]
public async Task<IActionResult> CreateAssignment([Bind("AssignmentID,UserName,CourseName,AssignmentName,AssignmentDescription,TotalPoints,DueDate")] CreateAssignment assignment)
{
if (ModelState.IsValid)
{
_context.Add(assignment);
try
{
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
return View(assignment);
}
//return View(assignment);
return RedirectToAction("Assignments", "Session", new { courseName = assignment.CourseName });
}
else
return View(assignment);
}
public IActionResult Students(string courseName)
{
var students = storedProcedure.getStudents(User.Identity.Name, courseName);
var studentsView = new StudentsViewModel();
foreach (var student in students.ToList())
{
Student Student = new Student();
Student.StudentFName = student.StudentFName;
Student.StudentLName = student.StudentLName;
Student.CourseName = student.CourseName;
studentsView.Students.Add(Student);
}
return View(studentsView);
}
public IActionResult AllStudents()
{
var students = storedProcedure.getAllStudents(User.Identity.Name);
var studentsView = new AllStudentsViewModel();
foreach (var student in students.ToList())
studentsView.Students.Add(student);
return View(studentsView);
}
public IActionResult Attendance()
{
return View();
}
}
}
You usually get that error when you are trying to use an implementation of an interface, but you have not registered the concrete implementation you want to use with the framework.
If you are using the default dependency injection framework used by asp.net core, you should register it in the ConfigureServices method in Startup.cs class
services.AddTransient<IAssignmentRepository , AssignmentRepository>();
where AssignmentRepository is a your concrete class where you are implementing the IAssignmentRepository interface.
public interface IAssignmentRepository
{
IEnumerable<CreateAssignment> Assignments { get; }
}
public class AssignmentRepository : IAssignmentRepository
{
public IEnumerable<CreateAssignment> Assignments
{
get
{
return new List<CreateAssignment>()
{
new CreateAssignment(),
new CreateAssignment()
};
}
}
}
Here i just hard coded the Assignments property to return 2 CreateAssignment objects. But i guess you probably will be reading it from database and return it.
Now when a new request comes for your controller action method, the framework will create a new object of SessionController and pass an object of AssignmentRepository to the constructor of SessionController
If you are not familiar with the dependency injection concept, i strongly suggest you to spend 16 minutes to read the excellent documentation on learn.microsoft.com before trying to write any further code.
Introduction to Dependency Injection in ASP.NET Core

Login Identity - Not working in HttpContext

My website uses " login form using IPrincipal and IIdentity .
The code can be seen below._iTs work Great
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Web;
namespace FREELANCER.Models
{
public class UserPrincipal : IPrincipal
{
private LoginModel LoginInfo;
private DemoUsers d = new DemoUsers();
public UserPrincipal(LoginModel log)
{
LoginInfo = log;
Identity = new GenericIdentity(log.Emailaddress);
}
public IIdentity Identity
{
get;
private set;
}
public bool IsInRole(string role)
{
var roles = role.Split(new char[] { ',' });
return roles.Any(r => this.LoginInfo.Roles.Contains(r));
}
}
}
But I canĀ“t get this to work:
string name = User.Identity.Name;
string AuthenticationType = User.Identity.AuthenticationType;
bool ss = User.Identity.IsAuthenticated;
bool Inrole =User.IsInRole("Admin");
They are alway empty, Why ?
Is it true that the object of IPrincipal and IIdentity will Copied to HttpContext and is therefore available throughout the website?
NB: namespace FREELANCER.Security
{
public class UserAuthorize : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (String.IsNullOrEmpty(SessionPersister.UserName))
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { Controller = "Login", Action = "Login" }));
else
{
DemoUsers ds = new DemoUsers();
UserPrincipal mp = new UserPrincipal(ds.FindUser(SessionPersister.UserName));
if (!mp.IsInRole(Roles))
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { Controller = "AccessDenied", Action = "Index" }));
}
}
}
}
After creating the new Principal object, you also need to attach it to current HttpContext object.
UserPrincipal mp = new UserPrincipal(ds.FindUser(SessionPersister.UserName));
HttpContext.Current.User = mp;
Thread.CurrentPrincipal = mp;
Second line is option unless you decide to call UserPrincipal from current thread - Thread.CurrentPrincipal as UserPrincipal.

Categories

Resources