Reputation: 84
I have an API that is ASP.NET webapi2. Not .NET Core. Then I have a React SPA. I'm using Identity and Oauth2.
I'm implementing the auth system and the password reset flow has me a little stumped. The API will generate a token that is emailed to the client. The client then clicks the link to navigate somewhere.
It makes sense for the link to navigate to the client javascript app which then takes the parameters from the token and submits them to the API. The problem with this is that the client url has to be known by the API to be able to generate the link. I don't want the API to know anything about where client apps are located because that seems like dumb coupling.
Another option is the password reset link navigates directly to the API where it then redirects the user to a client app. That has the same problem that the API needs to know where the client is and it also has this nasty redirect hack.
Are there resources on this or suggestions on how this is supposed to work?
Thanks
Upvotes: 1
Views: 2607
Reputation: 6491
WebAPI does not exposes the required methods in the AccountController.
I use these 2 methods that I added to AccountController
[Route("ForgotPassword")]
public async Task<IHttpActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = await UserManager.FindByNameAsync(model.UserName) ?? await UserManager.FindByEmailAsync(model.UserName);
if (user == null)
{
return Ok("Ok");
}
if (user.Email == null)
{
throw new InvalidOperationException("Cannot send email. Email address not configured.");
}
var token = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
#if DEBUG
System.Diagnostics.Process.Start(
string.Format(
"http://localhost:4444/#/forgot-password-reset/{0}/{1}",
HttpUtility.UrlEncode(user.UserName),
HttpUtility.UrlEncode(token)
)
);
#endif
SendMailForgotPassword(user, token);
return Ok("Ok");
}
[Route("ForgotPasswordReset")]
public async Task<IHttpActionResult> ForgotPasswordReset(ForgotPasswordResetViewModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = await UserManager.FindByNameAsync(model.UserName) ?? await UserManager.FindByEmailAsync(model.UserName);
if (user == null)
{
return Ok("Ok");
}
var result = await UserManager.ResetPasswordAsync(user.Id, model.Token, model.NewPassword);
if (result.Succeeded)
{
return Ok("Ok");
}
throw new InvalidOperationException(string.Join("\r\n", result.Errors));
}
Upvotes: 2
Reputation: 118
I believe what you are looking for is the identity provider's "login" portal. Flow for a password reset is part of the identity provider, not your main application. So when your users click their password reset it sends them to the identity provider password reset page (this reset password link probably should not exist in your main app at all). Once it is reset, the user will login and be redirected to your app with a authorization token which you will exchange for a access token. The redirect would have to be configured for each application that wants to use your identity provider.
Upvotes: 1