앱 토큰 기술 명세
개요
이 문서는 앱 토큰(App Token)의 기술적 명세를 정의합니다. 앱 토큰은 사용자 로그인 전 API 호출에 필요한 인증 메커니즘을 제공하며, 앱 보안 인증 시스템에서 수행된 앱 무결성 검증 후에 발급됩니다.
구현 가이드: 앱 토큰의 실제 구현 방법은 **앱 토큰 구현 가이드**를 참조하세요. 이 문서는 기술적 '명세'에 집중합니다.
앱 토큰 구조
앱 토큰은 JWT(JSON Web Token) 형식으로 구현되며, 다음과 같은 페이로드 구조를 갖습니다:
// 파일 경로: libs/feature/auth/src/lib/application/interfaces/app-token-payload.interface.ts (예시)
interface AppTokenPayload {
sub: string; // appId (애플리케이션 식별자)
jti: string; // 토큰 고유 ID
iat: number; // 발급 시간 (Unix timestamp)
exp: number; // 만료 시간 (Unix timestamp)
deviceId: string; // 디바이스 ID (해시 처리된)
env: string; // 환경 정보 (dev, prod)
}
앱 토큰 데이터 모델
앱 토큰의 데이터베이스 모델은 다음과 같습니다:
// 파일 경로: libs/feature/auth/src/lib/domain/entities/app-token.entity.ts (예시)
interface AppToken {
id: string; // 토큰 고유 식별자 (JWT의 jti와 동일)
appId: string; // 애플리케이션 식별자
userId: string | null; // 사용자 ID (로그인 후 연결될 수 있음, nullable)
token: string; // JWT 토큰 문자열 (참조용, 필요시 암호화)
deviceId: string; // 디바이스 ID (해시 처리된)
issuedAt: Date; // 발급 시간
expiresAt: Date; // 만료 시간
lastUsedAt?: Date; // 마지막 사용 시간 (선택적)
status: TokenStatus; // 토큰 상태 (ACTIVE, REVOKED, EXPIRED)
createdAt: Date; // 생성 시간
updatedAt: Date; // 업데이트 시간
}
// 파일 경로: libs/feature/auth/src/lib/domain/enums/token-status.enum.ts (예시)
enum TokenStatus {
ACTIVE = 'ACTIVE', // 활성 상태
REVOKED = 'REVOKED', // 취소됨
EXPIRED = 'EXPIRED' // 만료됨
}
앱 토큰 발급
앱 토큰 발급은, 디바이스 ID 검증과 챌린지-응답 인증(앱 보안 인증 시스템 참조)이 성공한 후 이루어집니다. 발급 프로세스의 주요 단계는 다음과 같습니다:
- 앱 ID와 검증된 디바이스 ID를 입력으로 받습니다.
- 고유한 토큰 ID(JTI)를 생성합니다.
- 앱 토큰 페이로드(
AppTokenPayload참조)를 구성합니다 (만료 시간 포함). - JWT 관리 시스템(예: JWK 기반)을 사용하여 RS256 알고리즘으로 페이로드를 서명하여 토큰을 생성합니다.
- 발급된 토큰 정보(ID, 앱 ID, 디바이스 ID, 상태, 만료 시간 등)를 데이터베이스(
AppToken모델 참조)에 저장합니다. - 성능 최적화를 위해 토큰 정보를 Redis 등에 캐싱할 수 있습니다.
- 생성된 앱 토큰 문자열을 반환합니다.
// 개념적 인터페이스 (구현은 implementation-app-token.md 참조)
interface AppTokenIssuer {
/**
* @param deviceId 해시된 디바이스 ID 문자열 (hashed device ID string)
*/
generateAppToken(appId: string, deviceId: string): Promise<string>;
}
상세 구현: 실제 토큰 생성 로직, JWK 연동, DB 저장 및 캐싱 방법은 **앱 토큰 구현 가이드**를 참조하세요.
앱 토큰 검증
앱 토큰의 검증은 API 요청 시 인증 미들웨어/가드에서 수행됩니다. 주요 검증 단계는 다음과 같습니다:
- 요청 헤더에서 앱 토큰 문자열을 추출합니다.
- JWT 관리 시스템(예: JWK 기반)을 사용하여 토큰 서명을 검증하고 페이로드를 디코딩합니다. (만료 시간 등 기본 클레임 포함 검증)
- (캐싱 사용 시) Redis 등 캐시에서 토큰 ID(JTI)로 유효한 토큰 정보를 조회합니다. 캐시 히트 시 추가 검증 없이 성공 처리될 수 있습니다.
- (캐시 미스 또는 캐싱 미사용 시) 데이터베이스에서 토큰 ID(JTI)로
AppToken레코드를 조회합니다. - 데이터베이스 레코드의 유효성을 확인합니다 (존재 여부, 상태가
ACTIVE인지, 페이로드의deviceId와 일치하는지 등). - 모든 검증 단계를 통과하면 유효한 토큰으로 간주하고, 요청 컨텍스트에
appId,deviceId등의 정보를 추가할 수 있습니다. - 필요 시 토큰의 마지막 사용 시간을 업데이트합니다.
// 개념적 인터페이스 (구현은 implementation-app-token.md 참조)
interface AppTokenValidator {
validateAppToken(token: string): Promise<{
isValid: boolean;
appId?: string;
deviceId?: string;
}>;
}
상세 구현: 실제 토큰 검증 로직, JWK 연동, 캐시 처리, DB 조회 및 상태 확인 방법은 **앱 토큰 구현 가이드**를 참조하세요.
앱 토큰 갱신
앱 토큰은 만료 전에 갱신될 수 있습니다. 갱신 프로세스는 다음과 같습니다 (단, 보안 정책에 따라 갱신 대신 앱 인증 재수행을 통한 재발급을 권장할 수 있습니다):
- 갱신 요청 시 기존 앱 토큰을 입력으로 받습니다.
- 기존 토큰의 유효성을 검증합니다 (
validateAppToken로직 활용). 유효하지 않으면 갱신 실패 처리합니다. - 기존 토큰의 ID(JTI)를 데이터베이스에서 찾아 상태를
REVOKED로 변경하고, 캐시에서도 제거합니다. - 기존 토큰의
appId와deviceId를 사용하여 새로운 앱 토큰을 발급합니다 (generateAppToken로직 활용). - 새로 발급된 토큰을 반환합니다.
// 개념적 인터페이스 (구현은 implementation-app-token.md 참조)
interface AppTokenRenewer {
renewAppToken(token: string): Promise<string>;
}
상세 구현: 실제 토큰 갱신 로직, 기존 토큰 취소 및 새 토큰 발급 방법은 **앱 토큰 구현 가이드**를 참조하세요.
디바이스 ID 처리
디바이스 ID는 앱 토큰 생성 및 검증의 핵심 요소로, 디바이스 고유 정보를 해싱하여 생성됩니다.
디바이스 ID 개념
- 입력: 디바이스의 고유 정보 (예: UUID, 플랫폼, 버전 등).
- 처리: 입력 정보를 일관된 방식으로 정렬/직렬화한 후, SHA-256과 같은 강력한 해시 알고리즘을 적용합니다.
- 출력: 고정 길이의 해시 문자열 (예: 64자 16진수). 이 해시 값이
deviceId로 사용됩니다.
디바이스 식별자 (개념적)
// 개념적 인터페이스 (구현은 implementation-app-token.md 참조)
interface DeviceIdentifier {
getValue(): string; // 해시된 디바이스 ID 반환
// 생성자: 원시 정보 또는 이미 해시된 ID를 받을 수 있음
// constructor(deviceIdOrInfo: string | Record<string, any>);
}
interface DeviceIdHandler {
hashDeviceInfo(deviceInfo: Record<string, any>): string;
validateDeviceIdFormat(hashedDeviceId: string): boolean;
}
상세 구현:
DeviceIdentifier클래스의 실제 구현, 해싱 로직, 유효성 검증 방법은 **앱 토큰 구현 가이드**를 참조하세요.
앱 토큰 라이프사이클
발급
앱 인증 과정을 성공적으로 완료한, 검증된 앱 클라이언트에게만 발급됩니다. 발급 시 설정된 유효 기간(예: 7일)이 적용됩니다.
갱신
만료되기 전, 유효한 토큰을 기반으로 새로운 토큰을 발급받을 수 있습니다. 갱신 시 이전 토큰은 REVOKED 상태로 변경됩니다. (정책에 따라 재인증 필요할 수 있음)
만료
유효 기간이 지나면 토큰은 EXPIRED 상태가 되며, 검증 시 실패 처리됩니다. DB 상태 업데이트는 검증 시 또는 백그라운드 작업으로 처리될 수 있습니다.
취소
다음 상황에서 앱 토큰이 명시적으로 취소될 수 있습니다:
- 보안 위협 감지 시 (해당 토큰 또는 디바이스 관련 토큰)
- 앱 버전 지원 중단 시
- 사용자 로그인 후 액세스 토큰으로 대체 시 (선택적 정책)
취소된 토큰은 상태가 REVOKED로 변경되어 즉시 무효화됩니다.
API 인터페이스
앱 토큰 발급 API (예시)
앱 토큰은 일반적으로 앱 인증 플로우의 마지막 단계에서 반환됩니다. 예를 들어, 챌린지-응답 검증 성공 후 발급될 수 있습니다.
// 예시: 앱 인증 시스템의 챌린지 완료 엔드포인트
@Post('auth/app/complete-challenge')
@ApiOperation({ summary: '챌린지 응답 검증 및 앱 토큰 발급' })
@ApiResponse({ status: 200, description: '앱 토큰 발급 성공', schema: { example: { appToken: 'eyJ...' } } })
@ApiResponse({ status: 401, description: '챌린지 검증 실패' })
async completeChallenge(@Body() responseDto: AppChallengeResponseDto): Promise<{ appToken: string }>;
// 실제 구현은 해당 컨트롤러 및 AppTokenService 참조
AppChallengeResponseDto: 앱 ID, 해시된 디바이스 ID, 암호화된 챌린지 값, nonce 등을 포함할 수 있습니다.
앱 토큰 검증 (미들웨어/가드 활용)
앱 토큰은 API 요청 시 HTTP Authorization 헤더의 Bearer 스킴을 통해 전달됩니다. 서버에서는 이를 검증하기 위한 NestJS Guard 또는 미들웨어를 구현합니다.
// 예시: 앱 토큰 검증 가드 인터페이스
@Injectable()
class AppTokenGuard implements CanActivate {
constructor(private readonly appTokenValidator: AppTokenValidator) {} // AppTokenService 주입
async canActivate(context: ExecutionContext): Promise<boolean> {
// 1. 요청에서 토큰 추출 ('Authorization: Bearer <token>')
// 2. appTokenValidator.validateAppToken(token) 호출
// 3. 결과가 유효하면 true 반환 (요청 객체에 appId, deviceId 추가 가능)
// 4. 유효하지 않으면 UnauthorizedException 발생
// 상세 구현은 implementation-app-token.md 및 실제 Guard 코드 참조
return true; // Placeholder
}
}
상세 구현: 실제 컨트롤러 엔드포인트, DTO 정의, Guard 구현은 앱 토큰 구현 가이드 및 관련 소스 코드를 참조하세요.
보안 고려사항 (요구사항)
토큰 보안
- 서명 알고리즘: 반드시 RS256과 같은 비대칭 키 알고리즘을 사용해야 합니다. JWK 관리가 필요합니다.
- 만료 시간: 적절히 짧은 만료 시간(예: 7일 이하)을 설정해야 합니다.
- 토큰 상태 관리: 데이터베이스에서 토큰 상태(
ACTIVE,REVOKED,EXPIRED)를 추적하고, 검증 시 반드시 확인해야 합니다. - 고유 식별자(JTI): 각 토큰은 고유 ID(JTI)를 가져야 하며, 이를 통해 DB에서 식별 및 관리되어야 합니다.
디바이스 보안
- 디바이스 정보 해싱: 디바이스 식별자는 원본 정보가 아닌, SHA-256 이상의 강력한 해시 알고리즘 결과여야 합니다.
- 디바이스 바인딩: 토큰 페이로드에 포함된
deviceId는 검증 시 데이터베이스에 저장된 값과 반드시 일치해야 합니다. - 앱 무결성: 앱 토큰은 반드시 앱 무결성 검증 (예: 챌린지-응답) 이후에만 발급되어야 합니다.
통신 보안
- TLS/SSL: 모든 API 통신은 TLS/SSL을 통해 암호화되어야 합니다 (HTTPS 강제).
저장소 보안
- 데이터베이스 보안: 토큰 정보 저장 시 데이터베이스 접근 제어 및 필요시 필드 암호화를 고려해야 합니다.
- 캐시 보안: Redis 등 캐시 사용 시 접근 제어 및 TLS 적용을 고려해야 합니다.
상세 구현 조치: 이러한 보안 요구사항을 충족하기 위한 구체적인 구현 방법과 코드 수준의 조치는 **앱 토큰 구현 가이드**를 참조하세요.
오류 처리 (개요)
앱 토큰 관련 오류 발생 시, 정의된 오류 코드를 사용하여 일관된 방식으로 클라이언트에 응답해야 합니다.
| HTTP 상태 코드 | 예상 오류 상황 (예시) | 관련 오류 코드 (예시) |
|---|---|---|
| 401 | 토큰 누락, 유효하지 않은 토큰, 만료 | MISSING_TOKEN, INVALID_APP_TOKEN, APP_TOKEN_EXPIRED, TOKEN_REVOKED |
| 400 | 잘못된 요청 형식 (예: deviceId) | INVALID_REQUEST_FORMAT |
| 401 | 디바이스 ID 불일치 | DEVICE_ID_MISMATCH |
| 500 | 서버 내부 오류 (토큰 생성/검증 실패) | SERVER_ERROR |
상세 오류 코드 및 처리: 구체적인 오류 코드 목록과 오류 처리 구현 방식은 앱 토큰 구현 가이드 및 오류 관리 가이드를 참조하세요.
테스트 전략 (개요)
- 단위 테스트: 토큰 페이로드 생성, 디바이스 ID 해싱 등 개별 로직 단위 테스트.
- 통합 테스트:
AppTokenService의 주요 기능(생성, 검증, 갱신)과 DB/캐시 연동 테스트. 앱 인증 플로우와 연계한 테스트. - E2E 테스트: 실제 API 엔드포인트를 통한 앱 토큰 발급 및 사용 시나리오 테스트.
- 보안 테스트: 토큰 위변조 시도, 만료/취소된 토큰 사용 시나리오, 디바이스 ID 불일치 등 보안 관련 케이스 테스트.
변경 이력
| 버전 | 날짜 | 작성자 | 변경 사항 |
|---|---|---|---|
| 0.1.0 | 2025-03-16 | bok@weltcorp.com | 최초 작성 |