FindByNameAsync throwing null exception - asp.net-core-identity

Currently my application that is asp.net core application validates user by userEmail which now needs to be change to validate it by userName.
I have changed the method to FindByNameAsync from FindByEmailAsync. It works If I enter the email address but if I use a
n userName it doesn't work. It throws a null exception for response.Result.UserName
Could somebody tell me what the problem could be
public async Task<SignInResult> PasswordSignInAsync(string user, string password, bool isPersistent, bool lockoutOnFailure)
{
var userName = GetUserName(user);
return await _signInManager.PasswordSignInAsync(userName, password, isPersistent, lockoutOnFailure);
}
private string GetUserName(string userName)
{
//if (!userNameOrEmail.Contains("#"))
// return userNameOrEmail;
var response = _userManager.FindByNameAsync(userName);
return response.Result != null ? response.Result.UserName : string.Empty;
}

You should await asynchronous calls to get results:
var response = await _userManager.FindByNameAsync(userName);
Also you will need to decorate GetUserName function with async

First, don't block on async tasks. You're in an async method, so there's no reason not to actually await the result:
private async Task<string> GetUserNameAsync(string userName)
{
var response = await _userManager.FindByNameAsync(userName);
return response?.UserName ?? string.Empty;
}
Then:
var userName = await GetUserNameAsync(user);
Second, this doesn't make any sense anyways, as you already have the username. You just need to know whether it exists yet or not. As such, you should really have something like?
public async Task<bool> UserNameExistsAsync(string userName)
{
return await _userManager.FindByNameAsync(userName) != null;
}
Then:
if (await UserNameExistsAsync(user))
{
// do something
}
And, actually, it would be better to use the context directly here, but that might require an additional dependency:
var userNameExists = await _context.Users.AnyAsync(x => x.UserName == user);
Regardless, the source of your issue is that when it comes time to call PasswordSignInAsync, the value you're passing in for userName could be an empty string, which is not a valid value for that param. Wrapping this call in a conditional would fix the issue, such that you only run it if the user actually exists.

Related

Correct Implementation of Forgot Password AspNetBoilerPlate

Im using aspnetboilerplate (MVC) and wanted to implement a forgot password feature to allow the user to reset their own passwords using a link on the login screen.
I imagine this to work by generating a password reset code which is then emailed to the user.The user follows the link and is taken to a screen allowing them to reset the password.
Im stuck at the initial stage. i started with a copy of the login action after noticing that when attempting to log in the user object was returned. From here i attempt to set a password reset code.
[HttpPost]
[UnitOfWork]
public virtual async Task<JsonResult> ForgotPassword(ForgotPasswordViewModel forgotPasswordModel, string returnUrl = "", string returnUrlHash = "")
{
returnUrl = NormalizeReturnUrl(returnUrl);
if (!string.IsNullOrWhiteSpace(returnUrlHash))
{
returnUrl = returnUrl + returnUrlHash;
}
var loginResult = await _logInManager.LoginAsync(forgotPasswordModel.UsernameOrEmailAddress, "ForgotPassword", GetTenancyNameOrNull());
loginResult.User.SetNewPasswordResetCode();
switch (loginResult.Result)
{
case AbpLoginResultType.Success:
return Json(loginResult);
default:
throw _abpLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(loginResult.Result, forgotPasswordModel.UsernameOrEmailAddress, GetTenancyNameOrNull());
}
}
Checking the AbpUser table after the
loginResult.User.SetNewPasswordResetCode();
i cannot see any password reset code for the user, they are all null.
Could someone point me in the right direction.
Thanks in advance
Thanks to answer below for being correct, just for completion below is exactly what worked. Obviously ignore the json return at the end
public virtual async Task<JsonResult> ForgotPassword(ForgotPasswordViewModel forgotPasswordModel, string returnUrl = "", string returnUrlHash = "")
{
//var user = await GetUserByChecking(emailAddress);
var user = await _userManager.FindByEmailAsync(forgotPasswordModel.UsernameOrEmailAddress);
if (user == null)
{
throw new UserFriendlyException("User not found!");
}
user.SetNewPasswordResetCode();
//Send an email to user with the below password reset code
/* Uri.EscapeDataString(user.PasswordResetCode) */
return Json("");
}
public class AccountAppService: IAccountAppService
{
public UserManager UserManager {get; set; }
public async Task SendPasswordResetCode(string emailAddress)
{
var user = await UserManager.FindByEmailAsync(emailAddress);
if (user == null)
{
throw new UserFriendlyException("User not found!");
}
user.SetNewPasswordResetCode();
//Send an email to user with the below password reset code
/* Uri.EscapeDataString(user.PasswordResetCode) */
}
}

Closing an async Response

