본문으로 건너뛰기

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최초 작성