Skip to content

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

  1. Tras iniciar sesión, el usuario entra en la configuración de seguridad (/user/webauthn/register/options)
  2. El navegador llama a la API WebAuthn para crear la credencial
  3. El resultado se envía a /user/webauthn/register/result
  4. La credencial de clave pública se almacena en la tabla webauthn_credentials

Inicio de sesión sin contraseña

  1. Introduce el nombre de usuario y haz clic en «Iniciar sesión con Passkey»
  2. POST a /login/webauthn/options para obtener el desafío
  3. El navegador llama a la API WebAuthn para firmar
  4. El resultado se envía a /login/webauthn/result
  5. WebauthnSuccessHandler gestiona 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:

  1. Actualiza la fecha del último uso de la credencial
  2. Identifica automáticamente el nombre del dispositivo (análisis del User-Agent)
  3. Comprueba si es el primer inicio de sesión y guía el cambio de contraseña
  4. Admite el flujo de restablecimiento de contraseña

Repositorio de credenciales WebAuthn

App\Repository\Security\WebauthnCredentialRepository implementa:

  • findOneByCredentialId(): buscar una credencial
  • findAllForUserEntity(): obtener todas las credenciales de un usuario
  • saveCredentialSource(): crear/actualizar una credencial
  • parseUserAgent(): identificar el nombre del dispositivo

Código abierto bajo MIT | Copyright © 2026 Doggy