본문으로 건너뛰기

PLT-SEC-005 인증/인가 시스템 구현 가이드

이 구현 가이드는 DTx 플랫폼의 PLT-SEC-005 인증/인가 요구사항을 체계적으로 구현하기 위한 포괄적인 계획을 제시합니다.

📋 목차

  1. 개요
  2. 현재 상태 분석
  3. 아키텍처 설계
  4. 구현 계획
  5. RBAC 시스템 구현
  6. MFA 구현
  7. 감사 로깅
  8. 모니터링 및 운영
  9. 배포 계획

1. 개요

1.1 목표

  • 기존 JWT/토큰 인증 시스템을 강화하여 완전한 RBAC 구현
  • MFA(Multi-Factor Authentication) 구현으로 보안 강화
  • 포괄적인 감사 로깅 시스템 구축
  • Redis 기반 권한 캐싱으로 성능 최적화
  • 의료 데이터 보호 규정 준수 (GDPR, HIPAA)

1.2 규정 준수 요구사항

  • DiGA: 독일 의료 규정에 따른 강화된 인증
  • FDA: 미국 의료 기기 소프트웨어 보안 요구사항
  • K-FDA: 한국 의료 기기 보안 표준
  • GDPR: 개인정보 보호를 위한 접근 제어
  • ISO 27001: 정보보안 관리 체계

1.3 성공 지표

  • 인증 성공률: 99.9% 이상
  • 평균 인증 응답 시간: 200ms 이하
  • MFA 적용률: 관리자 100%, 일반 사용자 90% 이상
  • 감사 로그 수집률: 100%
  • 보안 인시던트: 0건 유지

2. 현재 상태 분석

2.1 기존 구현 현황 ✅

인증 시스템

// 이미 구현된 JWT 인증 시스템
libs/feature/auth/src/lib/guards/
├── jwt-auth.guard.ts // JWT 토큰 기반 사용자 인증
├── app-token.guard.ts // 앱 토큰 기반 앱 인증
└── validate-token.handler.ts // 토큰 검증 핸들러

복잡한 RBAC 시스템 (이미 구현됨)

  • 15개 세분화된 역할: patientUser, clinicianAdmin, systemAdmin
  • 도메인별 권한 체계: User, IAM, TimeMachine, Sleep 도메인
  • Plan-Role 매핑: 8개 플랜 (plan_therapeutic, plan_clinician 등)
  • 다차원 권한 계산: Group 권한 ∩ Plan 권한

현재 역할 분류

// 환자 역할
roles/healthcare.patientUser // 전체 환자 기능
roles/healthcare.patientViewer // 읽기 전용 환자

// 의료진 역할
roles/healthcare.clinicianAdmin // 임상의 관리자
roles/healthcare.providerViewer // 의료 제공자 뷰어

// 임상시험 역할
roles/clinical.coordinatorUser // 임상시험 코디네이터
roles/clinical.monitorViewer // 임상시험 모니터
roles/clinical.researchAdmin // 연구 책임자

// 시스템 관리 역할
roles/admin.systemAdmin // 시스템 관리자
roles/data.managerUser // 데이터 관리자
roles/data.analystViewer // 데이터 분석가
roles/development.engineerViewer // 개발자

// 비즈니스 역할
roles/product.managerUser // 제품 관리자
roles/crm.managerAdmin // CRM 관리자
roles/regulatory.specialistUser // 규제 담당자
roles/support.agentUser // 고객 지원 담당자

도메인별 권한 체계 (이미 정의됨)

// User Domain
user.account.read, user.account.create, user.profile.update

// IAM Domain
iam.role.read, iam.role.create, iam.assignment.update

// Sleep Domain
sleep.log.create, sleep.goal.update, sleep.report.read

// TimeMachine Domain
timemachine.time.set, timemachine.config.update

2.2 강화가 필요한 부분 ⚠️

MFA 구현

  • 다중 인증 시스템 미구현
  • 관리자 계정 추가 보안 부족
  • 보안 정책 자동화 부재

포괄적 감사 로깅

  • 인증/인가 이벤트 상세 로깅 부족
  • BigQuery 기반 로그 분석 미구현
  • 실시간 보안 모니터링 부재

성능 최적화

  • 권한 검증 캐싱 시스템 미구현
  • Redis 기반 세션 관리 부족
  • 응답 시간 최적화 필요

3. 아키텍처 설계

3.1 전체 시스템 아키텍처

3.2 인증 플로우

4. 구현 계획

