69 lines
2.4 KiB
TypeScript
69 lines
2.4 KiB
TypeScript
import { AuthService } from '../src/identity/auth.service';
|
|
import { AuthStore } from '../src/identity/auth.store';
|
|
import { PasswordService } from '../src/identity/password.service';
|
|
import { RateLimitService } from '../src/identity/rate-limit.service';
|
|
import { parseEnvironment } from '../src/config/environment';
|
|
|
|
describe('login policy', () => {
|
|
const store = {
|
|
findUser: jest.fn(),
|
|
createSession: jest.fn(),
|
|
failedLogin: jest.fn(),
|
|
};
|
|
const passwords = { verify: jest.fn(), dummyVerify: jest.fn() };
|
|
const limits = { consume: jest.fn() };
|
|
const service = new AuthService(
|
|
store as unknown as AuthStore,
|
|
passwords as unknown as PasswordService,
|
|
limits as unknown as RateLimitService,
|
|
parseEnvironment({ DATABASE_URL: 'postgresql://localhost/mani' }),
|
|
);
|
|
const input = {
|
|
organizationId: 'organization',
|
|
email: 'user@example.com',
|
|
password: 'password',
|
|
};
|
|
beforeEach(() => {
|
|
jest.resetAllMocks();
|
|
store.findUser.mockResolvedValue({
|
|
id: 'user',
|
|
organizationId: 'organization',
|
|
status: 'ACTIVE',
|
|
passwordHash: 'hash',
|
|
});
|
|
passwords.verify.mockResolvedValue(true);
|
|
});
|
|
it('passes only the token digest to persistence', async () => {
|
|
const result = await service.login(input);
|
|
expect(store.createSession).toHaveBeenCalledWith(
|
|
'user',
|
|
'hash',
|
|
expect.stringMatching(/^[a-f0-9]{64}$/),
|
|
expect.any(Date),
|
|
);
|
|
expect(JSON.stringify(store.createSession.mock.calls)).not.toContain(
|
|
result.accessToken,
|
|
);
|
|
});
|
|
it('does not create sessions for unapproved users', async () => {
|
|
store.findUser.mockResolvedValue({
|
|
id: 'user',
|
|
status: 'PENDING',
|
|
organizationId: 'organization',
|
|
passwordHash: 'hash',
|
|
});
|
|
await expect(service.login(input)).rejects.toThrow('Invalid credentials');
|
|
expect(store.createSession).not.toHaveBeenCalled();
|
|
});
|
|
it('does equivalent hashing work for unknown users', async () => {
|
|
store.findUser.mockResolvedValue(null);
|
|
await expect(service.login(input)).rejects.toThrow('Invalid credentials');
|
|
expect(passwords.dummyVerify).toHaveBeenCalledWith(input.password);
|
|
});
|
|
it('stops before querying users when throttled', async () => {
|
|
limits.consume.mockRejectedValue(new Error('limited'));
|
|
await expect(service.login(input)).rejects.toThrow('limited');
|
|
expect(store.findUser).not.toHaveBeenCalled();
|
|
});
|
|
});
|