Getting Current User in Controller - c#

Hope somebody can help me with this. I am trying to create a Change Password Post Request in the Account controller, but my user is always returning null. I'm new to .NET, could someone help up me figure out the best way to retrive the current user Id? Here is the code I have thus far.
[HttpPost("changePassword")]
public async Task<ActionResult<ChangePasswordDTO>> ChangePassword([FromBody] ChangePasswordDTO changePassword) {
var currentUserId = User.Claims.ToList()
.FirstOrDefault(x => x.Type == "").Value;
var user = await _userManager.FindByIdAsync(currentUserId);
var result = await _userManager.ChangePasswordAsync(user, changePassword.Password, changePassword.NewPassword);
if (result.Succeeded) {
return Ok("Password changed succesfully");
} else return Unauthorized("An error occurred while attempting to change password");
}
When I change the "currentUserId" to a hard coded value, everything works fine, of course. Every user has a generated jwt token with his name, id, roles etc. I'm unsure how to proceed, everything I've tried retrieves the user as null. Let me know if you need extra information, I'm pretty new to all of this and appreciate whatever help I can get, thanks!
Solution
JamesS pointed me in the right direction, this code works:
var currentUserId =this.User.FindFirstValue(ClaimTypes.NameIdentifier);

You can get the current user id in your controller using the UserManager such as:
ASP.NET CORE >= 2.0
var currentUserId = User.FindFirstValue(ClaimTypes.NameIdentifier);
or Name using:
var currentUserId = User.FindFirstValue(ClaimTypes.Name);
If you want to get the current user in some other class, you can use the IHttpContextAccessor, passing it into the class's constructor and using the HttpContext to access the User

Related

C# .NET core Best way to get current user ID

I have a lot of tables in my database that use a user's identity User.Id as a foreign key. Now, in a lot of the requests I need to do the below lines it seems just in case the User is null (even if I add [Authorise] filter to the function).
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
ViewBag.ErrorCode = "1201";
ViewBag.ErrorMsg = "User not found";
return View("HandledError");
}
var userId = user.Id;
Does anyone know the best way I can access this without copying this same code for each function?
I guess I could have a function that retrieves the Id... and throws an exception on Null.. but then I would still need to write it in a try catch everytime anyway....
Any ideas?
User id is a claim. You can get it via:
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
Note: You'll need to add a using for System.Security.Claims.
In WebApi 2 you can use RequestContext.Principal from within a method on ApiController
string id;
id = User.Identity.GetUserId();
id = RequestContext.Principal.Identity.GetUserId();

How do I get current user another property asp mvc 5

