Queue password checks and shorten busy retries (Refs #95)
CI / checks (push) Successful in 4m36s
CI / checks (push) Successful in 4m36s
This commit is contained in:
@@ -193,15 +193,44 @@ describe('password storage and verification', () => {
|
||||
expect(await verifyPassword('a'.repeat(129), passwordHash)).toBe(false);
|
||||
});
|
||||
|
||||
it('bounds concurrent scrypt work and releases capacity when hashes finish', async () => {
|
||||
const { hashPassword } = await import('../../src/lib/server/auth/passwords.js');
|
||||
const first = hashPassword(password),
|
||||
second = hashPassword(replacement);
|
||||
await expect(hashPassword('third sufficiently long passphrase')).rejects.toThrow('busy');
|
||||
await expect(Promise.all([first, second])).resolves.toHaveLength(2);
|
||||
await expect(hashPassword('subsequent sufficiently long passphrase')).resolves.toMatch(
|
||||
/^scrypt\$/
|
||||
it('allows three simultaneous logins to complete with production-strength scrypt', async () => {
|
||||
const users = [seedUser('first'), seedUser('second'), seedUser('third')];
|
||||
const results = await Promise.all(
|
||||
users.map((user, index) =>
|
||||
auth.authenticatePassword(
|
||||
{ username: user.username, password },
|
||||
`192.0.2.${index + 1}`
|
||||
)
|
||||
)
|
||||
);
|
||||
expect(results.map((result) => result.user.id)).toEqual(users.map((user) => user.id));
|
||||
for (const result of results)
|
||||
expect(auth.readSession(result.session.token)?.user.id).toBe(result.user.id);
|
||||
});
|
||||
|
||||
it('uses a short retry for password capacity while preserving the rate-limit retry', async () => {
|
||||
const { PasswordServiceBusyError } = await import('../../src/lib/server/auth/passwords.js');
|
||||
const { authError } = await import('../../src/lib/server/auth/http.js');
|
||||
const busy = authError(new PasswordServiceBusyError());
|
||||
expect(busy.status).toBe(429);
|
||||
expect(busy.headers.get('Retry-After')).toBe('2');
|
||||
expect(busy.headers.get('Cache-Control')).toBe('no-store');
|
||||
expect(await busy.json()).toEqual({
|
||||
error: 'Password service is busy. Try again shortly.'
|
||||
});
|
||||
|
||||
const limited = authError(
|
||||
new auth.AuthError('Too many attempts. Try again in 15 minutes.', 429)
|
||||
);
|
||||
expect(limited.status).toBe(429);
|
||||
expect(limited.headers.get('Retry-After')).toBe('900');
|
||||
|
||||
const unexpected = authError(new Error('Database is busy with private details'));
|
||||
expect(unexpected.status).toBe(500);
|
||||
expect(unexpected.headers.has('Retry-After')).toBe(false);
|
||||
expect(await unexpected.json()).toEqual({
|
||||
error: 'The authentication request could not be completed.'
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the same login error for unknown, disabled and incorrect-password accounts', async () => {
|
||||
|
||||
Reference in New Issue
Block a user