async Task <int> DoVerifyAsync(ISqlCallContext ctx, SqlCommand hashReader, string password, object objectKey, bool actualLogin, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(password)) { return(0); } // 1 - Get the PwdHash and UserId. // hash is null if the user is not a UserPassword: we'll try to migrate it. byte[] hash = null; int userId = 0; using (await(hashReader.Connection = ctx[Database]).EnsureOpenAsync(cancellationToken).ConfigureAwait(false)) using (var r = await hashReader.ExecuteReaderAsync(System.Data.CommandBehavior.SingleRow, cancellationToken).ConfigureAwait(false)) { if (await r.ReadAsync(cancellationToken).ConfigureAwait(false)) { hash = r.GetSqlBytes(0).Buffer; userId = r.GetInt32(1); if (userId == 0) { return(0); } } } PasswordVerificationResult result = PasswordVerificationResult.Failed; PasswordHasher p = null; IUserPasswordMigrator migrator = null; // 2 - Handle external password migration or check the hash. if (hash == null) { migrator = _package.PasswordMigrator; if (migrator != null) { if (objectKey is int) { userId = (int)objectKey; } else { Debug.Assert(objectKey is string); userId = await _userTable.FindByNameAsync(ctx, (string)objectKey).ConfigureAwait(false); if (userId == 0) { return(0); } } if (migrator.VerifyPassword(ctx, userId, password)) { result = PasswordVerificationResult.SuccessRehashNeeded; p = new PasswordHasher(HashIterationCount); } } } else { p = new PasswordHasher(HashIterationCount); result = p.VerifyHashedPassword(hash, password); } // 3 - Handle result. if (result == PasswordVerificationResult.Failed) { return(0); } if (result == PasswordVerificationResult.SuccessRehashNeeded) { // 3.1 - If migration occurred, create the user with its password. // Else rehash the password and update the database. await CreateOrUpdatePasswordUserWithRawPwdHashAsync(ctx, 1, userId, p.HashPassword(password), CreateOrUpdateMode.CreateOrUpdate, cancellationToken).ConfigureAwait(false); if (migrator != null) { migrator.MigrationDone(ctx, userId); } } if (actualLogin) { await OnLoginAsync(ctx, userId).ConfigureAwait(false); } return(userId); }
int DoVerify(ISqlCallContext ctx, SqlCommand hashReader, string password, object objectKey, bool actualLogin) { if (string.IsNullOrEmpty(password)) { return(0); } // 1 - Get the PwdHash and UserId. // hash is null if the user is not a UserPassword: we'll try to migrate it. byte[] hash = null; int userId = 0; using ((hashReader.Connection = ctx[Database]).EnsureOpen()) using (var r = hashReader.ExecuteReader(System.Data.CommandBehavior.SingleRow)) { if (r.Read()) { hash = r.GetSqlBytes(0).Buffer; userId = r.GetInt32(1); if (userId == 0) { return(0); } } } // 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 (hash == null) { migrator = _package.PasswordMigrator; if (migrator != null) { if (objectKey is int) { userId = (int)objectKey; } else { Debug.Assert(objectKey is string); userId = _userTable.FindByName(ctx, (string)objectKey); if (userId == 0) { return(0); } } if (migrator.VerifyPassword(ctx, userId, password)) { result = PasswordVerificationResult.SuccessRehashNeeded; p = new PasswordHasher(HashIterationCount); } } } else { p = new PasswordHasher(HashIterationCount); result = p.VerifyHashedPassword(hash, password); } // 3 - Handle result. if (result == PasswordVerificationResult.Failed) { return(0); } if (result == PasswordVerificationResult.SuccessRehashNeeded) { // 3.1 - If migration occurred, create the user with its password. // Else rehash the password and update the database. CreateOrUpdatePasswordUserWithPwdRawHash(ctx, 1, userId, p.HashPassword(password), CreateOrUpdateMode.CreateOrUpdate); if (migrator != null) { migrator.MigrationDone(ctx, userId); } } // 4 - Side-effect of successful login. if (actualLogin) { OnLogin(ctx, userId); } return(userId); }
async Task <LoginResult> DoVerifyAsync(ISqlCallContext ctx, SqlCommand hashReader, string password, object objectKey, bool actualLogin, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(password)) { return(new LoginResult(KnownLoginFailureCode.InvalidCredentials)); } // 1 - Get the PwdHash, UserId and FailedAttemptCount. // 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 = await ctx[Database].ExecuteSingleRowAsync(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)); } 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 hashed password. // Otherwise, rehash the password and update the database. mode |= UCLMode.CreateOrUpdate; UCLResult r = await PasswordUserUCLAsync(ctx, 1, read.UserId, p.HashPassword(password), mode, null, cancellationToken).ConfigureAwait(false); 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 = await PasswordUserUCLAsync(ctx, 1, read.UserId, Array.Empty <byte>(), mode, (int)KnownLoginFailureCode.UnregisteredUser, cancellationToken); return(r.LoginResult); } // 4 - Challenges the database login checks. int?failureCode = null; if (result == PasswordVerificationResult.Failed) { failureCode = (int)KnownLoginFailureCode.InvalidCredentials; } return((await PasswordUserUCLAsync(ctx, 1, read.UserId, null, mode, failureCode, cancellationToken) .ConfigureAwait(false)).LoginResult); }
async Task <LoginResult> DoVerifyAsync(ISqlCallContext ctx, SqlCommand hashReader, string password, object objectKey, bool actualLogin, CancellationToken cancellationToken) { 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 = await ctx[Database].ExecuteSingleRowAsync(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)); } 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 hashed password. // Otherwise, rehash the password and update the database. mode |= UCLMode.CreateOrUpdate; UCLResult r = await PasswordUserUCLAsync(ctx, 1, read.UserId, p.HashPassword(password), mode, null, cancellationToken).ConfigureAwait(false); 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((await PasswordUserUCLAsync(ctx, 1, read.UserId, null, mode, failureCode, cancellationToken) .ConfigureAwait(false)).LoginResult); }