I have a situation.
AspNetUsers has a column called AccType which is int type
If AccType is 1 = customer
If AccType is 2 = manager.
I can access to current logged user id by User.Identity.GetUserId()
I am trying to do if statment as following:
if(User.Identity.GetUserId().AccType == 1){
redirect to specific page..
}elseif (User.Identity.GetUSerId().AccType == 2){
redirect to 2nd specific page..
}
User.Identity.GetUserId().AccType this is now working.
Please help! Thanks!
User.Identity.GetUserId() returns the id of the currently logged in user. However, if you want to access its properties, you need to get the actual user object. One way to to this is the following:
var userId = this.User.Identity.GetUserId();
var user = this.Request.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(userId);
A more convenient way will be to make a property for the ApplicationUser in your controller and then to reuse the code.
Property:
public ApplicationUserManager UserManager
{
get
{
return this.Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
}
Getting the user:
var user = this.UserManager
.FindById(this.User.Identity.GetUserId());

ASP.net Identity v2.0.0 Email Confirmation/Reset Password Errors

We just launched our ASP.net MVC (using Identity 2.0) web app 6 days ago and have had over 5000 people register for accounts, great!
The issue is 3-5% of those users who click the confirmation email link (and same seems to go for reset password) get shown the error screen presumably because they have an invalid token.
I read on StackOverflow here that you need to encode the url to avoid special characters throwing it off and then decode it right before you validate it. I did that to no effect though, some users were still getting errors on their validation token.
I also read that having different MachineKey's could be a reason tokens aren't processed as being valid. Everything is hosted on Azure so I presumed (and saw on SO) it was or should taken care of
So with 30-50 people emailing us for the past 6 days now about issues, I got desperate while I tried to come up with a solution and set my confirmEmail action to be the following:
[AllowAnonymous]
public ActionResult ConfirmEmail(string userId = null, string code = null)
{
if (userId == null || code == null)
{
return View("Error");
}
else
{
var emailCode = UserManager.GenerateEmailConfirmationToken(userId);
var result = UserManager.ConfirmEmail(userId, emailCode);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
}
I thought to myself there is no way in heck this could fail, it literally just generates a token and then immediately uses it - yet somehow it still fails (by fails I mean the user sees the error page)
My best guess as to a possible solution so far is this answer from SO (Asp.NET - Identity 2 - Invalid Token Error, halfway down)
Every time when a UserManager is created (or new-ed), a new
dataProtectionProvider is generated as well. So when a user receives
the email and clicks the link the AccountController is already not the
old one, neither are the _userManager and it's token provider. So the
new token provider will fail because it has no that token in it's
memory. Thus we need to use a single instance for the token provider.
But this is no longer necessary with all the OWIN stuff, right?
Is that really the issue still? If so, what the heck ASP.net Identity team? Why?
Some of the things I have changed:
The default Register Action recommends sending confirmation emails the following way:
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
Where I have the following in my Register Action
string callbackUrl = await SendEmailConfirmationTokenAsync(user.Id, "Confirm your XXXXXXX account");
And then I specify the following in the SendEmailConfirmationTokenAsync
private async Task<string> SendEmailConfirmationTokenAsync(string userID, string subject)
{
string code = await UserManager.GenerateEmailConfirmationTokenAsync(userID);
var callbackUrl = Url.Action("ConfirmEmail", "Account",
new { userId = userID, code = code }, protocol: Request.Url.Scheme);
// construct nice looking email body
await UserManager.SendEmailAsync(userID, subject, htmlBody);
return callbackUrl;
}
To me, both sections are equivalent, is this not the case?
And then the only other thing I can think of is how I added my db class, but that shouldn't affect the UserManager should it?
The top part of my account controller looks like the following (which is how the provided example from MS came + adding my database):
private readonly SiteClasses db = new SiteClasses();
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager )
{
UserManager = userManager;
SignInManager = signInManager;
}
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
...
We are using the recommended email provider sendgrid and I have personally never been able to replicate this issue (after creating ~60 test accounts manually) and most people seem to get along fine.
Part of this could be errors resulting from the users themselves, but this seems to be happening for a bunch of non tech-savvy people who just click on the link and expect it to work like a normal confirmation email should. Most users are coming to us from their iPhone where I would presume they are just using Apple's default mail client but not positive. I don't know of any spam filter or email settings that would remove the query string values from links.
I'm getting somewhat desperate for answers as I am trying to launch a great new startup but am getting hung up by ASP.net Identity technicalities or bugs or whatever is going on.
Advice on where to look or how to set up things would be greatly appreciated
I send the email as part of the URL currently.
We have a customer with a forwarding email account at tufts.edu going to gmail.com and it must be rewriting her email address that is part of the URL - which is HORRENDOUS but I can't see what else it is possibly doing.
This is one more thing to be aware of if I can confirm this.
I ended up confirming the email manually in the AspNetUsers table instead of creating a new token on the fly and trying to use UserManager.ConfirmEmail.
[HandleError(ExceptionType = typeof(HttpException), View = "ConfirmEmail")]
public ActionResult ConfirmEmail(string userId = null, string code = null)
{
if (userId == null || code == null)
{
return View("Error");
}
var result = await UserManager.ConfirmEmailAsync(userId, code);
if (result.Succeeded)
{
return View("ConfirmEmail");
}
else
{
var user = DBContext.Users.Find(userId);
user.EmailConfirmed = true;
DBContext.SaveChanges();
throw new HttpException(result.Errors.FirstOrDefault());
}
}
I also used [HandleError(ExceptionType = typeof(HttpException), View = "ConfirmEmail")] to still log the error but direct the use to the ConfirmEmail page still.
Not a great solution but I haven't been able to find anything to fix this issue.

asp.net webapi dynamic authorization

I am new to webapi and mvc and I am struggling to find a best practice for handling authorizations dynamically based on roles and ownership of the resource. For example an account page that should allow employee admins, employee call center or the owning client to Get, Post, Put or Delete account information. So an admin and call center employee should be able to Get, Post, Put or Delete any request for any userid, but a client should only be able to perform these actions on resources owned by them.
For example Tom is UserID 10 and Jerry is UserID 20.
/api/Account/10 should be accessible by any admin, call center or Tom. Jerry should be kicked out.
/api/Account/20 should be accessible by any admin, call center or Jerry. Tom should be kicked out.
In webforms the typical solution is to just check if the user is a client and verify their id against the request. (I know AuthorizeAttribute is not in webforms, but showing as an example of what it would covert to in webapi/mvc.)
[Authorize(Roles = "Administrator, CallCenter, Client")]
public string Get(int userID)
{
if (Thread.CurrentPrincipal.IsInRole("Client") && Thread.CurrentPrincipal.Identity.userID != userID)
{
//Kick them out of here.
}
return "value";
}
This will work, but it seems like the check for ownership should happen in a single location before it reaches the controller and should be reusable throughout an application. I am guessing the best place would either be a custom AuthorizationFilterAttribute or a custom AuthorizeAttribute and maybe create a new role ClientOwner.
[Authorize(Roles = "Administrator, CallCenter, ClientOwner")]
public string Get(int userID)
{
return "value";
}
Custom AuthorizeAttribute
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
//If user is already authenticated don't bother checking the header for credentials
if (Thread.CurrentPrincipal.Identity.IsAuthenticated) { return; }
var authHeader = actionContext.Request.Headers.Authorization;
if (authHeader != null)
{
if (authHeader.Scheme.Equals("basic", StringComparison.OrdinalIgnoreCase) &&
!String.IsNullOrWhiteSpace(authHeader.Parameter))
{
var credArray = GetCredentials(authHeader);
var userName = credArray[0];
var password = credArray[1];
//Add Authentication
if (true)
{
var currentPrincipal = new GenericPrincipal(new GenericIdentity(userName), null);
var user = GetUser(userName);
foreach (var claim in user.Cliams)
{
currentPrincipal.Identities.FirstOrDefault().AddClaim(new Claim(ClaimTypes.Role, claim);
}
//**************Not sure best way to get UserID below from url.***********************
if (user.userTypeID = UserTypeID.Client && user.userID == UserID)
{
currentPrincipal.Identities.FirstOrDefault().AddClaim(new Claim(ClaimTypes.Role, "ClientOwner"));
}
Thread.CurrentPrincipal = currentPrincipal;
return;
}
}
}
HandleUnauthorizedRequest(actionContext);
}}
Can someone point me in the right direction as to the best place to handle the authorization of the individual user? Should this still be done in the controller or should I move it to a custom AuthorizationFilterAttribute or a custom AuthorizationAttribute or is there somewhere else this should be handled? If the proper place is in a custom attribute, then what is the best way to get the userID and should I create a new role like the example above or should I do something different?
This is a common scenario and I am very surprised that I have struggled to find examples of the above scenario. This leads me to believe that either everyone is doing the check in the controller or there is another term I am not aware of so I am not getting good google results.
I think you may be getting authorization and permissions confused. "Dynamic authorization" isn't something you ever do.
Authorization is the act of verifying an author.
Request claims it is being sent from Alice.
Request presents a password or authorization token that proves the requester is Alice.
Server verifies that the password or authorization token matches its records for Alice.
Permissions are the business logic that specifies who can do what in your system.
Request is already authorized, and we know it came from Alice.
Alice is requesting to delete an important resource.
Is Alice an administrator? If not, tell her she can't do that because she doesn't have permission. (403 Forbidden)
The built-in [Authorize] attribute lets you optionally specify Roles that are permitted to access a resource. That option to specify permissions as part of authorization is slightly misplaced, in my opinion.
My advice would be to leave authorization as purely the process of verifying the author of a request. The BasicAuthHttpModule described here is close to what you want already.
Non-trivial permissions logic needs to be handled inside of your action body. Here's an example:
//Some authorization logic:
// Only let a request enter this action if the author of
// the request has been verified
[Authorize]
[HttpDelete]
[Route("resource/{id}")]
public IHttpActionResult Delete(Guid id)
{
var resourceOwner = GetResourceOwner(id);
//Some permissions logic:
// Only allow deletion of the resource if the
// user is both an admin and the owner.
if (!User.IsInRole("admin") || User.Identity.Name != resourceOwner)
{
return StatusCode(HttpStatusCode.Forbidden);
}
DeleteResource(id);
return StatusCode(HttpStatusCode.NoContent);
}
In this example, it would be difficult to convey the permissions logic as an attribute on the action, because the portion of the permissions that compares the current user to the resource owner can only be evaluated after you have actually gotten the resource owner info from your backend storage device.

