Search code examples
asp.net.netvb.netasp.net-identitychange-password

ASP.NET Identity Changing Password Signs User Out


I have an issue with the ASP.NET Identity provider. I started with the default template that comes with Visual Studio 2019, targeting .NET 4.7.2 WebForms.

On the change password page, the Identity provider doesn't seem to be updating the .AspNet.ApplicationCookie to reflect the new SecurityStamp. I can see in Fiddler that ASP.NET is sending a 302 to redirect back to the Manage.aspx page and it is sending an auth cookie, that presumably should reflect the new SecurityStamp that results from the password change, except that it doesn't. So the user is immediately logged out of the application after a password or 2FA change, since I have validateInterval set to TimeSpan.Zero. If I set it to another value, the user's cookie becomes invalid after that value, regardless of the actual age the cookie is supposed to have.

app.UseCookieAuthentication(New CookieAuthenticationOptions() With {
            .AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            .ExpireTimeSpan = TimeSpan.FromDays(14),
            .SlidingExpiration = True,
            .Provider = New CookieAuthenticationProvider() With {
                .OnValidateIdentity = SecurityStampValidator.OnValidateIdentity(Of ApplicationUserManager, ApplicationUser)(
                    validateInterval:=TimeSpan.Zero,
                    regenerateIdentity:=Function(manager, user) user.GenerateUserIdentityAsync(manager))},
            .LoginPath = New PathString("/Account/Login")})

And here is the relevant code in the change password page:

Protected Sub ChangePassword_Click(sender As Object, e As EventArgs)
    If IsValid Then
        Dim manager = Context.GetOwinContext().GetUserManager(Of ApplicationUserManager)()
        Dim signInManager = Context.GetOwinContext().Get(Of ApplicationSignInManager)()
        Dim result As IdentityResult = manager.ChangePassword(User.Identity.GetUserId(), CurrentPassword.Text, NewPassword.Text)
        If result.Succeeded Then
            Dim userInfo = manager.FindById(User.Identity.GetUserId())
            signInManager.SignIn(userInfo, isPersistent:=False, rememberBrowser:=False)
            Response.Redirect("~/Profile/Manage?m=ChangePwdSuccess")
        Else
            AddErrors(result)
        End If
    End If
End Sub

Solution

  • Turns out you need to log the user out and then log them back in:

    Protected Sub ChangePassword_Click(sender As Object, e As EventArgs)
        If IsValid Then
            Dim userManager = Context.GetOwinContext().GetUserManager(Of ApplicationUserManager)()
            Dim signInManager = Context.GetOwinContext().Get(Of ApplicationSignInManager)()
            Dim authenticateResult = Context.GetOwinContext().Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie).GetAwaiter.GetResult()
            Dim accountChangeResult As IdentityResult = userManager.ChangePassword(User.Identity.GetUserId(), CurrentPassword.Text, NewPassword.Text)
            If accountChangeResult.Succeeded Then
                Context.GetOwinContext().Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie)
                Dim userInfo = userManager.FindById(User.Identity.GetUserId())
                signInManager.SignIn(userInfo, isPersistent:=authenticateResult.Properties.IsPersistent, rememberBrowser:=False)
                Response.Redirect("~/Profile/Manage?m=ChangePwdSuccess")
            Else
                AddErrors(accountChangeResult)
            End If
        End If
    End Sub