
# Rotar claves de firma

`POST /v1/signing-keys:rotate` · permiso `signing-keys:write`

Ejecuta una rotación de claves de firma ahora, al margen de la programación automática.

- `mode: immediate`: el paso de rotación habitual se ejecuta de inmediato. La clave `next` publicada previamente comienza a firmar, la clave `active` anterior permanece `retired` durante 7 días y se publica una nueva clave `next`. Si `next` usa un algoritmo distinto al de la clave activa (un cambio de algoritmo) y se publicó hace menos de 7 días, la llamada falla con `409 next_key_too_young`.
- `mode: emergency` (sospecha de compromiso de la clave): la clave `active` anterior se revoca y sale del JWKS **de inmediato**; `next` pasa a estar activa y se genera una nueva clave `next`. `revoke_next: true` también revoca `next` y activa una clave recién generada que nunca se ha publicado. `revoke_sessions: true` además revoca todas las sesiones y familias de tokens de actualización del entorno, de modo que los usuarios deben volver a iniciar sesión. `reason` es obligatorio.

## Claves de federación

`use` también puede indicar una de las claves de federación del entorno: `saml_sp_signing` y `saml_sp_encryption` (las claves de firma y de cifrado del proveedor de servicios SAML publicadas en los metadatos de SP) o `oidc_rp_signing` (la clave `private_key_jwt` de las conexiones OIDC empresariales, publicada en `/sso/jwks.json`). Estas claves son siempre RSA-3072 y rotan una vez al año de forma autónoma: una nueva clave `next` se publica 30 días antes de entrar en uso, y la clave anterior permanece publicada durante 30 días después.

- `mode: immediate` activa de inmediato esa clave `next` publicada previamente; la clave anterior permanece publicada 30 días más (aparece en `retired_kids`). Sin una clave `next` publicada, la llamada falla con `409 conflict`; si la clave `next` se publicó hace menos de 7 días, falla con `409 next_key_too_young`.
- `mode: emergency` elimina de inmediato la clave activa de los metadatos o del JWKS **de inmediato** y destruye su clave privada (`revoked_kids`). La clave `next` publicada pasa a estar activa; si no hay ninguna, o con `revoke_next: true`, se genera una nueva clave que nunca se ha publicado. Los proveedores de identidad que cargan sus metadatos o su JWKS desde la URL recogen el cambio en su próxima actualización. Los proveedores en los que subió los metadatos o el certificado manualmente rechazan los inicios de sesión hasta que suba el nuevo certificado; en ese caso se envía un evento de webhook `connection.sp_certificate_rotation_started` por cada conexión afectada.

Para una clave de federación, `next_kid` en la respuesta es `null`, `removed_kids` está vacío y `revoke_sessions` no está permitido. Si el entorno nunca usó SAML o `private_key_jwt`, no hay ninguna clave que rotar y la llamada falla con `409 conflict`.

### Claves de conexión

Una conexión SAML puede tener sus propias claves SP dedicadas en lugar de las del entorno. Para rotarlas, añada `connection_id` (`conn_…`) con `use: saml_sp_signing` o `saml_sp_encryption`. Se aplican los mismos modos, permisos y reglas, pero solo cambia el conjunto de claves de esa conexión: las claves del entorno y las de las demás conexiones permanecen como están, y el evento `connection.sp_certificate_rotation_started` se envía solo para esa conexión. El registro de auditoría indica el entorno como destino y la conexión en `metadata.connection_id`. Una conexión desconocida, o una conexión de otro entorno, falla con `404 not_found`; una conexión que usa las claves del entorno falla con `409 connection_keys_not_dedicated` (rote las claves del entorno sin `connection_id`). Sin `connection_id` se rota el conjunto de claves del entorno.

El modo de emergencia necesita, además de `signing-keys:write`, el permiso `signing-keys:emergency` concedido explícitamente en la credencial: solo una clave restringida o un cliente M2M que tenga ese permiso puede llamarlo. Un secreto de acceso completo (`idp_sk_…`) recibe `403 insufficient_permissions` en modo de emergencia.

Las nuevas claves de firma de tokens usan el algoritmo de la política del entorno ([obtener la política](/docs/api/management/signing-keys/get-policy)). Una rotación correcta de la clave de firma de tokens emite el evento de webhook `signing_key.rotated` o `signing_key.revoked`; cada rotación escribe un registro de auditoría de gestión. Este endpoint pertenece a la familia de límite de tasa de «emisión de claves y secretos». Envíe una `Idempotency-Key`: un reintento tras un error de red devuelve el primer resultado en lugar de rotar dos veces.

## Solicitud

Cuerpo JSON (los campos desconocidos se rechazan con `400 unknown_field`):

| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
| `Idempotency-Key` | encabezado | no | Recomendado. 16-64 caracteres de `A-Z a-z 0-9 _ -`. |
| `mode` | string | sí | `immediate` o `emergency`. |
| `use` | string | no | La clave que se va a rotar. Por defecto `token_signing` (clave de firma de tokens de acceso e ID). Las claves de federación son `saml_sp_signing`, `saml_sp_encryption` y `oidc_rp_signing` (consulte [Claves de federación](#claves-de-federación)). |
| `revoke_sessions` | boolean | no | Solo para emergencia y `token_signing`. Revoca también todas las sesiones y tokens de actualización. Por defecto `false`. |
| `revoke_next` | boolean | no | Solo para emergencia. Revoca también la clave `next` publicada. Por defecto `false`. |
| `reason` | string | emergencia: sí | Solo para emergencia. 1-512 caracteres ASCII imprimibles, por ejemplo un número de incidente. No incluya aquí secretos ni datos personales. |
| `connection_id` | string | no | Solo para `saml_sp_signing` y `saml_sp_encryption`. Una conexión SAML (`conn_…`) con claves SP dedicadas; se rota su conjunto de claves en lugar del del entorno (consulte [Claves de conexión](#claves-de-conexión)). |

Con `mode: immediate`, si envía `revoke_sessions: true`, `revoke_next: true` o un `reason`, la llamada falla con `422` (`not_allowed`). Un `connection_id` con `token_signing` o `oidc_rp_signing` falla de la misma manera.

## Respuesta

`200 OK` con el resultado de la rotación. `activated_kid` es la clave que firma a partir de ahora y `next_kid` la clave `next` recién publicada (`null` para una clave de federación). `retired_kids` son las claves que dejaron de firmar y permanecen publicadas (7 días para una clave de firma de tokens, 30 días para una clave de federación); `revoked_kids` son las claves eliminadas del JWKS por una rotación de emergencia; `removed_kids` son las claves retiradas cuya ventana de 7 días terminó y que salieron del JWKS en el mismo paso.

```json
{
  "mode": "immediate",
  "use": "token_signing",
  "activated_kid": "ypeBEsobvcr6wjGzmiPcTaeG7_gUfE5yuYB3ha_uSLs",
  "next_kid": "GKw-c0PwFokMUQ6T-TUmEWnZ4_VlQ2Qpgw-vCTT0-OQ",
  "retired_kids": ["PiPoFgA5WUoziU9lZOGxNIu9egCI1CxKy3PurtWcAJ0"],
  "revoked_kids": [],
  "removed_kids": ["Ln0sA6lQeuJl7PW1NWiFpTOTogKdJBOUmXJloaJa78Y"],
  "revoke_sessions": false
}
```

## Errores

| HTTP | código | Cuándo |
|---|---|---|
| 400 | `unknown_field` | El cuerpo contiene un campo desconocido. |
| 400 | `malformed_request` | El cuerpo no es JSON válido, o el encabezado `Idempotency-Key` está mal formado o repetido. |
| 403 | `insufficient_permissions` | La credencial no tiene `signing-keys:write`, o se solicitó el modo de emergencia sin `signing-keys:emergency` concedido explícitamente. |
| 404 | `not_found` | `connection_id` no indica ninguna conexión SAML de este entorno. |
| 409 | `connection_keys_not_dedicated` | `connection_id` indica una conexión que usa las claves SP del entorno. |
| 409 | `next_key_too_young` | Hay un cambio de algoritmo pendiente y la clave `next` se publicó hace menos de 7 días, o (clave de federación, `immediate`) la clave `next` se publicó hace menos de 7 días. |
| 409 | `conflict` | El entorno no tiene ninguna clave `next` publicada, no tiene ninguna clave activa de la `use` de federación, o la clave de federación anterior sigue en su solapamiento de 30 días. |
| 415 | `unsupported_media_type` | El tipo de contenido no es `application/json`. |
| 422 | `validation_failed` | Falta un campo o no es válido; el puntero indica el campo, por ejemplo `/reason`. |
| 422 | `idempotency_key_reuse` | La `Idempotency-Key` ya se usó con un cuerpo distinto. |
| 501 | `not_implemented` | La operación todavía no está disponible en esta implementación. |
| 401 | `unauthenticated` | La credencial falta, no es válida o está revocada. |
| 429 | `rate_limited` | Se superó el límite de tasa; reintente después del período `Retry-After`. |
| 503 | `service_unavailable` | Una dependencia no está disponible temporalmente; reintente después del período `Retry-After`. |

## Ejemplo

Rotación habitual:

```bash
curl -X POST "https://api.eu.keenauth.com/v1/signing-keys:rotate" \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"mode":"immediate"}'
```

Rotación de emergencia con una clave restringida que concede `signing-keys:write` y `signing-keys:emergency`:

```bash
curl -X POST "https://api.eu.keenauth.com/v1/signing-keys:rotate" \
  -H "Authorization: Bearer $KEENAUTH_EMERGENCY_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"mode":"emergency","revoke_sessions":true,"reason":"INC-2041 key exposure"}'
```

Rotación de emergencia de la clave de firma SP de SAML, revocando también la clave `next` publicada:

```bash
curl -X POST "https://api.eu.keenauth.com/v1/signing-keys:rotate" \
  -H "Authorization: Bearer $KEENAUTH_EMERGENCY_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"mode":"emergency","use":"saml_sp_signing","revoke_next":true,"reason":"INC-2042 SP key exposure"}'
```

Rotación de emergencia de la clave de firma SP de SAML dedicada de una conexión:

```bash
curl -X POST "https://api.eu.keenauth.com/v1/signing-keys:rotate" \
  -H "Authorization: Bearer $KEENAUTH_EMERGENCY_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"mode":"emergency","use":"saml_sp_signing","connection_id":"conn_01h2xcejqtf2nbrexx3vqjhp41","reason":"INC-2043 connection key exposure"}'
```
