import { identityApp, type IdentityApp } from './helpers/identity-app'; import { AuthStore } from '../src/identity/auth.store'; import { RecoveryStore } from '../src/identity/recovery.store'; import { AccessStore } from '../src/identity/access.store'; import { RateLimitService } from '../src/identity/rate-limit.service'; import { SessionStore } from '../src/identity/session.store'; import { issueToken, hashToken } from '../src/identity/tokens'; describe('identity transactional failure paths', () => { let ctx: IdentityApp; beforeAll(async () => { ctx = await identityApp(); }, 60000); afterAll(async () => { await ctx?.close(); }); it('does not issue a session using an obsolete password hash', async () => { const issued = issueToken(); await expect( ctx.app .get(AuthStore) .createSession( ctx.owner.userId, 'outdated', issued.tokenHash, new Date(Date.now() + 60000), ), ).rejects.toThrow(); expect( await ctx.db.session.findUnique({ where: { tokenHash: issued.tokenHash }, }), ).toBeNull(); }); it('rejects a session whose account is no longer active even without explicit revocation', async () => { await ctx.db.user.update({ where: { id: ctx.owner.userId }, data: { status: 'SUSPENDED' }, }); await expect( ctx.app.get(SessionStore).authenticate(ctx.token), ).rejects.toThrow(); await ctx.db.user.update({ where: { id: ctx.owner.userId }, data: { status: 'ACTIVE' }, }); }); it('rejects expired recovery tokens without changing credentials', async () => { const issued = issueToken(); await ctx.app .get(RecoveryStore) .create(ctx.owner.userId, issued.tokenHash, new Date(0)); await expect( ctx.app.get(RecoveryStore).reset(issued.tokenHash, 'new-hash'), ).rejects.toThrow('Invalid or expired'); expect((await ctx.login()).status).toBe(200); }); it('rechecks account eligibility when storing and consuming recovery tokens', async () => { const issued = issueToken(); await ctx.app .get(RecoveryStore) .create(ctx.owner.userId, issued.tokenHash, new Date(Date.now() + 60000)); await ctx.db.user.update({ where: { id: ctx.owner.userId }, data: { status: 'SUSPENDED' }, }); expect( await ctx.app .get(RecoveryStore) .create(ctx.owner.userId, issueToken().tokenHash, new Date()), ).toBe(false); await expect( ctx.app.get(RecoveryStore).reset(issued.tokenHash, 'new-hash'), ).rejects.toThrow(); await ctx.db.user.update({ where: { id: ctx.owner.userId }, data: { status: 'ACTIVE' }, }); }); it('resets expired rate-limit windows', async () => { const limits = ctx.app.get(RateLimitService); await limits.consume('test-bucket', 1, 60); await expect(limits.consume('test-bucket', 1, 60)).rejects.toThrow( 'Too many', ); await ctx.db.rateLimit.update({ where: { key: hashToken('test-bucket') }, data: { expiresAt: new Date(0) }, }); await expect(limits.consume('test-bucket', 1, 60)).resolves.toBeUndefined(); }); it('rechecks current permissions inside administration transactions', async () => { const actor = await ctx.app.get(SessionStore).authenticate(ctx.token); const ownerRole = await ctx.db.role.findFirstOrThrow({ where: { isSystem: true }, }); const original = ownerRole.permissions; await ctx.db.role.update({ where: { id: ownerRole.id }, data: { permissions: [] }, }); const work = jest.fn(); await expect( ctx.app.get(AccessStore).mutate(actor, 'users.create', work), ).rejects.toThrow(); expect(work).not.toHaveBeenCalled(); await ctx.db.role.update({ where: { id: ownerRole.id }, data: { permissions: original }, }); }); it('preserves state when an audited transaction fails', async () => { const actor = await ctx.app.get(SessionStore).authenticate(ctx.token); await expect( ctx.app.get(AccessStore).mutate(actor, 'roles.manage', async (tx) => { await tx.role.create({ data: { organizationId: actor.organizationId, name: 'Rolled back', permissions: [], }, }); throw new Error('audit unavailable'); }), ).rejects.toThrow('audit unavailable'); expect(await ctx.db.role.count({ where: { name: 'Rolled back' } })).toBe(0); }); it('uses the same public recovery response for an unknown account', async () => { const response = await ctx .api() .post('/api/v1/auth/recovery/request') .send({ organizationId: ctx.owner.organizationId, email: 'unknown@example.com', }) .expect(202); expect(response.body.message).toContain('If the account is eligible'); }); });