사용자 로그인, 가입 처리
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import { describe, it, expect, beforeEach } from "vitest";
|
||||
|
||||
// 앞으로 구현할 인증/보안 유틸리티 모듈을 대상으로 TDD를 진행한다.
|
||||
// - 비밀번호 해시/검증 (bcrypt 기반)
|
||||
// - JWT 액세스 토큰 발급/검증
|
||||
// - 민감정보 JSON 암호화/복호화
|
||||
|
||||
import {
|
||||
hashPassword,
|
||||
verifyPassword,
|
||||
signAccessToken,
|
||||
verifyAccessToken,
|
||||
encryptJson,
|
||||
decryptJson,
|
||||
} from "@/features/auth/authCrypto";
|
||||
|
||||
interface TestUser {
|
||||
id: string;
|
||||
email: string;
|
||||
tokenVersion: number;
|
||||
}
|
||||
|
||||
describe("authCrypto 유틸리티", () => {
|
||||
beforeEach(() => {
|
||||
// 테스트 환경에서 사용할 JWT/암호화 키를 설정한다.
|
||||
process.env.AUTH_JWT_SECRET = "test-jwt-secret-key";
|
||||
// 32바이트 키를 hex 로 표현 (예시)
|
||||
process.env.AUTH_ENCRYPTION_KEY = "0123456789abcdef0123456789abcdef";
|
||||
});
|
||||
|
||||
describe("비밀번호 해시/검증", () => {
|
||||
it("8자 이상 비밀번호를 bcrypt 해시로 안전하게 저장하고, 검증에 성공해야 한다", async () => {
|
||||
const password = "securePass1";
|
||||
|
||||
const hash = await hashPassword(password);
|
||||
|
||||
expect(hash).toBeTypeOf("string");
|
||||
expect(hash).not.toBe(password);
|
||||
|
||||
const ok = await verifyPassword(password, hash);
|
||||
const fail = await verifyPassword("wrongPass", hash);
|
||||
|
||||
expect(ok).toBe(true);
|
||||
expect(fail).toBe(false);
|
||||
});
|
||||
|
||||
it("8자 미만 비밀번호는 해시 시도 시 오류를 발생시켜야 한다", async () => {
|
||||
const shortPassword = "short"; // 5자
|
||||
|
||||
await expect(hashPassword(shortPassword)).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("JWT 액세스 토큰", () => {
|
||||
it("유저 정보를 기반으로 JWT 액세스 토큰을 발급하고, 검증 시 동일한 정보가 나와야 한다", async () => {
|
||||
const user: TestUser = {
|
||||
id: "user-1",
|
||||
email: "user@example.com",
|
||||
tokenVersion: 1,
|
||||
};
|
||||
|
||||
const token = await signAccessToken(user);
|
||||
|
||||
expect(token).toBeTypeOf("string");
|
||||
expect(token.length).toBeGreaterThan(10);
|
||||
|
||||
const payload = await verifyAccessToken(token);
|
||||
|
||||
expect(payload).not.toBeNull();
|
||||
expect(payload?.sub).toBe(user.id);
|
||||
expect(payload?.email).toBe(user.email);
|
||||
expect(payload?.tokenVersion).toBe(user.tokenVersion);
|
||||
|
||||
const anyPayload = payload as any;
|
||||
const iat = anyPayload.iat;
|
||||
const exp = anyPayload.exp;
|
||||
|
||||
expect(typeof iat).toBe("number");
|
||||
expect(typeof exp).toBe("number");
|
||||
expect(exp > iat).toBe(true);
|
||||
});
|
||||
|
||||
it("잘못된 토큰이나 시크릿으로 검증할 경우 null 을 반환해야 한다", async () => {
|
||||
const user: TestUser = {
|
||||
id: "user-2",
|
||||
email: "user2@example.com",
|
||||
tokenVersion: 3,
|
||||
};
|
||||
|
||||
const token = await signAccessToken(user);
|
||||
|
||||
// 시크릿을 바꿔서 검증 실패 상황을 만든다.
|
||||
process.env.AUTH_JWT_SECRET = "other-secret";
|
||||
|
||||
const payload = await verifyAccessToken(token);
|
||||
expect(payload).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("민감정보 JSON 암호화/복호화", () => {
|
||||
it("JSON 객체를 암호화한 뒤 복호화하면 원래 객체와 동일해야 한다", async () => {
|
||||
const original = {
|
||||
cardToken: "tok_123",
|
||||
customerId: "cus_456",
|
||||
meta: { plan: "pro", country: "KR" },
|
||||
};
|
||||
|
||||
const ciphertext = await encryptJson(original);
|
||||
|
||||
expect(ciphertext).toBeTypeOf("string");
|
||||
expect(ciphertext).not.toContain("tok_123");
|
||||
expect(ciphertext).not.toContain("cus_456");
|
||||
|
||||
const decrypted = await decryptJson<typeof original>(ciphertext);
|
||||
|
||||
expect(decrypted).toEqual(original);
|
||||
});
|
||||
|
||||
it("암호화 문자열이 손상되었거나 키가 잘못된 경우 복호화 시 오류를 발생시켜야 한다", async () => {
|
||||
const original = { secret: "value" };
|
||||
const ciphertext = await encryptJson(original);
|
||||
|
||||
// 중간 일부를 잘라 손상시킨다.
|
||||
const broken = ciphertext.slice(0, Math.floor(ciphertext.length / 2));
|
||||
|
||||
await expect(decryptJson(broken as string)).rejects.toThrow();
|
||||
|
||||
// 키를 바꿔서 복호화 시도
|
||||
process.env.AUTH_ENCRYPTION_KEY = "ffffffffffffffffffffffffffffffff";
|
||||
await expect(decryptJson(ciphertext)).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user