
# Modificare il passkey rpId di una connessione

`POST /v1/connections/{connection_id}:change-rp-id` · autorizzazione `connections:write`

Sposta una connessione passkey su un nuovo rpId e avvia una finestra di migrazione. Un passkey è associato all'rpId con cui è stato registrato, quindi, una volta che gli utenti hanno dei passkey, questo è l'unico modo per cambiare l'rpId: un `PATCH` che modifica `settings.rp_id` viene rifiutato con `409 rp_id_change_requires_migration`.

Durante la finestra, gli utenti possono ancora accedere con i passkey del precedente rpId, e i nuovi passkey vengono registrati sul nuovo rpId. Quando la finestra termina, oppure quando la chiudi in anticipo con [Completare la migrazione dell'rpId](complete-rp-id-migration.md), i passkey che sono ancora sul precedente rpId vengono revocati (motivo `rp_id_changed`). Gli utenti che non hanno altri metodi di accesso avranno bisogno quindi dell'accesso senza password tramite email o del recupero dell'account. In un ambiente può essere in esecuzione una sola migrazione alla volta.

L'rpId della connessione, il record di migrazione, la voce di audit di gestione e l'evento `passkey_rp_id.migration_started` vengono scritti insieme. Un dominio personalizzato che è l'rpId, o il precedente rpId di una migrazione in corso, non può essere eliminato (`409 domain_in_use_by_rp_id`).

## Richiesta

| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
| `connection_id` | path | sì | ID della connessione passkey (`conn_…`). |
| `If-Match` | header | sì | L'`ETag` della connessione (vedi [Ottenere una connessione](get.md)). |
| `Idempotency-Key` | header | no | Chiave facoltativa che rende sicura una richiesta ritentata (da 16 a 64 caratteri). |
| `new_rp_id` | string | sì | Il nuovo rpId: il nome host di un dominio personalizzato `active` dell'ambiente (minuscolo, etichetta A IDNA), oppure una stringa vuota per usare ogni host di accesso come rpId separato. Massimo 253 byte. |
| `confirm_rp_id` | string | sì | `new_rp_id` digitato di nuovo. |
| `migration_window_days` | integer | no | Per quanti giorni continuano a funzionare i passkey del precedente rpId: da 30 a 365, valore predefinito 90. |

```json
{
  "new_rp_id": "login.example.com",
  "confirm_rp_id": "login.example.com",
  "migration_window_days": 90
}
```

## Risposta

`200 OK`; la migrazione e un header `ETag` con la nuova versione della connessione.

```json
{
  "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
}
```

| Campo | Descrizione |
|---|---|
| `previous_rp_id`, `new_rp_id` | L'rpId prima e dopo la modifica; `null` significa che ogni host di accesso è un rpId separato. |
| `status` | `active` mentre la finestra è in corso; `completed` quando è terminata; `completed_early` quando è stata chiusa in anticipo. |
| `ends_at` | Quando la finestra è prevista che termini. |
| `completed_at`, `revoked_count`, `new_registration_count` | Scritti alla chiusura della migrazione; `null` mentre è `active`. |
| `fallback_enabled_by_migration` | `true` quando per la finestra è stato attivato un metodo di accesso di fallback perché i passkey erano l'unico primo fattore. |

## Errori

| HTTP | code | Quando |
|---|---|---|
| 400 | `unknown_field` | Il corpo contiene un campo sconosciuto. |
| 404 | `not_found` | La connessione non esiste o appartiene a un altro ambiente. |
| 409 | `rp_id_migration_in_progress` | Un'altra migrazione dell'rpId è in corso in questo ambiente. |
| 412 | `precondition_failed` | `If-Match` non corrisponde alla versione corrente. |
| 422 | `validation_failed` | Un campo manca, è troppo lungo o fuori intervallo; `new_rp_id` non è un dominio personalizzato `active` dell'ambiente oppure è uguale all'rpId attuale; `confirm_rp_id` è diverso; oppure la connessione non è una connessione passkey (`errors[].pointer`: `/new_rp_id`, `/confirm_rp_id`, `/migration_window_days`, `/connection_id`). |
| 428 | `precondition_required` | Manca l'header `If-Match`. |
| 401 | `unauthenticated` | La credenziale manca, non è valida o è stata revocata. |
| 403 | `insufficient_permissions` | La credenziale non ha l'autorizzazione richiesta. |
| 429 | `rate_limited` | È stato superato il limite di richieste; riprova dopo il periodo indicato in `Retry-After`. |
| 503 | `service_unavailable` | Una dipendenza è temporaneamente non disponibile; riprova dopo il periodo indicato in `Retry-After`. |

## Esempio

```bash
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"}'
```

```bash
keenauth connections change-rp-id conn_01j9xp6q0r1s5t7v9w1x3y5z7b --if-match '"3"' --yes \
  --data '{"new_rp_id":"login.example.com","confirm_rp_id":"login.example.com"}'
```
