예제 #1
0
        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);
        }
예제 #2
0
        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);
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
        }