4.1 1단계: 기존 시스템 강화 (예상 2주)

현재 권한 시스템 확장

// 기존 시스템에 추가될 권한 매트릭스
// libs/feature/auth/src/lib/types/enhanced-permission.types.ts
export interface EnhancedPermission {
id: string;
domain: string; // 기존 도메인 유지: user, iam, sleep, timemachine
resource: string; // 기존 리소스 확장
action: string; // 기존 액션 확장
conditions?: PermissionCondition[];
contextualScope?: ContextualScope;
}

export interface ContextualScope {
patientScope?: 'own' | 'assigned' | 'all';
organizationScope?: string[];
timeRestriction?: {
startTime?: string;
endTime?: string;
};
}

// 기존 15개 역할과 연동
export interface RolePermissionMapping {
roleId: string; // 예: roles/healthcare.patientUser
permissions: EnhancedPermission[];
planContext?: string; // 예: plan_therapeutic
groupContext?: string; // 예: patients.general
}

기존 RBAC 서비스 강화

// libs/feature/auth/src/lib/services/enhanced-rbac.service.ts
@Injectable()
export class EnhancedRbacService extends ExistingRbacService {
constructor(
private readonly existingPermissionService: PermissionService,
private readonly groupService: GroupService,
private readonly planService: PlanService,
private readonly cacheService: CacheService,
private readonly auditService: AuditService
) {
super();
}

// 기존 다차원 권한 계산 강화
async calculateEffectivePermissions(
userId: string,
context?: any
): Promise<EnhancedPermission[]> {
// 1. 기존 Group 기반 권한 계산
const groupPermissions = await this.getGroupBasedPermissions(userId);

// 2. 기존 Plan 기반 권한 계산
const planPermissions = await this.getPlanBasedPermissions(userId);

// 3. 교집합 계산 (기존 로직 유지)
const effectivePermissions = this.intersectPermissions(
groupPermissions,
planPermissions
);

// 4. 컨텍스트 기반 필터링 (신규 추가)
return this.applyContextualFiltering(effectivePermissions, context);
}

// 기존 시스템과 호환되는 권한 검증
async hasPermission(
userId: string,
domain: string,
resource: string,
action: string,
context?: any
): Promise<boolean> {
const cacheKey = `enhanced:permission:${userId}:${domain}.${resource}.${action}`;

// 캐시 확인
const cachedResult = await this.cacheService.get(cacheKey);
if (cachedResult !== null) {
return cachedResult;
}

// 효과적 권한 계산
const effectivePermissions = await this.calculateEffectivePermissions(userId, context);

// 권한 매칭
const hasPermission = effectivePermissions.some(permission =>
permission.domain === domain &&
permission.resource === resource &&
permission.action === action &&
this.evaluateContextualScope(permission, context)
);

// 결과 캐싱 (TTL: 5분)
await this.cacheService.set(cacheKey, hasPermission, 300);

// 감사 로깅
await this.auditService.logPermissionCheck({
userId,
permission: `${domain}.${resource}.${action}`,
granted: hasPermission,
timestamp: new Date(),
context
});

return hasPermission;
}
}

4.2 2단계: 기존 Guard 시스템에 MFA 통합 (예상 1주)

기존 JWT Guard 확장

// libs/feature/auth/src/lib/guards/enhanced-jwt-auth.guard.ts
@Injectable()
export class EnhancedJwtAuthGuard extends JwtAuthGuard {
constructor(
commandBus: CommandBus,
loggerService: LoggerService,
private readonly mfaService: MfaService,
private readonly enhancedRbacService: EnhancedRbacService
) {
super(commandBus, loggerService);
}

async canActivate(context: ExecutionContext): Promise<boolean> {
// 1. 기존 JWT 검증
const basicAuthResult = await super.canActivate(context);
if (!basicAuthResult) {
return false;
}

const request = context.switchToHttp().getRequest<ExtendedRequest>();
const user = request.user;

// 2. 사용자 역할에 따른 MFA 요구사항 확인
const userRoles = await this.enhancedRbacService.getUserRoles(user.id);
const requiresMfa = this.checkMfaRequirement(userRoles);

if (requiresMfa) {
// 3. MFA 검증 확인
const mfaToken = request.headers['x-mfa-token'];
if (!mfaToken) {
throw new UnauthorizedException('MFA token required');
}

const mfaValid = await this.mfaService.verifyMfaToken(user.id, mfaToken);
if (!mfaValid) {
throw new UnauthorizedException('Invalid MFA token');
}
}

return true;
}

private checkMfaRequirement(userRoles: string[]): boolean {
// 기존 역할에 따른 MFA 요구사항
const mfaRequiredRoles = [
'roles/admin.systemAdmin',
'roles/healthcare.clinicianAdmin',
'roles/data.managerUser',
'roles/regulatory.specialistUser'
];

return userRoles.some(role => mfaRequiredRoles.includes(role));
}
}

