Inicio de sesión sin contraseña con WebAuthn
Doggy implementa la autenticación estándar WebAuthn basándose en web-auth/webauthn-symfony-bundle, con soporte para inicio de sesión mediante huella dactilar, reconocimiento facial y llaves de seguridad.
Descripción general
Las credenciales WebAuthn se almacenan en src/Entity/Security/WebauthnCredential.php (tabla webauthn_credentials), asociadas a App\Entity\Organization\Employee (la entidad de usuario).
El repositorio de credenciales implementa PublicKeyCredentialSourceRepositoryInterface, con soporte para guardar y consultar credenciales WebAuthn.
Configuración
yaml
# config/packages/webauthn.yaml
webauthn:
credential_repository: 'App\Repository\Security\WebauthnCredentialRepository'
user_repository: 'App\Repository\Organization\EmployeeRepository'
creation_profiles:
default:
rp:
name: '%env(RELYING_PARTY_NAME)%'
id: '%env(RELYING_PARTY_ID)%'
public_key_credential_parameters:
- -7 # ES256
- -257 # RS256
authenticator_selection_criteria:
resident_key: required
user_verification: required
request_profiles:
default:
rp_id: '%env(RELYING_PARTY_ID)%'
user_verification: required
allowed_origins: ['http://localhost:8000']Configuración del firewall de seguridad
yaml
# config/packages/security.yaml
security:
firewalls:
main:
custom_authenticator: App\Security\AppCustomAuthenticator
webauthn:
success_handler: App\Security\WebauthnSuccessHandler
authentication:
enabled: true
routes:
options_path: '/login/webauthn/options'
result_path: '/login/webauthn/result'Gestión de credenciales
php
// src/Entity/Security/WebauthnCredential.php
class WebauthnCredential
{
private string $publicKeyCredentialId;
private string $type; // 'public-key'
private array $transports; // ['usb', 'nfc', 'internal']
private string $attestationType;
private string $credentialPublicKey;
private string $userHandle; // Employee ID
private int $counter;
private ?string $deviceName; // p. ej. "Chrome en Mac"
#[ORM\ManyToOne(targetEntity: Employee::class, inversedBy: 'passkeys')]
private ?Employee $employee = null;
}Flujo de inicio de sesión
Registrar un Passkey
- Tras iniciar sesión, el usuario entra en la configuración de seguridad (
/user/webauthn/register/options) - El navegador llama a la API WebAuthn para crear la credencial
- El resultado se envía a
/user/webauthn/register/result - La credencial de clave pública se almacena en la tabla
webauthn_credentials
Inicio de sesión sin contraseña
- Introduce el nombre de usuario y haz clic en «Iniciar sesión con Passkey»
- POST a
/login/webauthn/optionspara obtener el desafío - El navegador llama a la API WebAuthn para firmar
- El resultado se envía a
/login/webauthn/result WebauthnSuccessHandlergestiona el inicio de sesión correcto (registra el dispositivo, actualiza la fecha)
Procesador de éxito
App\Security\WebauthnSuccessHandler, tras un inicio de sesión correcto:
- Actualiza la fecha del último uso de la credencial
- Identifica automáticamente el nombre del dispositivo (análisis del User-Agent)
- Comprueba si es el primer inicio de sesión y guía el cambio de contraseña
- Admite el flujo de restablecimiento de contraseña
Repositorio de credenciales WebAuthn
App\Repository\Security\WebauthnCredentialRepository implementa:
findOneByCredentialId(): buscar una credencialfindAllForUserEntity(): obtener todas las credenciales de un usuariosaveCredentialSource(): crear/actualizar una credencialparseUserAgent(): identificar el nombre del dispositivo