Quería Sign in with Apple. Puse una passkey
ContenidoContents
Quería entrar a portal.sergiocomeron.com con Sign in with Apple. El portal es la cuenta del plugin de Jitsi: licencias, registro, telemetría. PHP propio, usuario y contraseña, sin SSO. Lo que yo quería no era federar identidad. Era Face ID y dentro.
No lo puse. El 26 de agosto monté una passkey. Aquí va qué es, por qué no Apple, y cómo se añade, con lo que hay ahora en el portal.
Qué es una passkey, en cristiano
Una passkey es una llave criptográfica que vive en el aparato. En un iPhone o un Mac, en el Secure Enclave. El navegador le pide a Face ID o Touch ID que firme un desafío. El servidor comprueba esa firma con la clave pública que guardó al registrarla.
Apple, aquí, es el llavero. No es el proveedor de identidad. No hay cuenta de Apple en medio. No hay redirección a la página de Apple.
El estándar se llama WebAuthn. El navegador tiene dos gestos: uno para registrar la llave y otro para entrar.
Por qué no Sign in with Apple
Sign in with Apple sirve para que cualquiera entre con su cuenta de Apple. Para eso hace falta el programa de desarrollador, un identificador de servicios, el dominio verificado, una URL de vuelta y un secreto JWT que hay que emitir y rotar. Cloudflare Access, que ya uso delante de otras cosas, no trae Apple como proveedor de primera. Se puede forzar, y es frágil.
El portal no necesitaba eso. Necesitaba que yo no teclee usuario y contraseña cada vez que abro el panel. Eso es desbloquear el teléfono. Una passkey.
Qué tiene que estar bien antes
WebAuthn es estricto con el origen. Si esto falla, el resto da igual.
El sitio tiene que ir por HTTPS. En local el navegador perdona. En un dominio real, no. El portal ya sale por túnel de Cloudflare.
El identificador de la web (RP ID) es el hostname y nada más. En el portal: portal.sergiocomeron.com. Ni www, ni el dominio padre, ni una IP.
El origin es esa misma URL con https, sin barra al final y sin otro puerto. Si no coincide, el PHP responde que la solicitud no es válida.
La clave privada no sale del Enclave. Lo que guardas tú es el identificador de la credencial, la clave pública y un contador. Eso va fuera del directorio que sirve Apache. En el portal es un JSON en el home, solo legible por mi usuario. La contraseña se queda: si Safari falla, no me quedo fuera.
Cómo se monta
En el directorio de la app, PHP 8.3, se instala la librería lbuchs/webauthn con Composer. No hace falta un framework.
Se fijan cuatro datos: el fichero del JSON, el hostname, el nombre que verá el aparato (en el portal, «mod_jitsi Account»), el origin y cuánto vive el desafío. En el portal el desafío caduca a los 120 segundos y se usa una sola vez.
Hay un solo endpoint, por POST y en JSON. Cuatro operaciones:
- Pedir opciones para registrar. Solo si ya hay sesión. Si no, 401.
- Confirmar el registro y guardar la clave pública. También con sesión.
- Pedir opciones para entrar. Si no hay ninguna passkey, 400, y el botón de la puerta ni se pinta.
- Comprobar la firma y abrir la misma sesión que el login de siempre.
El botón de añadir está en My account, detrás del login. Nadie registra una passkey desde fuera. Listar y borrar también exigen sesión.
Al registrar, el servidor manda el desafío y pide tres cosas: que la llave sea residente (Safari la encuentra solo), que haya Face ID o Touch ID de verdad, y que el autenticador sea el del aparato, no una llave USB. Firma con ES256. Si ese usuario ya tiene passkeys, se excluyen para no duplicar la misma.
El identificador de usuario que ve WebAuthn no es el nombre en claro: es un hash del hostname y del usuario.
Al entrar, el portal reutiliza el límite de intentos del login por contraseña (diez fallos por hora y por IP). Si la firma vale, escribe en la sesión lo mismo que el formulario: autenticado, id, usuario y rol. A partir de ahí no distingue cómo has entrado.
Una passkey es un aparato. El Mac no es el iPhone. Hay que registrar los dos. WebAuthn no comparte llaves entre hostnames: otra web es otro hostname, otro origin y otro JSON.
Cómo se usa en el portal
- Entra en el portal con usuario y contraseña.
- Abre My account.
- Pulsa Añadir passkey.
- Confirma con Face ID o Touch ID.
- Cierra sesión. En el login ya sale Entrar con passkey.
Para otro aparato, los mismos cuatro primeros pasos desde ese aparato.
Qué no es esto
No es una firma cualificada. No es eIDAS. No sustituye a un proveedor de identidad si tienes invitaciones, bajas y un departamento legal.
Si esto tuviera que ser el login de todos los Moodle que usan el plugin, se queda corto: recuperación, nombres, quién revoca. Para cómo entro yo, con la contraseña de reserva, sí.
Sign in with Apple habría resuelto un problema que no tenía. La passkey resolvió el que sí: no querer teclear.