I'm trying to close the current response but nothing happens when I try HttpContext.Response.Body.Close() and Response.End() does not exist.
The reason I'm trying to achieve this is because of legacy validator functions that write an error and close the response, or at least stopping the parent WebAPI method.
Example:
private async Task Register_v2()
{
//Read JSON to object
UserRegisterRequest userRegisterRequest = Request.ReadBody().FromJson<UserRegisterRequest>();
//Validate object (legacy static method with a lot of logic)
//Validate() should end the response if object not validated
userRegisterRequest.Validate(isJson: true, isThrowHttpError: true);
//Code still reaches here and request does not close
string test = "hey I'm alive";
}
Can I workaround this with middleware somehow?
Thanks
There are two ways to terminate the Request pipeline.
Use app.Run in Startup.Configure
Do not invoke _next(context) in Middleware.InvokeAsync
For your scenario, you could try second option by determining whether to invoke _next(context).
public class FirstMiddleware
{
private readonly RequestDelegate _next;
public FirstMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
await context.Response.WriteAsync($"This is { GetType().Name }");
//decide whether to invoke line below based on your business logic
//await _next(context);
bool isValid = userRegisterRequest.Validate(isJson: true, isThrowHttpError: true);
//change userRegisterRequest.Validate to reutrn whether the model is valid
if(!isValid)
{
await context.Response.WriteAsync($"Model is not valid");
}
else
{
await _next(context);
}
}
}

invalid_grant of OAuthAuthorizationServerProvider

I'm working on writing fully customized ASP.NET Identity for my WebAPi.
I have rewritten my own derived OAuthAuthorizationServerProvider in this way:
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
// Check User availability ...
//ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
// if i couldn't found user in my DataBase ...
//if (user == null)
//{
//context.SetError("invalid_grant", "The user name or password is incorrect.");
// return;
//}
context.Validated();
}
}
GrantResourceOwnerCredentials just returns an invalid_grant error for each calls. i want to handle it but, i don't know how.
ValidateClientAuthentication is where you would do your authentication checks and this is where you throw errors if anything doesn't match.
move your code there and do the checks before you call context.Validated(). You only call the Validate method once you make sure everything is validated correctly.
here is an example of such an implementation I did a while back:
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId;
string clientSecret;
//first try to get the client details from the Authorization Basic header
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
//no details in the Authorization Header so try to find matching post values
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (string.IsNullOrWhiteSpace(clientId) || string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError("client_not_authorized", "invalid client details");
return Task.FromResult<object>(null);
}
var dataLayer = new RepoManager(new DataLayerDapper()).DataLayer;
var audienceDto = dataLayer.GetAudience(clientId);
if (audienceDto == null || !clientSecret.Equals(audienceDto.Secret))
{
context.SetError("unauthorized_client", "unauthorized client");
return Task.FromResult<object>(null);
}
context.Validated();
return Task.FromResult<object>(null);
}
Notice how the checks happen in order and certain errors are raised with some appropriate errors.
This code takes a client id and client secret from an authorization header but you can easily drop all that and replace it with your own checks and database calls.
The important part is that this is where you deal with stuff like this and this is where you set the errors so your clients know what's going on.
GrantResourceOwnerCredentials this is where you get once the call is properly authenticated, at which point you can start creating tokens, adding claims and creating the authentication ticket. This method does not get hit if the previous one fails to authenticate the request.
Here is a working example:
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
var identity = new ClaimsIdentity("JWT");
identity.AddClaim(new Claim("clientID", context.ClientId));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"audience", context.ClientId
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
return Task.FromResult<object>(null);
}
Now, if you get an invalid grant error that usually happens because you either didn't set up the grant_type in your initial call or you set up the wrong value.
in my case I had to setup this:
"grant_type", "password"

Why GrantRefreshToken method is not called - Oauth2 ASP.NET Web API

I send request to oauth2 server with request body :
grant_type=refresh_token&refresh_token=abc
I save refresh_token in database.
ReceiveAsync method :
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("clientAllowedOrigin");
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
string hashTokenName = Helper.GetHash(context.Token);
var refreshToken = await repo.FindRefreshToken(hashTokenName);
if (refreshToken != null)
{
//Get protectedTicket from refreshToken class
context.DeserializeTicket(refreshToken.ProtectedTicket);
var result = await repo.RemoveRefreshToken(hashTokenName);
}
}
GrantRefreshToken method :
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
var originClient = context.Ticket.Properties.Dictionary["client_id"];
var currenClient = context.ClientId;
if (originClient != currenClient)
{
context.SetError("Error");
return Task.FromResult<object>(null);
}
var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
newIdentity.AddClaim(new Claim(ClaimTypes.Name, context.ClientId));
var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
context.Validated(newTicket);
return Task.FromResult<object>(null);
}
Why GrantRefreshToken is not called when ReceiveAsync finished ?
I had been facing the same problem for two days.
the issue causes by ReceiveAsync method does not set OAuthGrantRefreshTokenContext context.ticket
properly.
to diagnose that, use postman to send the refresh_token request. an invalid_grant error will be prompted.
the minimum requirement for generate a ticket is to set IssuedUtc & ExpiresUtc and leave other properties to default.
the code below can simply replicate the issue.
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
AuthenticationTicket ticket = null;
var identity = new ClaimsIdentity();
var props = new AuthenticationProperties();
ticket = new AuthenticationTicket(identity, props);
context.SetTicket(ticket);
//context.Ticket.Properties.IssuedUtc = DateTime.UtcNow;
//context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow;
}
once IssuedUtc & ExpiresUtc are set, GrantRefreshToken will be
invoked.
solution: put a breakpoint by the end of ReceiveAsync method, check
whether context.ticket is set properly.
IssuedUtc & ExpiresUtc these two properties can not be null.
ReceiveAsync signature should be: public Task ReceiveAsync(AuthenticationTokenReceiveContext context)
And you should return a Task in that method.
I've faced the exact same problem, and the root cause in my case was the machine key, the engine use the machine key in serializing the ticket, so if you don't configure the machine key in web.config then one key could be used in the serialization process and another one in the deserialization.
So try to configure the machine key in the web.cofig. Hope this helps

