brockallen / BrockAllen.MembershipReboot

MembershipReboot is a user identity management and authentication library.
Other
742 stars 238 forks source link

Unable to make Password Change example work #624

Closed imonthercks closed 7 years ago

imonthercks commented 8 years ago

I have been struggling to implement a password expiration and change scenario work with Membership Reboot. I am using Membership Reboot in an MVC app relying on the MembershipReboot.Owin implementation. I am also using RavenDB as a back-end store.

I have based my current setup on the SingleTenantOwinSystemWeb example, and can see the AuthenticateWithUsernameOrEmail succeed. In the SignIn call of the AuthorizationService I can see that it is issuing a Partial SignIn Token for an expired user, and I see an .AspNet.MembershipReboot.2fa cookie being created, however when the GET Action for the Change Password screen is hit, and it checks if User.HasUserID(), none of the identity/claims information that it issued on the Partial SignIn Token is present and HasUserID returns false.

I am trying to understand where it would be loading the cookie in order to retrieve the UserID so that I can track down the issue. Any insight into where I should be looking would be much appreciated.

On additional comment... I did modify the PasswordChanged date manually in RavenDB in order to quickly test the expiration of a user account. It did not appear to me that this would be an issue, but wanted to mention in case this had some bearing.

brockallen commented 8 years ago

I'm a bit confused (but I've also not looked at the sample in a long time)... the 2fa cookie is if you're doing, well, 2 factor auth, and it's not really related to password reset. It's a temp cookie to remember who you are while you visit the 2fa page.

Password reset is just a flag that needs to be checked and force the user to change their password -- that's after they've logged in properly.

imonthercks commented 8 years ago

Thanks for the response. I will take a closer look at my code.

Is the intent that a temporary cookie be created in the case of authenticating with an expired password so that the identity of the user is available for the HasUserID extension method in the context of another request to the actual "Change Password" screen?

Tracing through the MR code, it appears that when the password is expired, it is calling IssuePartialSignInToken from the AuthenticationService.SignIn method...

        private void IssuePartialSignInToken(TAccount account, string method)
        {
            if (account == null) throw new ArgumentNullException("account");

            Tracing.Verbose("[AuthenticationService.IssuePartialSignInCookieForTwoFactorAuth] Account ID: {0}", account.ID);

            var claims = GetBasicClaims(account, method).Union(GetPendingAuthClaims(account));

            var ci = new ClaimsIdentity(claims); // no auth type param so user will not be actually authenticated
            var cp = new ClaimsPrincipal(ci);

            IssueToken(cp, MembershipRebootConstants.AuthenticationService.TwoFactorAuthTokenLifetime, false);
        }

I would assume that this token is what the should contain the user's identifier to make HasUserId() return true.

For now, I have worked around by passing the userid to the Change Password screen, and then re-authenticating them before actually changing the password.

Thanks for your help.

brockallen commented 8 years ago

If you want a temp cookie (like 2fa is doing) then notice how the 2fa cookie MW is configured to be passive -- this means you must explicitly call Authenticate in the code to have the cookie read. You don't automatically get the user populated.

sevenzsu commented 8 years ago

Having the same problem here. I have a method to create a user with temporary password(expired password) to force the user to change it the first time they login. IssuePartialSignInToken won't populate the user object and hence User.HasUserID() will failed in changepasswordcontroller. What is the best solution here? Could you make the IssuePartialSignInToken public virtual method like IssueToken so we can override it to authenticate the user?