Saltar al contenido

El día que el diálogo reaparece para todos

El incidente clásico de macOS: una versión nueva y miles de usuarios viendo un diálogo de autorización a la vez. Qué lo causa, cómo se detecta antes de publicar y qué hacer si ya ha pasado.

Los seis cambios que lo causan

Todos comparten forma: la identidad de firma con la que se compara ya no coincide con la que guardó el secreto.

Los dos primeros son la inmensa mayoría de los casos reales, y los dos se evitan con disciplina: elegir el identificador de paquete el primer día y renovar certificados sin cambiar de equipo.

La comprobación de dos minutos

Antes de publicar cualquier versión, comparar la identidad de la nueva con la de la anterior. Es un comando del sistema y la salida se lee de un vistazo:

Shell
# la identidad de la versión publicada y la de la nueva, lado a ladocodesign -dv --verbose=4 /Applications/MiApp.app 2>&1 | grep -E 'Identifier|TeamIdentifier|Authority'codesign -dv --verbose=4 ./target/release/bundle/macos/MiApp.app 2>&1 | grep -E 'Identifier|TeamIdentifier|Authority'

Si el identificador y el equipo coinciden, no hay diálogo. Si difiere alguno, lo hay — y ahora lo sabes antes de publicarlo, que es toda la diferencia.

Merece la pena que esto sea un paso automático de tu proceso de publicación, comparando contra los valores esperados. Es una comprobación de diez líneas que previene el incidente entero, y es el ejemplo canónico de lo que la fase X llama una puerta de publicación.

Si ya ha pasado

El caso desagradable. Ordenado por lo que de verdad se puede hacer:

La migración del segundo bloque es la respuesta cuando el cambio es real —de equipo, de modelo de distribución— y tiene una propiedad buena: el diálogo aparece una vez, y el usuario que pulsa «permitir siempre» no vuelve a verlo. Lo que hace daño no es el diálogo: es que se repita porque nadie migró.

El código que lo hace soportable

Tres cosas, y no son de llavero sino de trato al usuario:

Rust
// 1 · "no está" y "denegado" son caminos, no errores inesperadosmatch secretos::leer(&app) {    Ok(Some(t)) => sesion::restaurar(t),    Ok(None) => sesion::pedir_credenciales(),      // primera vez, o el usuario lo borró    Err(e) if e.es_denegado() => sesion::pedir_credenciales(),  // denegó: no insistir    Err(e) => return Err(e),                        // esto sí es un fallo}
  • Denegado y ausente llevan al mismo sitio: pedir credenciales. Ninguno de los dos es una pantalla de error.
  • No reintentar tras un rechazo. Reintentar saca el diálogo otra vez, y eso convierte una molestia en un motivo de desinstalación.
  • Y no leer el llavero durante el arranque si se puede evitar: un diálogo del sistema encima de una ventana que aún se está pintando es la peor primera impresión posible.