9d3d431416
- Added account types and privileged account types constants. - Created admin user controller for updating user security fields. - Developed role assignment controller for managing user roles. - Implemented validation middleware for request schemas. - Defined user role and auth session models for database interactions. - Created services for authentication, email notifications, and OTP handling. - Developed OAuth service for Google and Apple authentication. - Added JWT utility functions for token generation and verification. - Implemented comprehensive tests for authentication, session management, and password policies. - Created migration for updating user schema and adding new tables for auth sessions and user identities.
38 lines
2.5 KiB
JavaScript
38 lines
2.5 KiB
JavaScript
const mockTransaction = { LOCK: { UPDATE: "UPDATE" } };
|
|
const mockDb = {
|
|
AuthSession: { create: jest.fn(), findByPk: jest.fn(), update: jest.fn() },
|
|
User: { findByPk: jest.fn() },
|
|
sequelize: { transaction: jest.fn((callback) => callback(mockTransaction)) },
|
|
};
|
|
jest.mock("../../app/models", () => mockDb);
|
|
const service = require("../../app/services/auth/session.service");
|
|
|
|
describe("durable refresh sessions", () => {
|
|
beforeEach(() => jest.clearAllMocks());
|
|
test("stores a hash and never the raw refresh token", async () => {
|
|
mockDb.AuthSession.create.mockImplementation(async (values) => ({ ...values }));
|
|
const result = await service.createSession({ user: { id: "usr", tokenVersion: 2 }, rememberMe: false });
|
|
expect(result.refreshToken).toContain(`${result.session.id}.`);
|
|
expect(result.session.refresh_token_hash).toBe(service.hashRefreshToken(result.refreshToken));
|
|
expect(JSON.stringify(result.session)).not.toContain(result.refreshToken);
|
|
});
|
|
test("rotates once and marks the previous session replaced", async () => {
|
|
const token = "11111111-1111-4111-8111-111111111111.secret";
|
|
const old = { id: token.split(".")[0], user_id: "usr", token_family_id: "22222222-2222-4222-8222-222222222222", refresh_token_hash: service.hashRefreshToken(token), remember_me: false, token_version: 1, expires_at: new Date(Date.now() + 10000), revoked_at: null, save: jest.fn() };
|
|
const user = { id: "usr", accountStatus: "ACTIVE", tokenVersion: 1 };
|
|
mockDb.AuthSession.findByPk.mockResolvedValue(old); mockDb.User.findByPk.mockResolvedValue(user);
|
|
mockDb.AuthSession.create.mockImplementation(async (values) => ({ ...values }));
|
|
const result = await service.rotateSession(token);
|
|
expect(result.refreshToken).not.toBe(token);
|
|
expect(old.revoked_reason).toBe("ROTATED");
|
|
expect(old.replaced_by_session_id).toBe(result.session.id);
|
|
});
|
|
test("replay of a rotated token revokes its family", async () => {
|
|
const token = "id.old-token";
|
|
const old = { id: "id", token_family_id: "family", revoked_at: new Date(), revoked_reason: "ROTATED", refresh_token_hash: service.hashRefreshToken(token) };
|
|
mockDb.AuthSession.findByPk.mockResolvedValue(old); mockDb.AuthSession.update.mockResolvedValue([1]);
|
|
await expect(service.rotateSession(token)).rejects.toMatchObject({ code: "REFRESH_TOKEN_REUSE" });
|
|
expect(mockDb.AuthSession.update).toHaveBeenCalledWith(expect.objectContaining({ revoked_reason: "REFRESH_TOKEN_REUSE" }), expect.objectContaining({ where: expect.objectContaining({ token_family_id: "family" }) }));
|
|
});
|
|
});
|