기존 시스템과 통합된 MFA 서비스

// libs/feature/auth/src/lib/services/integrated-mfa.service.ts
@Injectable()
export class IntegratedMfaService {
constructor(
private readonly existingUserService: UserService,
private readonly cacheService: CacheService,
private readonly auditService: AuditService
) {}

async setupMfaForRole(userId: string, roleId: string): Promise<MfaSetupResponse> {
// 기존 역할에 따른 MFA 방법 결정
const mfaMethod = this.getMfaMethodForRole(roleId);

switch (mfaMethod) {
case 'TOTP':
return this.setupTotpForUser(userId);
case 'EMAIL':
return this.setupEmailMfaForUser(userId);
default:
throw new BadRequestException(`No MFA required for role: ${roleId}`);
}
}

private getMfaMethodForRole(roleId: string): string | null {
const roleMfaMapping = {
'roles/admin.systemAdmin': 'TOTP',
'roles/healthcare.clinicianAdmin': 'TOTP',
'roles/data.managerUser': 'EMAIL',
'roles/regulatory.specialistUser': 'EMAIL'
};

return roleMfaMapping[roleId] || null;
}
}

4.3 3단계: 기존 Guard 시스템 확장 (예상 1주)

기존 Permission 검증 강화

// libs/feature/auth/src/lib/guards/enhanced-permission.guard.ts
@Injectable()
export class EnhancedPermissionGuard implements CanActivate {
constructor(
private readonly reflector: Reflector,
private readonly enhancedRbacService: EnhancedRbacService,
private readonly auditService: AuditService
) {}

async canActivate(context: ExecutionContext): Promise<boolean> {
// 기존 방식으로 권한 요구사항 추출
const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
'permissions', [context.getHandler(), context.getClass()]
);

if (!requiredPermissions || requiredPermissions.length === 0) {
return true;
}

const request = context.switchToHttp().getRequest();
const user = request.user;

if (!user) {
throw new UnauthorizedException('User not authenticated');
}

// 기존 도메인.리소스.액션 형식으로 권한 확인
for (const permission of requiredPermissions) {
const [domain, resource, action] = permission.split('.');

const hasPermission = await this.enhancedRbacService.hasPermission(
user.id,
domain,
resource,
action,
this.buildContextFromRequest(request)
);

if (!hasPermission) {
// 감사 로깅
await this.auditService.logPermissionDenied({
userId: user.id,
permission,
endpoint: `${request.method} ${request.path}`,
timestamp: new Date()
});

throw new ForbiddenException(`Missing permission: ${permission}`);
}
}

return true;
}

// 기존 컨트롤러와 호환되는 데코레이터
@SetMetadata('permissions', ['user.profile.read'])
static RequirePermission(...permissions: string[]) {
return SetMetadata('permissions', permissions);
}
}

// 기존 컨트롤러에서 사용 예시
@Controller('users')
export class UserController {
@Get('profile')
@UseGuards(EnhancedPermissionGuard)
@EnhancedPermissionGuard.RequirePermission('user.profile.read')
async getUserProfile(@Req() request) {
// 기존 로직 유지
}

@Post('sleep-log')
@UseGuards(EnhancedPermissionGuard)
@EnhancedPermissionGuard.RequirePermission('sleep.log.create')
async createSleepLog(@Req() request) {
// 기존 로직 유지
}
}

5. RBAC 시스템 구현

5.1 권한 매트릭스

의료 데이터 접근 권한

역할환자 데이터 조회환자 데이터 수정의료진 노트시스템 설정감사 로그
PatientOwn OnlyOwn OnlyRead OnlyNoneNone
ClinicianAssigned OnlyAssigned OnlyFullNoneRead Only
AdminAllAllFullFullFull
SystemAllAllNoneRead OnlyWrite Only

5.2 동적 권한 조건

소유권 기반 접근 제어

export const USER_OWNERSHIP_CONDITION: PermissionCondition = {
field: 'userId',
operator: 'eq',
value: '{{current_user_id}}'
};

