I would like to manually validate a password reset token in ASP.NET Identity 2.0. I'm trying to create my own version of UserManager.ResetPasswordAsync(string userId, string token, string newPassword) that takes and IdentityUser instead of userId like this:
UserManager.ResetPasswordAsync(IdentityUser user, string token, string newPassword)
Not sure if I am doing this right, but here I am attempting to validate the code that was emailed to the user in an earlier step. I have not modified the code/token that sends the email to the user and generates the code. I am assuming this is the correct method to call, but the purpose argument is incorrect. (I tried passing "ASP.NET Identity" but no dice.)
if (await userManager.UserTokenProvider.ValidateAsync(purpose: "?", token: code, manager: userManager, user: user))
{
return IdentityResult.Success;
}
else
{
return new IdentityResult("Invalid code.");
}
If someone could fill me in on the details of how it works out of the box, or point me at Microsoft's source code for UserManager.ResetPasswordAsync(IdentityUser user, string token, string newPassword) that would be most appreciated!
I overcame my problem by setting the purpose to "ResetPassword".
Below is a snippet of the final result in case someone wants to do something similar. It is a method in my ApplicationUserManager class. Realize, though, that some of the exception handling that Microsoft implements is missing or not localized because certain private variables, methods, and resources used in their code are inaccessible. It's unfortunate they did not make that stuff protected so that I could have gotten at it. The missing ThrowIfDisposed method call in particular is interesting (and bazaar) to me. Apparently they are anticipating method calls after an instance has been disposed in order to provide a friendlier error message and avoid the unexpected.
public async Task<IdentityResult> ResetPasswordAsync(IdentityUser user,
string token, string newPassword)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
// Make sure the token is valid and the stamp matches.
if (!await UserTokenProvider.ValidateAsync("ResetPassword", token,
this, user))
{
return IdentityResult.Failed("Invalid token.");
}
// Make sure the new password is valid.
var result = await PasswordValidator.ValidateAsync(newPassword)
.ConfigureAwait(false);
if (!result.Succeeded)
{
return result;
}
// Update the password hash and invalidate the current security stamp.
user.PasswordHash = PasswordHasher.HashPassword(newPassword);
user.SecurityStamp = Guid.NewGuid().ToString();
// Save the user and return the outcome.
return await UpdateAsync(user).ConfigureAwait(false);
}
It appears that the code for Microsoft.AspNet.Identity has not been Open Sourced according to the Codeplex repository located at:
https://aspnetidentity.codeplex.com/SourceControl/latest#Readme.markdown
At present, the ASP.NET Identity framework code is not public and
therefore will not be published on this site. However, we are planning
to change that, and as soon as we are able, the code will be published
in this repository.
However I did find this which might be the source for the UserManager based on the debug symbols:
UserManager Source Code
I also found these posts which might help:
Implementing custom password policy using ASP.NET Identity
UserManager Class Documentation
IUserTokenProvider Interface Documentation
Related
I'm learning asp.net core and I'm stuck. I'll try to explain somehow.
Problem is with user claim.
When I log in into website, user have, let's say Create and Delete Claim and all working.
But when THAT user changes that HE cannot Delete something, after updating database HE still can.
In database Delete is gone (which is good).
Authorization is checked by authorization attribute:
[Authorize(Policy="DeletePolicy")]
I found something what may help.
ClaimsPrincipal User
property still have both claims (Create and Delete), but when I check DB:
await userManager.GetClaimsAsync("user_id")
I get only Create Claim which is good.
My question is: what precisely is that ClaimsPrincipal User property and why is not updated automatically?
Do I need to update User's Claims manually?
Claims are embedded in authentication cookie. It's like a snapshot of user's claims at a moment when user was signed in. Instead of putting all claims into cookie you can create ExtraClaimsMiddleware and put it after authentication middleware:
public class ExtraClaimsMiddleware
{
private readonly RequestDelegate _next;
public ExtraClaimsMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context,
YourDb db)
{
//get additional claims for the current user from database, cache it if you want
var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
var extraClaims = await db.GetExtraClaimsAsync(userId);
foreach (var claim in extraClaims)
{
context.User.Identities.First().AddClaim(new Claim(claim.ClaimType, claim.ClaimValue));
}
await _next(context);
}
}
public static class ExtraClaimsMiddlewareExtensions
{
public static IApplicationBuilder UseExtraClaims(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ExtraClaimsMiddleware>();
}
}
Then in Startup.cs:
app.UseAuthentication();
app.UseExtraClaims();
I am not sure if I understand your problem correctly but I guess it can be solved with the SignInManager Class.
After you deleted the claim from the database you should be able to use the “RefreshSignInAsync”-Method to update the user´s cookie.
I'm setting up my own OAuth2 server. So far, I have succesfully implemented GrantResourceOwnerCredentials in my implementation of OAuthAuthorizationServerProvider. Now, because I am developing an app for our business, I want to implement the OAuth2 Authorization Code grant.
I have tried to follow directions here https://learn.microsoft.com/en-us/aspnet/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server but in my implementation, I have not found how to reach the Create call of the AuthorizationCodeProvider (which I set in OAuthAuthorizationServerOptions).
I have briefly checked whether accessing the TokenEndpointPath with a (wrong) code parameter works, and in the debugger I see that my AuthorizationCodeProvider's Receive call is hit. Of course there is no success because the code I send is 'sometestcode' instead of a real one, but the code is hit so that means I'm on the right path.
Here's what I have so far:
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (OAuthRepository.GetClient(context.ClientId) != null)
{
var expectedRootUri = new Uri(context.Request.Uri, "/");
if (context.RedirectUri.StartsWith(expectedRootUri.AbsoluteUri))
{
context.Validated();
return Task.FromResult<object>(null);
}
}
context.Rejected();
return Task.FromResult<object>(null);
}
public override Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
// I know this is wrong but it's just a start and not the focus of this SO question.
context.Response.Redirect(context.AuthorizeRequest.RedirectUri);
context.RequestCompleted();
return Task.FromResult<object>(null);
}
public override Task GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context)
{
// Needs additional checks, not the focus of my question either
var newTicket = new AuthenticationTicket(context.Ticket.Identity, context.Ticket.Properties);
context.Validated(newTicket);
return Task.FromResult<object>(null);
}
Now, when I call my AuthorizeEndpointPath with a redirect_uri, I am sent to that Uri immediately. I know this is wrong: I should be sent to a separate login page. I'll fix my Web API later to redirect to the correct Uri.
The focus of my question is this: I am now in the process of implementing the login page, but I do not know how to get the authorization code from my WebAPI after the user has logged in. (I'm skipping the consent part for now and assume that if the user is logged in they're okay with it, I'll add giving consent later.)
I am basing my flow on the diagram shared here https://docs.apigee.com/api-platform/security/oauth/oauth-v2-policy-authorization-code-grant-type
I am using Thinktecture IdentityModel to create the login page in an MVC Controller. Now I need to retrieve the authorization code from the Web API in my MVC Controller. And after that I can then redirect the user back to the original client (app) that requested the Authorization Code flow.
To obtain the authorization code from my Web API, I see three methods in Thinktecture's OAuth2Client:
CreateAuthorizeUrl
CreateCodeFlowUrl
RequestAuthorizationCodeAsync
Neither seem to do what I want. How do I proceed so that my WebAPI is called to generate the code?
[HttpGet]
[ImportModelStateFromTempData]
public ActionResult Authorize(string clientId, string returnUrl, string responseType)
{
AuthorizeViewModel viewModel = new AuthorizeViewModel();
...
...
...
return View(viewModel);
}
[HttpPost]
[ExportModelStateToTempData]
public async Task<ActionResult> Authorize(AuthorizeViewModel viewModel)
{
// NOTE: This is in MVC and is postback from *.cshtml View.
OAuth2Client.?????? // <=== How to obtain authorization code from WebAPI?
...
return Redirect(returnUrl);
}
I think I have it correctly setup on the Web API side. I just don't know how to hit the Create part of the flow. I hope someone can help me understand what I am not seeing. I have a blind spot somewhere I think...
How do I have OAuth2Client get me the authorization code from my WebAPI?
I am also using Postman to test my Web API. If anyone can help me get the URL in Web API 2.0 that returns an authorization code, I would also accept that as an answer. Then I can write the code in MVC myself.
Edit
Okay, so I think I found a part of my blind spot. Firstly, I marked `AuthorizeEndpoint' as "not the focus of this SO question", but that was a big mistake.
When I adapt the AuthorizeEndpoint like so:
public override Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
System.Security.Claims.ClaimsIdentity ci = new System.Security.Claims.ClaimsIdentity("Bearer");
context.OwinContext.Authentication.SignIn(ci);
context.RequestCompleted();
return Task.FromResult<object>(null);
}
And if I adapt my implementation of AuthorizationCodeProvider.Create like so:
public void Create(AuthenticationTokenCreateContext context)
{
context.Ticket.Properties.IssuedUtc = DateTime.UtcNow;
context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddSeconds(60);
// Some random Guid
context.SetToken(Guid.NewGuid().ToString("n"));
}
Any call to /authorize is redirected to redirect_uri with a query parameter code=<THE_RANDOM_GUID>! :D
Obviously, this implementation is not where it should be, so my question is not yet resolved. Remaining issues:
Right now, anybody can request an authorization code, the client_id is ignored. ValidateClientAuthentication is apparently not hit as part of AuthorizeEndpoint. How do I obtain ClientId in AuthorizeEndpoint?
The authorization code is not coupled to a client. Anyone who intercepts the code could use it. How do I obtain the ClientId in AuthorizationCodeProvider.Create so that I can store it with the code?
The authorization code is not coupled to a user at all, it's an empty ClaimsIdentity. How do I put a user-login page in between and in AuthorizeEndpoint obtain the ClaimsIdentity for the logged-in user?
So, after quite some searching online, I got some success by searching github. Apparently, OAuthAuthorizationServerProvider offers AuthorizeEndpoint and that method should be used for both "Hey, you're not authorized, go log in you!" as well as for "Ahh, okay you're cool, here's an authorization code.". I had expected that OAuthAuthorizationServerProvider would have two separate methods for that, but it doesn't. That explains why on github, I find some projects that implement AuthorizeEndpoint in a rather peculiar way. I've adopted this. Here's an example:
public override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
if (context.Request.User != null && context.Request.User.Identity.IsAuthenticated)
{
var redirectUri = context.Request.Query["redirect_uri"];
var clientId = context.Request.Query["client_id"];
var authorizeCodeContext = new AuthenticationTokenCreateContext(
context.OwinContext,
context.Options.AuthorizationCodeFormat,
new AuthenticationTicket(
(ClaimsIdentity)context.Request.User.Identity,
new AuthenticationProperties(new Dictionary<string, string>
{
{"client_id", clientId},
{"redirect_uri", redirectUri}
})
{
IssuedUtc = DateTimeOffset.UtcNow,
ExpiresUtc = DateTimeOffset.UtcNow.Add(context.Options.AuthorizationCodeExpireTimeSpan)
}));
await context.Options.AuthorizationCodeProvider.CreateAsync(authorizeCodeContext);
context.Response.Redirect(redirectUri + "?code=" + Uri.EscapeDataString(authorizeCodeContext.Token));
}
else
{
context.Response.Redirect("/account/login?returnUrl=" + Uri.EscapeDataString(context.Request.Uri.ToString()));
}
context.RequestCompleted();
}
Source: https://github.com/wj60387/WebApiOAUthBase/blob/master/OwinWebApiBase/WebApiOwinBase/Providers/OAuthServerProvider.cs
As for my remaining three questions:
Right now, anybody can request an authorization code, the client_id is ignored. ValidateClientAuthentication is apparently not hit as part of AuthorizeEndpoint. How do I obtain ClientId in AuthorizeEndpoint?
Answer: You have to implement `ValidateClientAuthentication'.
The authorization code is not coupled to a client. Anyone who intercepts the code could use it. How do I obtain the ClientId in AuthorizationCodeProvider.Create so that I can store it with the code?
Answer: OAuthAuthorizationServerProvider takes care of this. As long as you set "client_id" in the ticket, it will check that the client that requests an access token for the authorization code is the same.
The authorization code is not coupled to a user at all, it's an empty ClaimsIdentity. How do I put a user-login page in between and in AuthorizeEndpoint obtain the ClaimsIdentity for the logged-in user?
Answer: You create a separate login page. What this does is sign the user in. If your WebAPI uses cookie-based authentication, you can just redirect the user to the AuthorizeEndpoint again. If you use access tokens, your login page has to make a request to `AuthorizeEndpoint' with the access token to obtain an authorization code. (Don't give the access token to the third party. Your login page requests the authorization code and sends that back.) In other words, if you use access tokens then there are two clients involved in this flow.
I'm making my own system to authenticate jwt tokens in certain scenarios.
When I have properly validated the token, I have
var userIdentity = await user.CreateIdentityAsync(DefaultAuthenticationTypes.ExternalBearer);
owinContext.Authentication.User = new System.Security.Claims.ClaimsPrincipal(userIdentity);
owinContext.Authentication.SignIn(userIdentity);
System.Web.HttpContext.Current.User = owinContext.Authentication.User;
await next()
but that doesn't seem to fix authentication which still fails at - I believe - the Asp.Net Mvc level. Because I know it uses HttpContext I try adding this before calling next()
HttpContext.Current.User = new GenericPrincipal(userIdentity, new string[0]);
This gets me further along but I still seem to be getting an an authorization error it would seem (by searching source for the message that I get and where its used) to be coming from the Web Api [Authorize] attribute.
I'm hitting a wall as far as tracing through the .net source code. The only way I should be getting this message is if IsAuthorized returns false. But there are no roles nor users specified (it's just plain [Authorize]) and before heading off to the next() I can stop the debugger and check that yes there is a user identity, and yes it IsAuthorized.
I've overridden the AuthorizeAttribute so as to place breakpoints and can see that by the time it is called however, my actionContext is associated with a completely different identity with IsAuthorized == false. Which in turn makes me wonder if I'm signing in the user identity wrong
So... am I doing this correctly? What should I be doing?
I have never undertstood why but in my case, i have need to valid the ticket after signing in:
var userIdentity = await user.CreateIdentityAsync(DefaultAuthenticationTypes.ExternalBearer);
ctx.Authentication.SignIn(userIdentity);
AuthenticationTicket ticket = new AuthenticationTicket(userIdentity, null);
ctx.Validated(ticket);
Edit
I'm not really in the same context. In my case, I have a custom authentication provider inheriting of Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationProvider :
public class CustomBearerAuthenticationProvider:OAuthBearerAuthenticationProvider
{
public CustomBearerAuthenticationProvider() : base()
{
this.OnValidateIdentity = (context) => Task.Run(() =>
{
var identity = this.CreateApplicationIdentity(user);
context.OwinContext.Authentication.SignIn(identity);
AuthenticationTicket ticket = new AuthenticationTicket(identity, null);
context.Validated(ticket);
});
}
}
context is of type : Microsoft.Owin.Security.OAuth.OAuthValidateIdentityContext
MVC application using AspNet.Identity
I've set my application up so when someone registers an account they are sent a verification email with a token to confirm the account.
I have had this working previously in a different application so am not sure why this isn't working.
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
try
{
var provider = new DpapiDataProtectionProvider("SOMETHING");
UserManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(provider.Create("EmailConfirmation"));
result = await UserManager.ConfirmEmailAsync(userId, code);
}
Now when i breakpoint my code, it is giving me the error when i get to ConfirmEmailAsync, i am passing ina userId and Code, but for some reason it is wanting me to implement IUserEmailStore.
I haven't had to do this previously, and the above code has worked fine.
For anyone else having this issue. It was because my Microsoft.AspNet.Identity.EntityFramework was on version 1.
Its partly(read all) my fault as I assumed when I updated the identity it would update all of the identity references, apparently not.
I have implemented the ForgotPassword (with token reset) into my MVC 5 application. We are in production. Although this works in majority of the cases, many of our end-users are of older age and get confused when they cannot login and need a reset. So in those situations, I am considering giving one of our admin staff the ability to reset a user's password and giving them the new password on the phone. The data is not that sensitive.
I tried this:
public ActionResult ResetPassword()
{ UserManager<IdentityUser> userManager =
new UserManager<IdentityUser>(new UserStore<IdentityUser>());
var user = userManager.FindByEmail("useremail.samplecom");
userManager.RemovePassword(user.Id);
userManager.AddPassword(user.Id, "newpassword");
}
I get a cryptic error stating Invalid Column EMail, Invalid Column Email Confirmed ......
I also tried the userManager.ResetPassword(), but abandoned that idea because it needs a token reset. I want to bypass it.
What am I not seeing?
Thanks in advance.
I also tried the userManager.ResetPassword(), but abandoned that idea because it needs a token reset. I want to bypass it.
How about you just generate the token and pass it to the Reset routine ?
var userManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
var code = await userManager.GeneratePasswordResetTokenAsync("username");
var result = await userManager.ResetPasswordAsync("username", code, "your new password");
if (!result.Succeeded)
{
//password does not meet standards
}
The idea here is you are just emulating/bypassing the usual routine of sending the token to the client (via email) and having the link that they click on call ResetPasswordAsync
I'm not completely sure if this will work in your implementation but I use the following code with success in a use case which has basically the same requirements as yours. The difference is that I'm not letting any user reset it's own password. This is always the task of an admin.
I'm bypassing the ApplicationUserManager and edit the information directly in the table, using just Entity Framework.
// I created an extension method to load the user from the context
// you will load it differently, but just for completeness
var user = db.LoadUser(id);
// some implementation of random password generator
var password = General.Hashing.GenerateRandomPassword();
var passwordHasher = new Microsoft.AspNet.Identity.PasswordHasher();
user.PasswordHash = passwordHasher.HashPassword(password);
db.SaveChanges();
You have to get the user from the database and generate the code not by username :
public async Task<Unit> ResetPassword(string userName, string password)
{
if (!string.IsNullOrWhiteSpace(userName))
{
var returnUser = await _userManager.Users.Where(x => x.UserName == userName).FirstOrDefaultAsync();
var code = await _userManager.GeneratePasswordResetTokenAsync(returnUser);
if (returnUser != null)
await _userManager.ResetPasswordAsync(returnUser, code, password);
}
return Unit.Value;
}