연결의 패스키 rpId 변경

POST /v1/connections/{connection_id}:change-rp-id · 권한 connections:write

패스키 연결을 새 rpId로 이동하고 마이그레이션 기간을 시작합니다. 패스키는 등록된 rpId에 묶여 있으므로, 사용자가 패스키를 보유한 후에는 이 방법으로만 rpId를 변경할 수 있습니다. settings.rp_id를 변경하는 PATCH 요청은 409 rp_id_change_requires_migration으로 거부됩니다.

마이그레이션 기간 동안 사용자는 이전 rpId의 패스키로 여전히 로그인할 수 있으며, 새 패스키는 새 rpId에 등록됩니다. 기간이 끝나거나 rpId 마이그레이션 완료로 조기에 종료하면, 이전 rpId에 남아 있는 패스키는 폐기됩니다(사유 rp_id_changed). 다른 로그인 방법이 없는 사용자는 비밀번호 없는 이메일 로그인 또는 계정 복구가 필요합니다. 한 환경에서는 한 번에 하나의 마이그레이션만 실행할 수 있습니다.

연결의 rpId, 마이그레이션 기록, 관리 감사 항목, passkey_rp_id.migration_started 이벤트는 함께 기록됩니다. rpId이거나 실행 중인 마이그레이션의 이전 rpId인 사용자 지정 도메인은 삭제할 수 없습니다(409 domain_in_use_by_rp_id).

요청

필드 유형 필수 설명
connection_id path 예 패스키 연결 ID(conn_…).
If-Match header 예 연결의 ETag(연결 조회 참조).
Idempotency-Key header 아니요 재시도 요청을 안전하게 만드는 선택 키(16~64자).
new_rp_id string 예 새 rpId: 환경의 active 상태 사용자 지정 도메인의 호스트 이름(소문자, IDNA A-label), 또는 각 로그인 호스트를 자체 rpId로 사용하려면 빈 문자열. 최대 253바이트.
confirm_rp_id string 예 new_rp_id를 다시 입력한 값.
migration_window_days integer 아니요 이전 rpId의 패스키가 계속 동작하는 일수: 30~365, 기본값 90.
{
  "new_rp_id": "login.example.com",
  "confirm_rp_id": "login.example.com",
  "migration_window_days": 90
}

응답

200 OK; 마이그레이션 정보와 연결의 새 버전을 담은 ETag 헤더.

{
  "id": "rpm_01j9xr4a6b8c0d2e4f6g8h0j2k",
  "connection_id": "conn_01j9xp6q0r1s5t7v9w1x3y5z7b",
  "previous_rp_id": null,
  "new_rp_id": "login.example.com",
  "status": "active",
  "started_at": "2026-10-27T10:00:00Z",
  "ends_at": "2027-01-25T10:00:00Z",
  "completed_at": null,
  "revoked_count": null,
  "new_registration_count": null,
  "fallback_enabled_by_migration": false
}
필드 설명
previous_rp_id, new_rp_id 변경 전후의 rpId; null은 각 로그인 호스트가 자체 rpId임을 의미합니다.
status 기간이 진행 중이면 active; 기간이 끝났으면 completed; 조기에 종료되었으면 completed_early.
ends_at 기간이 종료될 예정 시각.
completed_at, revoked_count, new_registration_count 마이그레이션이 종료될 때 기록되며, active 상태에서는 null.
fallback_enabled_by_migration 패스키가 유일한 1차 인증 수단이어서 기간 동안 대체 로그인 방법이 켜진 경우 true.

오류

HTTP code 발생 조건
400 unknown_field 요청 본문에 알 수 없는 필드가 있습니다.
404 not_found 연결이 존재하지 않거나 다른 환경에 속합니다.
409 rp_id_migration_in_progress 이 환경에서 다른 rpId 마이그레이션이 실행 중입니다.
412 precondition_failed If-Match가 현재 버전과 일치하지 않습니다.
422 validation_failed 필드가 없거나, 너무 길거나, 범위를 벗어납니다. new_rp_id가 환경의 active 상태 사용자 지정 도메인이 아니거나 현재 rpId와 같습니다. confirm_rp_id가 다릅니다. 또는 연결이 패스키 연결이 아닙니다(errors[].pointer: /new_rp_id, /confirm_rp_id, /migration_window_days, /connection_id).
428 precondition_required If-Match 헤더가 없습니다.
401 unauthenticated 자격 증명이 없거나, 유효하지 않거나, 폐기되었습니다.
403 insufficient_permissions 자격 증명에 필요한 권한이 없습니다.
429 rate_limited 요청 한도를 초과했습니다. Retry-After 기간 후에 다시 시도하십시오.
503 service_unavailable 종속 서비스를 일시적으로 사용할 수 없습니다. Retry-After 기간 후에 다시 시도하십시오.

예시

curl -X POST "https://api.eu.keenauth.com/v1/connections/conn_01j9xp6q0r1s5t7v9w1x3y5z7b:change-rp-id" \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H 'If-Match: "3"' \
  -H "Content-Type: application/json" \
  -d '{"new_rp_id":"login.example.com","confirm_rp_id":"login.example.com"}'
keenauth connections change-rp-id conn_01j9xp6q0r1s5t7v9w1x3y5z7b --if-match '"3"' --yes \
  --data '{"new_rp_id":"login.example.com","confirm_rp_id":"login.example.com"}'