본문으로 건너뛰기

앱 토큰 기술 명세

개요

이 문서는 앱 토큰(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 검증과 챌린지-응답 인증(앱 보안 인증 시스템 참조)이 성공한 후 이루어집니다. 발급 프로세스의 주요 단계는 다음과 같습니다:

  1. 앱 ID와 검증된 디바이스 ID를 입력으로 받습니다.
  2. 고유한 토큰 ID(JTI)를 생성합니다.
  3. 앱 토큰 페이로드(AppTokenPayload 참조)를 구성합니다 (만료 시간 포함).
  4. JWT 관리 시스템(예: JWK 기반)을 사용하여 RS256 알고리즘으로 페이로드를 서명하여 토큰을 생성합니다.
  5. 발급된 토큰 정보(ID, 앱 ID, 디바이스 ID, 상태, 만료 시간 등)를 데이터베이스(AppToken 모델 참조)에 저장합니다.
  6. 성능 최적화를 위해 토큰 정보를 Redis 등에 캐싱할 수 있습니다.
  7. 생성된 앱 토큰 문자열을 반환합니다.
// 개념적 인터페이스 (구현은 implementation-app-token.md 참조)
interface AppTokenIssuer {
/**
* @param deviceId 해시된 디바이스 ID 문자열 (hashed device ID string)
*/
generateAppToken(appId: string, deviceId: string): Promise<string>;
}

상세 구현: 실제 토큰 생성 로직, JWK 연동, DB 저장 및 캐싱 방법은 **앱 토큰 구현 가이드**를 참조하세요.

앱 토큰 검증

앱 토큰의 검증은 API 요청 시 인증 미들웨어/가드에서 수행됩니다. 주요 검증 단계는 다음과 같습니다:

  1. 요청 헤더에서 앱 토큰 문자열을 추출합니다.
  2. JWT 관리 시스템(예: JWK 기반)을 사용하여 토큰 서명을 검증하고 페이로드를 디코딩합니다. (만료 시간 등 기본 클레임 포함 검증)
  3. (캐싱 사용 시) Redis 등 캐시에서 토큰 ID(JTI)로 유효한 토큰 정보를 조회합니다. 캐시 히트 시 추가 검증 없이 성공 처리될 수 있습니다.
  4. (캐시 미스 또는 캐싱 미사용 시) 데이터베이스에서 토큰 ID(JTI)로 AppToken 레코드를 조회합니다.
  5. 데이터베이스 레코드의 유효성을 확인합니다 (존재 여부, 상태가 ACTIVE인지, 페이로드의 deviceId와 일치하는지 등).
  6. 모든 검증 단계를 통과하면 유효한 토큰으로 간주하고, 요청 컨텍스트에 appId, deviceId 등의 정보를 추가할 수 있습니다.
  7. 필요 시 토큰의 마지막 사용 시간을 업데이트합니다.
// 개념적 인터페이스 (구현은 implementation-app-token.md 참조)
interface AppTokenValidator {
validateAppToken(token: string): Promise<{
isValid: boolean;
appId?: string;
deviceId?: string;
}>;
}

상세 구현: 실제 토큰 검증 로직, JWK 연동, 캐시 처리, DB 조회 및 상태 확인 방법은 **앱 토큰 구현 가이드**를 참조하세요.

앱 토큰 갱신

앱 토큰은 만료 전에 갱신될 수 있습니다. 갱신 프로세스는 다음과 같습니다 (단, 보안 정책에 따라 갱신 대신 앱 인증 재수행을 통한 재발급을 권장할 수 있습니다):

  1. 갱신 요청 시 기존 앱 토큰을 입력으로 받습니다.
  2. 기존 토큰의 유효성을 검증합니다 (validateAppToken 로직 활용). 유효하지 않으면 갱신 실패 처리합니다.
  3. 기존 토큰의 ID(JTI)를 데이터베이스에서 찾아 상태를 REVOKED로 변경하고, 캐시에서도 제거합니다.
  4. 기존 토큰의 appIddeviceId를 사용하여 새로운 앱 토큰을 발급합니다 (generateAppToken 로직 활용).
  5. 새로 발급된 토큰을 반환합니다.
// 개념적 인터페이스 (구현은 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 구현은 앱 토큰 구현 가이드 및 관련 소스 코드를 참조하세요.

보안 고려사항 (요구사항)

토큰 보안

  1. 서명 알고리즘: 반드시 RS256과 같은 비대칭 키 알고리즘을 사용해야 합니다. JWK 관리가 필요합니다.
  2. 만료 시간: 적절히 짧은 만료 시간(예: 7일 이하)을 설정해야 합니다.
  3. 토큰 상태 관리: 데이터베이스에서 토큰 상태(ACTIVE, REVOKED, EXPIRED)를 추적하고, 검증 시 반드시 확인해야 합니다.
  4. 고유 식별자(JTI): 각 토큰은 고유 ID(JTI)를 가져야 하며, 이를 통해 DB에서 식별 및 관리되어야 합니다.

디바이스 보안

  1. 디바이스 정보 해싱: 디바이스 식별자는 원본 정보가 아닌, SHA-256 이상의 강력한 해시 알고리즘 결과여야 합니다.
  2. 디바이스 바인딩: 토큰 페이로드에 포함된 deviceId는 검증 시 데이터베이스에 저장된 값과 반드시 일치해야 합니다.
  3. 앱 무결성: 앱 토큰은 반드시 앱 무결성 검증 (예: 챌린지-응답) 이후에만 발급되어야 합니다.

통신 보안

  1. TLS/SSL: 모든 API 통신은 TLS/SSL을 통해 암호화되어야 합니다 (HTTPS 강제).

저장소 보안

  1. 데이터베이스 보안: 토큰 정보 저장 시 데이터베이스 접근 제어 및 필요시 필드 암호화를 고려해야 합니다.
  2. 캐시 보안: 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.02025-03-16bok@weltcorp.com최초 작성