How to get user context during Web Api calls?

I have an web front end calling an ASP Web Api 2 backend. Authentication is managed with ASP Identity. For some of the controllers I'm creating I need to know the user making the call. I don't want to have to create some weird model to pass in including the user's identity (which I don't even store in the client).
All calls to the API are authorized using a bearer token, my thought is the controller should be able to determine the user context based on this but I do not know how to implement. I have searched but I don't know what I'm searching for exactly and haven't found anything relevant. I'm going for something like...
public async Task<IHttpActionResult> Post(ApplicationIdentity identity, WalkthroughModel data)
Update
I found the below which looked very promising... but the value is always null! My controller inherits from ApiController and has an Authorize header.
var userid = User.Identity.GetUserId();
Update 2
I have also tried all of the solutions in Get the current user, within an ApiController action, without passing the userID as a parameter but none work. No matter what I am getting an Identity that is valid and auth'd, but has a null UserID
Update 3
Here's where I'm at now.
[Authorize]
[Route("Email")]
public async Task<IHttpActionResult> Get()
{
var testa = User.Identity.GetType();
var testb = User.Identity.GetUserId();
var testc = User.Identity.AuthenticationType;
var testd = User.Identity.IsAuthenticated;
return Ok();
}
testa = Name: ClaimsIdentity,
testb = null,
testc = Bearer,
testd = true
The user is obviously authenticated but I am unable to retrieve their userID.
Update 4
I found an answer, but I'm really unhappy with it...
ClaimsIdentity identity = (ClaimsIdentity)User.Identity;
string username = identity.Claims.First().Value;
That gets me the username without any db calls but it seems very janky and a pain to support in the future. Would love if anyone had a better answer.
What if I need to change what claims are issued down the road? Plus any time I actually need the user's id I have to make a db call to convert username to ID
A common approach is to create a base class for your ApiControllers and take advantage of the ApplicationUserManager to retrieve the information you need. With this approach, you can keep the logic for accessing the user's information in one location and reuse it across your controllers.
public class BaseApiController : ApiController
{
private ApplicationUser _member;
public ApplicationUserManager UserManager
{
get { return HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>(); }
}
public string UserIdentityId
{
get
{
var user = UserManager.FindByName(User.Identity.Name);
return user.Id;
}
}
public ApplicationUser UserRecord
{
get
{
if (_member != null)
{
return _member ;
}
_member = UserManager.FindByEmail(Thread.CurrentPrincipal.Identity.Name);
return _member ;
}
set { _member = value; }
}
}
I use a custom user authentication (I dont use AspIdentity because my existing user table fields was far different from IdentityUser properties) and create ClaimsIdentity passing my table UserID and UserName to validate my bearer token on API calls.
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
User user;
try
{
var scope = Autofac.Integration.Owin.OwinContextExtensions.GetAutofacLifetimeScope(context.OwinContext);
_service = scope.Resolve<IUserService>();
user = await _service.FindUserAsync(context.UserName);
if (user?.HashedPassword != Helpers.CustomPasswordHasher.GetHashedPassword(context.Password, user?.Salt))
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
catch (Exception ex)
{
context.SetError("invalid_grant", ex.Message);
return;
}
var properties = new Dictionary<string, string>()
{
{ ClaimTypes.NameIdentifier, user.UserID.ToString() },
{ ClaimTypes.Name, context.UserName }
};
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
properties.ToList().ForEach(c => identity.AddClaim(new Claim(c.Key, c.Value)));
var ticket = new AuthenticationTicket(identity, new AuthenticationProperties(properties));
context.Validated(ticket);
context.Request.Context.Authentication.SignIn(identity);
}
And how I use the ClaimsIdentity to retrieve my User table details on User ApiController Details call.
[HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)]
[Route("Details")]
public async Task<IHttpActionResult> Details()
{
var user = await _service.GetAsync(RequestContext.Principal.Identity.GetUserId<int>());
var basicDetails = Mapper.Map<User, BasicUserModel>(user);
return Ok(basicDetails);
}
Notice the
ClaimTypes.NameIdentifier = GetUserId() and ClaimTypes.Name = GetUserName()

Resources