콘텐츠로 이동

API key와 client-secret

두 credential은 목적과 수명주기가 다릅니다. API key는 워크스페이스에 귀속된 장기 읽기 credential이고, client-secret은 로그인 회원이 실시간 세션을 위해 발급받는 단기 Ed25519 JWT입니다.

워크스페이스 API key

  • 발급: POST /v1/workspaces/api-keys
  • 권한: X-Workspace-Id 대상 워크스페이스의 OWNER
  • 형식: 환경별 접두 + SecureRandom 32바이트를 base62로 인코딩한 랜덤 부분
  • 저장: 원본이 아니라 SHA-256 secret_hash와 표시용 key_prefix
  • 노출: 발급 응답에서만 원본을 보여 주며 이후 목록에는 포함하지 않음
  • 사용: Authorization: Bearer <api-key>
  • 권한: SCOPE_WORKSPACE_READ
  • 폐기: 물리 삭제가 아니라 REVOKED 상태 전환

API key 목록은 원본과 해시를 제외하고 ID, 이름, 접두, 종류, 상태, 최근 사용 시각, 생성 시각만 반환합니다. 운영 프로파일은 LIVE, 기본·개발 프로파일은 TEST 종류를 발급합니다.

client-secret 발급

sequenceDiagram
    participant C as 로그인 클라이언트
    participant A as App Backend
    participant DB as PostgreSQL
    participant S as Socket Backend

    C->>A: POST /v1/workspaces/client-secrets<br/>Bearer access JWT + X-Workspace-Id
    A->>A: HMAC access JWT 검증
    A->>DB: ACTIVE 멤버십 확인
    A->>A: TTL clamp(300..10800, 기본 3600)
    A->>A: Ed25519 개인키로 JWT 서명
    A-->>C: clientSecret.value + 실제 TTL + expiresAt
    Note over A,DB: client-secret은 DB에 저장하지 않음
    C->>S: WebSocket 연결에 client-secret 제출
    S->>S: Ed25519 공개키로 서명·claim·만료 검증

발급 요청 본문은 생략하거나 {"expiresInSeconds": 3600}으로 보냅니다. 값이 범위 밖이면 400을 반환하지 않고 300초 또는 10,800초로 보정합니다.

client-secret의 JOSE·claim 계약은 다음과 같습니다.

  • header: alg=EdDSA, typ=JWT
  • iss=sohri-app-backend
  • aud=sohri-realtime 단일 문자열
  • sub=<memberId>
  • workspaceId=<workspace UUID>
  • jti=<random UUID>
  • iat, exp는 epoch seconds이며 exp = iat + 실제 TTL
  • apiKeyId는 포함하지 않음

일회용 토큰이 아닙니다

응답에서만 전달되고 서버가 저장하지 않는다는 의미이지, 한 번 사용하면 폐기된다는 뜻이 아닙니다. 사용 횟수나 jti 소비 상태를 기록하지 않으므로 유효 기간 안에는 재사용할 수 있습니다. 탈취 위험을 TTL과 전송 채널 보호로 제한해야 합니다.

키 분리와 서비스 경계

로그인 JWT는 HMAC 대칭키, client-secret은 Ed25519 개인키로 서명합니다. Socket Backend에는 client-secret 검증용 공개키만 배포해야 하며 로그인 HMAC 비밀키를 공유할 이유가 없습니다. 발급 뒤 App Backend가 Socket Backend를 HTTP로 호출하거나 토큰을 전달하는 단계도 없습니다.

회원·워크스페이스 검증은 App Backend가 발급 시 수행하고, 실시간 연결의 토큰 검증은 Socket Backend가 자체 수행합니다. 상세 인증 흐름은 로그인과 토큰 인증, 현재 키 배선 위험은 보안과 현재 격차를 참고하세요.