Skip to content

WebAuthn 비밀번호 없는 로그인

Doggy는 web-auth/webauthn-symfony-bundle 기반으로 WebAuthn 표준 인증을 구현하며 지문, 얼굴 인식, 보안 키 로그인을 지원합니다.

개요

WebAuthn 자격 증명은 src/Entity/Security/WebauthnCredential.php(테이블 webauthn_credentials)에 저장되며 App\Entity\Organization\Employee(사용자 엔티티)에 연결됩니다.

자격 증명 저장소는 PublicKeyCredentialSourceRepositoryInterface를 구현하여 WebAuthn 자격 증명의 저장과 조회를 지원합니다.

설정

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']

보안 파이어월 설정

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'

자격 증명 관리

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;   // 예: "Chrome on Mac"

    #[ORM\ManyToOne(targetEntity: Employee::class, inversedBy: 'passkeys')]
    private ?Employee $employee = null;
}

로그인 흐름

Passkey 등록

  1. 사용자가 로그인 후 보안 설정 진입(/user/webauthn/register/options)
  2. 브라우저가 WebAuthn API를 호출하여 자격 증명 생성
  3. 결과를 /user/webauthn/register/result에 제출
  4. 공개 키 자격 증명을 webauthn_credentials 테이블에 저장

비밀번호 없는 로그인

  1. 사용자 이름 입력, "Passkey 로그인" 클릭
  2. POST /login/webauthn/options로 챌린지 획득
  3. 브라우저가 WebAuthn API를 호출하여 서명
  4. 결과를 /login/webauthn/result에 제출
  5. WebauthnSuccessHandler가 로그인 성공 처리(장치 기록, 업데이트 시간)

성공 핸들러

App\Security\WebauthnSuccessHandler는 로그인 성공 후:

  1. 자격 증명의 마지막 사용 시간 업데이트
  2. 장치 이름 자동 인식(User-Agent 파싱)
  3. 최초 로그인 여부 확인, 비밀번호 변경 안내
  4. 비밀번호 재설정 흐름 지원

WebAuthn 자격 증명 저장소

App\Repository\Security\WebauthnCredentialRepository 구현:

  • findOneByCredentialId(): 자격 증명 조회
  • findAllForUserEntity(): 사용자의 모든 자격 증명 가져오기
  • saveCredentialSource(): 자격 증명 생성/업데이트
  • parseUserAgent(): 장치 이름 인식

MIT 라이선스 오픈소스 | Copyright © 2026 Doggy