연결의 패스키 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"}'