LoginResult DoVerify(ISqlCallContext ctx, SqlCommand hashReader, string password, object objectKey, bool actualLogin) { if (string.IsNullOrEmpty(password)) { return(new LoginResult(KnownLoginFailureCode.InvalidCredentials)); } // 1 - Get the PwdHash and UserId. // hash is null if the user is not a UserPassword: we'll try to migrate it. var read = ctx[Database].ExecuteSingleRow(hashReader, row => row == null ? (0, null) : (UserId: row.GetInt32(1), PwdHash: row.IsDBNull(0) ? null : row.GetBytes(0))); if (read.UserId == 0) { return(new LoginResult(KnownLoginFailureCode.InvalidUserKey)); } // If hash is null here, it means that the user is not registered. PasswordVerificationResult result = PasswordVerificationResult.Failed; PasswordHasher p = null; IUserPasswordMigrator migrator = null; // 2 - Handle external password migration or check the hash. if (read.PwdHash == null) { migrator = _package.PasswordMigrator; if (migrator != null && migrator.VerifyPassword(ctx, read.UserId, password)) { result = PasswordVerificationResult.SuccessRehashNeeded; p = new PasswordHasher(HashIterationCount); } } else { p = new PasswordHasher(HashIterationCount); result = p.VerifyHashedPassword(read.PwdHash, password); } // 3 - Handle result. var mode = actualLogin ? UCLMode.WithActualLogin : UCLMode.WithCheckLogin; if (result == PasswordVerificationResult.SuccessRehashNeeded) { // 3.1 - If migration occurred, create the user with its password. // Else rehash the password and update the database. mode |= UCLMode.CreateOrUpdate; UCLResult r = PasswordUserUCL(ctx, 1, read.UserId, p.HashPassword(password), mode, null); if (r.OperationResult != UCResult.None && migrator != null) { migrator.MigrationDone(ctx, read.UserId); } return(r.LoginResult); } // 4 - Challenges the database login checks. int?failureCode = null; if (result == PasswordVerificationResult.Failed) { failureCode = (int)KnownLoginFailureCode.InvalidCredentials; } return(PasswordUserUCL(ctx, 1, read.UserId, null, mode, failureCode).LoginResult); }
LoginResult DoVerify(ISqlCallContext ctx, SqlCommand hashReader, string password, object objectKey, bool actualLogin) { if (string.IsNullOrEmpty(password)) { return(new LoginResult(KnownLoginFailureCode.InvalidCredentials)); } // 1 - Get the PwdHash and UserId. // hash is null if the user is not a UserPassword: we'll try to migrate it. // hash can be empty iif a previous attempt to migrate it has failed. var read = ctx[Database].ExecuteSingleRow(hashReader, row => row == null ? (0, null, -1) : (UserId: row.GetInt32(1), PwdHash: row.IsDBNull(0) ? null : row.GetBytes(0), FailedAttemptCount: row.IsDBNull(2) ? -1 : row.GetByte(2))); if (read.UserId == 0) { return(new LoginResult(KnownLoginFailureCode.InvalidUserKey)); } // If hash is null here, it means that the user is not registered. PasswordVerificationResult result = PasswordVerificationResult.Failed; PasswordHasher p = null; IUserPasswordMigrator migrator = null; // 2 - Handle external password migration or check the hash. if (read.PwdHash == null || read.PwdHash.Length == 0) { migrator = UserPasswordPackage.PasswordMigrator; if (migrator != null && migrator.VerifyPassword(ctx, read.UserId, password)) { result = PasswordVerificationResult.SuccessRehashNeeded; p = new PasswordHasher(HashIterationCount); } } else { p = new PasswordHasher(HashIterationCount); result = p.VerifyHashedPassword(read.PwdHash, password); } // 3 - Handle result. var mode = actualLogin ? UCLMode.WithActualLogin : UCLMode.WithCheckLogin; if (result == PasswordVerificationResult.SuccessRehashNeeded) { // 3.1 - If migration occurred, create the user with its password. // Otherwise, rehash the password and update the database. mode |= UCLMode.CreateOrUpdate; UCLResult r = PasswordUserUCL(ctx, 1, read.UserId, p.HashPassword(password), mode, null); if (r.OperationResult != UCResult.None && migrator != null) { migrator.MigrationDone(ctx, read.UserId); } return(r.LoginResult); } if (result == PasswordVerificationResult.Failed && migrator != null) { // 3.2 - Migration failed, create the user with an empty hash. // so that FailedAttemptCount (or other) can be handled. mode |= UCLMode.CreateOnly; mode &= ~UCLMode.UpdateOnly; // KnownLoginFailureCode is UnregisteredUser: the fact that we have a password migrator // (that failed to migrate) results in the user actually not registered in the UserPassword provider. UCLResult r = PasswordUserUCL(ctx, 1, read.UserId, Array.Empty <byte>(), mode, (int)KnownLoginFailureCode.UnregisteredUser); return(r.LoginResult); } // 4 - Challenges the database login checks. int?failureCode = null; if (result == PasswordVerificationResult.Failed) { failureCode = (int)KnownLoginFailureCode.InvalidCredentials; } return(PasswordUserUCL(ctx, 1, read.UserId, null, mode, failureCode).LoginResult); }