Role Management in ASP MVC 5 (Microsoft.AspNet.Identity)

in ASP MVC5 RC I didn't get the role system to work.
My database has all needs tables an role exist but proofing if user is in role always return false (no SQL exception or something)!?
Did I need to activate role system for IPrincipal somewhere?
Test code:
AccountController accCont = new AccountController();
// check role exist : result = true
var roleExist = await accCont.IdentityManager.Roles.RoleExistsAsync("61c84919-72e2-4114-9520-83a3e5f09de1");
// try find role by name : result = role object
var role = await accCont.IdentityManager.Roles.FindRoleByNameAsync("ProjectAdministrator");
// check with AccountController instance : result = true
var exist = await accCont.IdentityManager.Roles.IsUserInRoleAsync(User.Identity.GetUserId(), role.Id);
// check if current user is in role : result (both) = false????
var inRole = User.IsInRole(role.Id);
var inRole2 = User.IsInRole(role.Name);
I also try to build an custom extenuation like the IIdentity.GetUserId() extension method from Microsoft.AspNet.Identity.Owin Namespace.
namespace Microsoft.AspNet.Identity
{
public static class IdentityExtensions
{
public static string IsUserInRole(this IIdentity identity)
{
if (identity == null)
{
throw new ArgumentNullException("identity");
}
ClaimsIdentity identity2 = identity as ClaimsIdentity;
if (identity2 != null)
{
var result = identity2.FindFirstValue(IdentityConfig.Settings.GetAuthenticationOptions().RoleClaimType);
return null; // later result
}
return null;
}
}
}
But the result for claim Type RoleClaimType is always null :(
I'm really stuck with this.
Thank you for your help! Steffen
I'm trying to understand how to use roles in MVC 5 myself, which is what brought me here. I can't answer your question, but check out this link. The downloaded solution works right out of the box and I've already been able to cut-and-paste some of the code and get it working in my own app. Now I'm trying to fully understand what it's doing.
http://www.typecastexception.com/post/2013/11/11/Extending-Identity-Accounts-and-Implementing-Role-Based-Authentication-in-ASPNET-MVC-5.aspx
It may not answer your question but at least it's a fully working solution that actually does work as described without a lot of hassle, so it's a good starting point.
User.IsInRole is basically looking at the claims for the currently signed in user. What does your sign in logic look like? That is what is responsible for minting the cookie that turns into the User identity. That needs to have the Role claim set properly for the IsInRole method to work correctly.

Categories

Resources