export const ASSIGNED_PATIENT_CONDITION: PermissionCondition = {
field: 'assignedClinician',
operator: 'eq',
value: '{{current_user_id}}'
};

6. MFA 구현

6.1 지원 MFA 방법

TOTP (Time-based One-Time Password)

  • Google Authenticator, Authy 지원
  • 30초 윈도우, 6자리 코드
  • QR 코드 기반 초기 설정

SMS 기반 MFA

  • 국제 SMS 지원
  • 5분 유효기간
  • 일일 시도 횟수 제한 (10회)

이메일 기반 MFA

  • 보안 이메일 발송
  • 10분 유효기간
  • HTML 템플릿 지원

6.2 MFA 정책

관리자 필수 MFA

export const MFA_POLICIES = {
admin: {
required: true,
methods: [MfaMethod.TOTP, MfaMethod.SMS],
timeout: 300 // 5분
},
clinician: {
required: true,
methods: [MfaMethod.TOTP, MfaMethod.EMAIL],
timeout: 600 // 10분
},
patient: {
required: false,
methods: [MfaMethod.SMS, MfaMethod.EMAIL],
timeout: 900 // 15분
}
};

7. 감사 로깅

7.1 감사 이벤트 타입

인증 이벤트

export interface AuthenticationEvent {
eventType: 'authentication';
userId: string;
action: 'login' | 'logout' | 'token_refresh' | 'mfa_challenge';
success: boolean;
ipAddress: string;
userAgent: string;
timestamp: Date;
metadata?: any;
}

권한 이벤트

export interface AuthorizationEvent {
eventType: 'authorization';
userId: string;
resource: Resource;
action: Action;
granted: boolean;
context?: any;
timestamp: Date;
}

7.2 감사 로그 저장소

BigQuery 스키마

CREATE TABLE `audit_auth_events` (
event_id STRING NOT NULL,
event_type STRING NOT NULL,
user_id STRING,
action STRING NOT NULL,
resource STRING,
success BOOLEAN NOT NULL,
ip_address STRING,
user_agent STRING,
context JSON,
timestamp TIMESTAMP NOT NULL,
environment STRING NOT NULL
)
PARTITION BY DATE(timestamp)
CLUSTER BY user_id, event_type;

8. 모니터링 및 운영

8.1 주요 메트릭

인증 메트릭

  • 로그인 성공/실패율
  • MFA 성공/실패율
  • 토큰 갱신 빈도
  • 세션 지속 시간

권한 메트릭

  • 권한 확인 응답 시간
  • 권한 거부율
  • 캐시 히트율
  • 권한 체크 성공률

8.2 알림 정책

보안 알림

  • 연속된 인증 실패 (5회 이상)
  • 관리자 권한 사용
  • 비정상적인 접근 패턴
  • MFA 우회 시도

9. 배포 계획 (기존 시스템 강화 방식)

9.1 점진적 강화 전략

Phase 1: Dev 환경 - 기존 시스템 확장 (1주차)

  • 기존 Guard 시스템에 Enhanced 버전 추가
  • 현재 15개 역할과 호환되는 권한 캐싱 시스템 구현
  • 기존 JWT 토큰 검증과 병행하는 감사 로깅 강화
  • 롤백 준비: 기존 시스템은 그대로 유지

Phase 2: Stage 환경 - MFA 및 성능 최적화 (2주차)

  • 기존 JWT Guard에 MFA 레이어 추가
  • 현재 Role-Plan 매핑을 활용한 Redis 캐싱 구현
  • 기존 도메인별 권한 체계와 연동된 성능 최적화
  • 호환성 검증: 현재 API 엔드포인트 영향 없음 확인

Phase 3: Prod 환경 - 실운영 적용 (3주차)

  • Feature Flag로 Enhanced Guard 점진적 활성화
  • 기존 사용자 역할에 따른 자동 MFA 설정
  • 실시간 권한 검증 모니터링
  • 무중단 전환: 기존 시스템 대기 상태 유지

9.2 기존 시스템과의 호환성 보장

현재 시스템 보존

// 기존 Guard는 그대로 유지
@UseGuards(JwtAuthGuard) // 기존 방식 지원
@UseGuards(EnhancedJwtAuthGuard) // 신규 강화 방식

점진적 API 업그레이드

  1. Phase 1: 중요 API부터 Enhanced Guard 적용
  2. Phase 2: 일반 API로 확장
  3. Phase 3: 모든 API 적용 완료
  4. 검증 완료 후: 기존 Guard 단계적 제거

롤백 전략

  • 기존 JWT 인증 시스템 완전 보존
  • Feature Flag로 즉시 기존 시스템 복귀 가능
  • 데이터베이스 스키마 변경 없음 (추가만)

9.3 성공 기준 (기존 시스템 기준)

기능적 요구사항

  • ✅ 기존 API 호환성 100% 유지
  • ✅ 권한 확인 응답 시간 증가 < 50ms
  • ✅ 기존 15개 역할 완전 지원
  • ✅ 현재 Plan-Role 매핑 유지

비기능적 요구사항

  • ✅ 기존 시스템 가용성 유지 > 99.9%
  • ✅ 사용자 경험 변화 없음
  • ✅ 기존 토큰 정책 호환성 100%

📊 비용 최적화 (기존 인프라 강화 기준)

환경별 추가 비용 (월간)

Dev 환경 (기존 + 추가)

  • BigQuery (감사 로그 강화): +$10/월
  • Secret Manager (MFA 키): +$5/월
  • Cloud Monitoring (추가 메트릭): +$10/월
  • 기존 인프라 활용: Redis, JWT 시스템 등
  • 총 추가 비용: +$25/월

Stage 환경 (기존 + 추가)

  • BigQuery (감사 로그 강화): +$25/월
  • Secret Manager (MFA 키): +$10/월
  • Cloud Monitoring (추가 메트릭): +$20/월
  • Redis 캐싱 강화: +$15/월
  • 총 추가 비용: +$70/월

Prod 환경 (기존 + 추가)

  • BigQuery (감사 로그 강화): +$150/월
  • Secret Manager (MFA 키): +$25/월
  • Cloud Monitoring (추가 메트릭): +$75/월
  • Redis 고가용성 캐싱: +$50/월
  • 고가용성 감사 시스템: +$100/월
  • 총 추가 비용: +$400/월

💰 기존 대비 비용 효율성

  • 기존 복잡한 RBAC 시스템 활용: 신규 개발 비용 $50,000 절약
  • 현재 JWT 인프라 재사용: 월 $200 운영비 절약
  • 점진적 강화 방식: 한 번에 모든 인프라 교체 불필요

🎯 결론 (수정된 현실적 접근법)

이 수정된 구현 가이드를 통해 PLT-SEC-005 인증/인가 요구사항을 기존 시스템을 활용하여 효율적으로 강화할 수 있습니다:

주요 성과

  1. 기존 복잡한 시스템 인정 - 이미 구현된 15개 역할과 도메인별 권한 체계 활용
  2. 점진적 강화 방식 - 새로운 시스템 구축 대신 기존 JWT/Guard 시스템 확장
  3. 무중단 마이그레이션 - Feature Flag 기반 단계적 적용으로 위험 최소화
  4. 비용 효율성 - 신규 개발 $50,000 절약, 월 운영비 대폭 절감
  5. 호환성 보장 - 현재 API 엔드포인트 및 사용자 경험 변화 없음

🔄 기존 시스템 대비 개선사항

구분기존 시스템강화된 시스템
인증JWT + AppTokenJWT + AppToken + MFA
권한 관리15개 정적 역할15개 역할 + 동적 컨텍스트
캐싱기본Redis 기반 성능 최적화
감사 로깅제한적BigQuery 기반 포괄적 추적
모니터링기본실시간 보안 알림 시스템

📋 다음 단계

  1. 기존 시스템 영향도 분석 - 현재 15개 역할별 사용 현황 파악
  2. Phase 1 구현 - Dev 환경에서 Enhanced Guard 시스템 구축
  3. 호환성 테스트 - 기존 API 엔드포인트 정상 동작 확인
  4. 점진적 적용 - Stage → Prod 환경 순차 적용
  5. 성능 모니터링 - 기존 대비 응답 시간 및 가용성 추적

💡 핵심 인사이트

  • "새로 만들기" 대신 "기존 강화": 이미 복잡하고 잘 설계된 시스템을 존중
  • 위험 최소화: 기존 시스템 완전 보존하면서 점진적 개선
  • 비용 효율성: 대규모 재개발 없이 보안 강화 목표 달성
  • 실무 중심: 이론적 설계보다 실제 구현 가능한 계획 수립

이제 이 현실적인 구현 가이드를 바탕으로 기존 팀과 인프라를 활용하여 안전하고 효율적으로 PLT-SEC-005 요구사항을 충족할 수 있습니다.

변경 이력

버전날짜작성자변경 내용
0.1.02025-08-13bok@weltcorp.com최초 작성