Get your free and exclusive +45-page Authentication Analytics Whitepaper
개요로 돌아가기

프로덕션 환경의 WebAuthn 오류 완벽 가이드 (2026)

NotAllowedError와 같은 일반적인 WebAuthn 오류가 프로덕션 환경에서 무엇을 의미하는지, 그리고 작업 유형, 타이밍 및 플랫폼 컨텍스트에 따라 어떻게 분류하는지 알아보세요.

Vincent Delitz
Vincent Delitz

작성일: 2026년 2월 9일

업데이트: 2026년 7월 3일

프로덕션 환경의 WebAuthn 오류 완벽 가이드 (2026)

이 페이지는 자동 번역되었습니다. 영어 원문은 여기.

1. 소개#

프로덕션 환경에서 WebAuthn 오류는 혼란스럽습니다. 브라우저가 근본적인 원인을 다양하게 나타낼 수 있는 소수의 DOMException 이름(예: NotAllowedError)만 노출하기 때문입니다. 게다가 최적화된 대규모 배포에서 95% 이상을 차지하는 대다수의 "오류"는 실제로는 예상된 동작(사용자가 운영 체제의 패스키 프롬프트를 중단함)입니다.

Debugger Icon

Passkeys Debugger에서 passkey 흐름을 실험하세요.

무료로 체험하기

중요: 개인 정보 보호를 위해 브라우저는 사용자가 적극적으로 취소했는지 아니면 패스키가 존재하지 않는지 구분하지 않습니다. 그러나 웹과 네이티브 플랫폼 모두의 일부 상황에서 충분한 컨텍스트가 있다면 타이밍과 같은 신호를 사용하여 이러한 사례 중 일부를 구별할 수 있습니다.

이러한 이름에 대한 표준 정의를 원한다면 MDN DOMException부터 시작하세요. 이러한 예외를 발생시키는 WebAuthn 관련 조건(및 브라우저가 강제해야 하는 사항)에 대해서는 W3C 웹 인증 사양을 참조하세요.

모든 오류를 "버그"로 취급하면 잘못된 조치를 취하게 됩니다.

  • 정상적인 취소로 인해 오류 지표가 오염됩니다.
  • 방대한 NotAllowedError 속에 숨겨진 실제 회귀를 놓치게 됩니다.
  • 사용자의 복구를 돕는 대신 사용자를 혼란스럽게 하는 UI를 제공하게 됩니다.

이 글에서는 다음 질문에 답합니다.

  • 실제 트래픽에서 가장 일반적인 WebAuthn 오류 이름은 보통 무엇을 의미할까요?
  • NotAllowedError를 어떻게 조치 가능한 버킷(취소 vs 시간 초과 vs 가용성)으로 구분할 수 있을까요?
  • 작업(조건부 UI 로그인 vs 모달 로그인 vs 패스키 생성 vs 조건부 생성)에 따라 동일한 오류가 다른 의미를 갖는 이유는 무엇일까요?
  • "실패했습니다"가 재현 가능한 문제가 되도록 하기 위해 캡처해야 하는 최소 컨텍스트는 무엇일까요?
핵심 정보
  • NotAllowedError는 근본 원인이 아니라 표면적 신호입니다. 컨텍스트에 따라 취소, 시간 초과, "로컬 자격 증명 없음" 또는 누락된 사용자 활성화를 의미할 수 있습니다.
  • 작업 유형에 따라 의미가 달라집니다. 동일한 NotAllowedError라도 조건부 UI 로그인, 모달 로그인, 수동 패스키 생성, 조건부 생성 및 자동 트리거 추가 중에 다른 의미를 갖습니다.
  • 작업 시작 후 타이밍은 가장 과소평가된 신호입니다. 즉시(<1초), 사용자 취소(1~15초), 시간 초과(30초 이상)는 근본적으로 다른 범주입니다.
  • AbortError는 일반적으로 수명 주기/동시성 문제(탐색, 리렌더링, 진행 중인 여러 요청)입니다.
  • SecurityError는 거의 항상 구성/컨텍스트 문제이며 성숙한 프로덕션 배포에서는 드뭅니다.
  • "브라우저 오류 이름"과 "서버 확인 거부"는 다른 계층입니다. 서버 거부를 일반적인 클라이언트 실패가 아닌 명시적인 코드로 추적하세요.
  • 원시 오류 이름만으로는 조치할 수 없습니다. 오류를 실제로 수정할 수 있는 버킷으로 분류할 수 있도록 항상 error.name과 함께 작업 유형, 타이밍, 플랫폼 컨텍스트를 캡처하세요.
  • "환경"은 단순한 브라우저 + OS 이상입니다. 오류를 진정으로 이해하려면 OS 버전, 클라이언트(브라우저/앱 버전), 인증자 설정(예: iCloud/GPM 상태) 및 특정 하드웨어 모델의 전체 조합을 추적해야 합니다.
  • 로그인 실패는 P1이고 생성 실패는 P2입니다. 생성 오류(P2)는 사용자 포기로 인해 더 많이 발생하지만 로그인 오류(P1)는 액세스를 차단하므로 즉각적인 경고가 필요합니다.

2. 프로덕션 치트 시트#

디버깅을 풀기 위한 빠른 매핑이 필요하다면 이 표부터 시작하세요. 이는 팀이 대시보드와 지원 티켓에서 실제로 보는 내용에 맞춰져 있습니다.

error.name프로덕션 환경에서 보통 의미하는 것확인할 사항첫 번째 조치(UX + 엔지니어링)
NotAllowedError사용자가 시트를 닫았거나, 시간이 초과되었거나, 가용성 불일치가 하나의 버킷으로 축소되었습니다. 이는 프로덕션에서 가장 큰 오류 버킷입니다.발생까지의 시간, QR/하이브리드 UI가 표시되었는지 여부, 세레모니가 실제 사용자 작업에서 시작되었는지 여부예상된 동작으로 취급: UI 복원 + 대체 수단 표시
AbortError앱(또는 브라우저)이 세레모니를 중단했습니다.세레모니 중 탐색/리렌더링; 동시 WebAuthn 호출; AbortController.abort()진행 중인 요청을 하나만 허용하도록 강제; 경로 변경 방지; 중단을 정상적인 제어 흐름으로 처리
SecurityError컨텍스트/정책이 허용되지 않습니다.출처 + RP ID 전략; iframe/임베딩; HTTPS; 기능 정책RP ID/출처 구성 수정; 임베딩 정책 검증; 보안 컨텍스트 보장
InvalidStateError상태 불일치(보통 중복 등록)등록 vs 로그인; excludeCredentials; 인증자에 기존 자격 증명 존재"이미 등록됨"으로 취급; UX 경로 조정; 옵션 생성 수정
ConstraintError요구 사항을 충족할 수 없습니다.authenticatorAttachment, userVerification, 상주 키 요구 사항제약을 완화하거나 대체 경로/대체 수단 제공. 예: Android에서 화면 잠금 누락
DataError입력이 잘못되었거나 일치하지 않습니다.base64url 인코딩; ID/챌린지/사용자 핸들 형식인코딩/직렬화 수정; 옵션 생성에 유효성 검사 추가
NotSupportedError플랫폼/브라우저가 요청한 내용을 지원하지 않습니다.OS/브라우저 버전; 기능 감지 가정즉시 대체 수단으로 전환; 세그먼트 기록; 지원되지 않는 환경에서는 패스키 CTA 표시 방지
UnknownError플랫폼/인증자가 일반적인 방식으로 실패했습니다.OS 업데이트 후 급증; 기기 빌드; 자격 증명 관리자 공급자 문제재시도 친화적인 UX; 빌드 번호 캡처; 세그먼트 급증 조사

놓치기 쉬운 한 가지 사실은 작업 유형에 따라 동일한 error.name이 매우 다른 의미를 가질 수 있다는 것입니다. 아래 섹션을 읽을 때 작업 컨텍스트를 염두에 두세요. 실제로 패스키 생성(등록) 오류는 일반적으로 로그인 오류보다 훨씬 많습니다. 위의 표는 둘 다에 적용되지만 생성은 대부분의 볼륨이 있는 곳입니다.

다음으로, 가장 많이 볼 수 있고 팀에서 가장 자주 오해하는 오류인 NotAllowedError에 대해 더 깊이 살펴보겠습니다.

3. NotAllowedError: 작업이 시간 초과되었거나 허용되지 않았습니다#

NotAllowedError는 종종 "패스키 실패"처럼 보이지만, 보통 사용자가 OS UI를 완료하지 않았다는 플랫폼의 알림입니다. 핵심은 조치할 수 있는 버킷으로 나누는 것입니다.

브라우저 콘솔에서 표시되는 내용:

출처오류 메시지
Chrome, EdgeNotAllowedError: The operation either timed out or was not allowed. See: https://www.w3.org/TR/webauthn-2/#sctn-privacy-considerations-client.
Safari, WebKitNotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.
Safari, WebKitNotAllowedError: This request has been cancelled by the user.
Chrome, EdgeNotAllowedError: The operation is not allowed at this time because the page does not have focus.
Safari, WebKitNotAllowedError: The document is not focused.
FirefoxNotAllowedError: Operation failed.

이들 모두는 error.name === "NotAllowedError"로 나타납니다. error.message는 브라우저 엔진과 근본 원인에 따라 다르지만 결과는 동일합니다. 즉, 세레모니가 완료되지 않았습니다.

이것은 로그인 및 패스키 생성 모두에 적용됩니다. 로그인 중(조건부 UI, allowList가 있거나 없는 모달)에 NotAllowedError는 보통 사용자가 세레모니를 완료하지 않았음을 의미합니다. 패스키 생성 중에는 동일한 오류가 다른 이유로 발생합니다. 사용자가 생성 대화 상자를 닫았거나(조건부 생성이 작동하지 않음), 자동 트리거 추가 중에 페이지가 포커스를 잃었습니다. 작업 유형에 따라 오류가 의미하는 바와 취해야 할 조치가 달라집니다.

타이밍은 종종 과소평가되는 신호입니다. 클릭 후 1초 미만의 오류는 보통 즉각적인 거부(환경에서 할 수 없음, 문서에 포커스가 없음, 기능 누락)를 의미합니다. 몇 초 후의 오류는 사용자 취소입니다(사용자가 대화 상자를 보고 계속 진행하지 않기로 결정함). 30초 이상 지난 후의 오류는 시간 초과입니다. 네이티브 플랫폼에서 타이밍은 특히 중요합니다. 인증자 왕복, 생체 인식 프롬프트, 자격 증명 관리자 핸드오프에는 모두 "작동하지 않음"과 "사용자가 자리를 비움"을 구분하는 데 도움이 되는 고유한 기간이 있습니다. 여전히 패스키가 있었는지 쉽게 구분할 수는 없습니다.

3.1 컨텍스트로 구분하기#

완벽한 신호가 필요한 것은 아닙니다. 모든 NotAllowedError를 동일하게 취급하지 않을 만큼의 충분한 컨텍스트가 필요합니다. iOS/Safari는 고유한 제약 조건(이전 버전의 사용자 활성화 요구 사항)이 있기 때문에 아래에서 특별한 주의를 기울이지만, 원시 오류 볼륨에서는 Windows와 Chromium 브라우저가 종종 다른 플랫폼보다 더 많은 NotAllowedError를 생성합니다. 이러한 신호는 대개 문제의 80%를 해결해 줍니다.

신호예상되는 의미다음 조치
즉시 실패(<1초)환경 거부: 기능 없음, 문서에 포커스가 없음, 조건부 생성 표면을 사용할 수 없음기능 감지 확인; 문서에 포커스가 있는지 확인; 이 플랫폼에서 작업이 지원되는지 확인
빠른 취소(1~3초)깜짝 프롬프트 / 컨텍스트 없음프롬프트 타이밍 변경; 취소 후 쿨다운 추가
사용자 시간 내 취소(3~15초)사용자가 대화 상자를 보고 계속 진행하지 않기로 결정함예상된 UX; UI 복원 + 대체 수단 표시
시간 초과(30초 이상)사용자 작업 없이 세레모니 시간 초과"완료되지 않음"으로 분류; 프롬프트를 보았는지 고려
실패하기 전에 QR/하이브리드 UI 표시이 기기에서 사용할 수 있는 로컬 자격 증명이 없음. 참고: QR 코드 결정이 발생하기 전에 안정적으로 감지하려면 현재 기기에 사용 가능한 자격 증명이 있는지 여부를 아는 패스키 인텔리전스 계층이 필요합니다.패스키 제안 게이팅; "전화 사용" 명시화; 깜짝 QR 줄이기
iOS/Safari에 집중되고 클릭/탭 없이 트리거됨누락된 사용자 활성화실제 사용자 제스처에서 세레모니 시작
조건부 생성 또는 자동 트리거 추가 중자동 완성을 사용할 수 없거나, 자격 증명이 이미 있거나, 페이지가 포커스를 잃음. 기능 출시 시 조건부 생성 오류가 갑자기 대량으로 나타날 수 있으며, 하룻밤 사이에 오류의 가장 큰 원인 중 하나가 됩니다.조건부 생성 참조; 문서 가시성 상태 확인; 호출을 시도하기 전에 getClientCapabilities()를 사용하여 conditionalCreate 지원 확인

이것이 NotAllowedError가 사용자에게 거의 노출되지 않아야 하는 이유이기도 합니다. 사용자가 조치할 수 있는 메시지가 아닙니다.

컨텍스트 분류는 성공률이 가장 크게 나뉘는 곳이기도 합니다. Corbado Passkey Benchmark 2026 패스키 인증 성공률 분석은 대규모 B2C 배포 전반에 걸쳐 식별자 우선(identifier-first) 알 수 없는 기기 흐름에 대해 2026년 1분기 완료율을 5595%로, 알려진 기기 반환의 경우 9599%로 측정합니다. iOS 웹 식별자 우선 완료율은 8595%(CDA 비율 05%), Android 웹은 7085%(CDA 비율 510%), macOS 웹은 7085%(CDA 비율 1015%)에 달하는 반면, Windows 웹은 식별자 우선 완료율이 4560%이며 Windows 11에서 CDA 비율은 5565%, Windows 10에서는 40~55%입니다. 알려진 기기와 알 수 없는 기기 컨텍스트를 분리하지 않고 NotAllowedError 볼륨을 읽는 것은 근본적으로 다른 두 성공 체제를 하나로 혼합하는 것입니다.

프로덕션에서 놓치기 쉬운 뉘앙스: 일부 사용자 에이전트는 시간 초과를 TimeoutError로 표시할 수 있지만, 많은 팀에서 시간 초과가 대시보드에서 NotAllowedError로 축소되는 것을 볼 수 있습니다. 어느 쪽이든 시간 초과를 "세레모니가 완료되지 않음"으로 간주하고 타이밍과 컨텍스트를 사용하여 분류하세요.

3.2 UX 처리: 취소를 정상적인 종료로 만들기#

OS 시트가 해제되거나 시간이 초과되면 UI가 즉시 복구되고 정상적으로 반응해야 합니다. 실용적인 규칙 세트:

  • 로그인 UI 복원(계속 실행되는 스피너 없음)
  • 식별자 상태 유지(재입력 강제 금지)
  • 동일한 화면에 눈에 띄는 대체 수단 표시
  • 예상된 결과에 대해 무서운 배너 방지

기본 그 이상:

  • 첫 번째 중단을 정상으로 취급하고 차분한 설명과 함께 재시도를 제안합니다. 두 번째 중단 후에만 대체 옵션을 제안해야 합니다(패스키 대체 및 복구 참조).
  • 깜짝 놀란 사용자에게 두 번째 기회가 주어지도록 쿨다운 전 최대 세 번의 생성 프롬프트를 허용합니다(패스키 생성 모범 사례 참조).
  • 유사한 항목을 비교할 수 있도록 생성과 로그인 간의 오류 수를 분할합니다.
  • OS, 브라우저 및 기기별로 오류율을 분류하여 마찰이 실제로 어디에 존재하는지 파악할 수 있습니다(패스키 로그인 모범 사례 참조).

"취소"가 진정으로 많다면 다음 단계는 그 이면에 있는 근본 원인(프롬프트 타이밍, 깜짝 QR, 낮은 가용성)을 수정하는 것입니다.

3.3 NotAllowedError를 줄이는 엔지니어링 수정#

지표를 빠르게 이동시키는 변경 사항부터 시작하세요.

  • 프롬프트 타이밍 및 사용자 활성화(특히 iOS/Safari에서) 수정: Safari WebAuthn 사용자 활성화 이벤트.
  • 깜짝 QR/하이브리드 감소: 패스키 흐름에 QR 코드가 표시되고 사용자가 포기하는 이유.
  • 성공할 가능성이 높을 때 패스키가 표시되도록 제안 게이팅: 성공할 가능성이 높을 때만 패스키 제안.
  • 로컬 자격 증명이 없을 때 프롬프트를 피하는 패턴 사용: WebAuthn 즉시 중재.
  • 네트워크 불안정성 처리: 확인 중 네트워크 오류는 종종 일반적인 클라이언트 측 오류로 나타납니다. 원격 분석 로그에 대한 오프라인 큐를 구현하여 세레모니 중에 사용자의 연결이 끊어지더라도 오류 데이터를 잃지 않도록 합니다.
  • 감지 API를 사용하여 세레모니 게이팅: 환경 오류로 인해 NotAllowedError 버킷이 부풀려지지 않도록 합니다. 가장 기본적인 게이트로 isUVPAA()로 시작한 다음, 더 세밀한 확인(조건부 생성, 조건부 가져오기, 하이브리드 전송, 플랫폼 인증자)을 위해 getClientCapabilities()를 사용합니다. 감지 API가 OS 업데이트로 인해 깨질 수 있다는 점에 유의하세요. iOS 26.2에는 패스키가 정상적으로 작동함에도 불구하고 모든 WKWebView 기반 브라우저에서 isUVPAA()false를 반환하는 WebKit 버그가 포함되어 출시되어 iOS 사용자의 10-25%에서 갑작스러운 NotAllowedError 급증을 초래했습니다(getClientCapabilities()로 패스키 감지 수정 참조).

3.4 진화하는 오류 이름에 대한 참고 사항#

오류 이름은 계속 변하는 목표입니다. 더 세분화된 WebAuthn 오류를 추가하기 위한 제안("사용 가능한 자격 증명 없음"을 "사용자 취소"와 분리하기 위해)이 진행 중입니다. 2026년 2월 현재 이는 어느 브라우저에도 구현되지 않았으므로 컨텍스트와 타이밍을 기반으로 자체적인 이유 버킷을 구축하는 것이 여전히 가치가 있습니다. 이 작업을 추적하려면 WebAuthn 문제 #2062"새로운 오류 코드" 설명자를 참조하세요.

나머지 오류 이름은 빈도는 적지만 나타날 때 이해할 가치가 있습니다.

4. AbortError: 작업이 중단되었습니다#

AbortErrorNotAllowedError와 비교하여 볼륨 측면에서 드물지만 발생하면 매우 진단적입니다. 이는 일반적으로 앱이 요청을 무효화했기 때문에 세레모니가 완료되지 않았음을 의미합니다(탐색 발생, 상태 변경 또는 두 번째 요청 시작).

브라우저 콘솔에서 표시되는 내용:

출처오류 메시지
Chrome, EdgeAbortError: The operation was aborted.
Chrome, EdgeAbortError: Aborted by AbortSignal.
FirefoxAbortError: signal is aborted without reason
FirefoxAbortError: Operation timed out.
Safari, WebKitAbortError: The user aborted a request.
ChromeAbortError: CredentialContainer request is not allowed.

일반적인 프로덕션 원인:

  • 여러 동시 WebAuthn 호출(두 프롬프트 경합)
  • 진행 중인 세레모니 중 경로 변경/리렌더링
  • 재시도 또는 상태 정리 중 AbortController.abort() 호출

이를 해결하려면 세레모니를 "임계 영역"으로 만드는 데 집중하세요.

  • 한 번에 하나의 진행 중인 요청만 허용
  • 세레모니 중 탐색 차단(또는 정상적으로 취소하고 UI 복원)
  • 중단을 예상된 제어 흐름으로 처리: 재시도 버튼과 대체 방법 표시

임베디드 표면이나 다중 도메인 앱에 집중된 AbortError가 표시되는 경우 확인해야 할 다음 버킷은 SecurityError입니다.

5. SecurityError: TLS 인증서 오류가 있는 사이트에서는 WebAuthn이 지원되지 않습니다#

SecurityError는 브라우저가 "이 컨텍스트는 요청한 작업을 수행하도록 허용되지 않습니다."라고 알려주는 것입니다. 실제로 이는 거의 항상 사용자 행동이 아닌 구성 문제입니다. 성숙한 프로덕션 배포에서 SecurityError는 드뭅니다. 이러한 문제는 통합 테스트 중에 일반적으로 포착되기 때문입니다. 프로덕션 환경에 나타난다면 대개 적절한 WebAuthn 구성 없이 새 도메인, 임베딩 컨텍스트 또는 배포 대상이 추가되었음을 의미합니다.

일반적인 원인:

  • RP ID / 출처 불일치(다중 도메인 설정)
  • 교차 출처 임베딩 제한(iframe)
  • 안전하지 않은 컨텍스트(HTTPS가 아님) 또는 차단된 권한/정책
  • .well-known/webauthn 또는 .well-known/assetlinks.json이 잘못 구성되었거나, 없거나, 일시적으로 사용할 수 없음. 브라우저가 이러한 파일을 가져오는 임계 창 동안의 네트워크 문제는 실패를 유발합니다. 일반적인 사각지대: 홈페이지가 유지 보수를 위해 다운된 경우 well-known 파일도 오프라인이 되어 이를 사용하는 모든 의존 당사자의 패스키 세레모니가 중단됩니다.

브라우저 콘솔에서 표시되는 내용:

출처오류 메시지
Chrome, EdgeSecurityError: WebAuthn is not supported on sites with TLS certificate errors.
모든 브라우저SecurityError: The relying party ID is not a registrable domain suffix of, nor equal to the current origin's effective domain.
Chrome(iframe)SecurityError: The 'publickey-credentials-create' feature is not enabled in this document.

프로덕션에서 SecurityError는 드뭅니다. 이들은 거의 항상 통합 테스트 중에 포착됩니다. 이들이 나타날 때 가장 흔하게 살아남는 것은 TLS 인증서 오류입니다.

가장 빠른 디버깅 루프는 다음과 같습니다.

  • 사용한 출처와 RP ID 입력을 기록합니다.
  • 동일한 컨텍스트(최상위 vs iframe, 프로덕션 도메인 vs 스테이징)에서 재현합니다.
  • 로그인을 포함하는 경우 권한 정책이 구성되어 있는지 확인합니다(예: publickey-credentials-create / publickey-credentials-get): MDN Permissions-Policy
  • 도메인 전략을 검증합니다(관련 출처 참조).
  • iframe을 사용하는 경우 기능 정책을 검증합니다(iframe 패스키 참조).

SecurityError가 처리되고 나면 심각하게 다루어야 할 다음 버킷은 종종 구현 버그를 나타내는 오류인 InvalidStateError, ConstraintErrorDataError입니다.

PasskeysCheatsheet Icon

Passkeys 치트시트. passkey 프로그램을 위한 실무 가이드, 도입 패턴, KPI.

치트시트 받기

6. InvalidStateError, ConstraintError, DataError: 구현 버그로 취급#

이러한 오류는 성숙한 패스키 구현에서는 드물어야 합니다. 이들이 나타나면 일반적으로 세그먼트에 대한 옵션 생성이 잘못되었거나 흐름이 잘못된 상태임을 나타냅니다.

6.1 InvalidStateError: "의존 당사자에 이미 등록된 자격 증명"#

브라우저 콘솔에서 표시되는 내용:

출처오류 메시지
Safari, WebKitInvalidStateError: The user attempted to register an authenticator that contains one of the credentials already registered with the relying party.
Chrome, EdgeInvalidStateError: At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator.
Chrome, EdgeInvalidStateError: A request is already pending.
FirefoxInvalidStateError: An attempt was made to use an object that is not, or is no longer, usable

일반적인 의미:

  • 등록: 이미 존재하는 자격 증명(중복)을 생성하려고 시도했습니다.
  • 로그인: 덜 일반적입니다. 종종 플랫폼의 흐름/상태가 일치하지 않음을 의미합니다.

실용적인 처리:

  • 수동 등록에서 발생하면 "이미 등록됨"으로 취급하고 적절히 라우팅합니다.
  • 인증자가 중복을 감지할 수 있도록 excludeCredentials가 사용자의 모든 기존 자격 증명 ID를 나열하는지 확인합니다(excludeCredentials 참조).
  • 조건부 생성 중 InvalidStateError는 예상된 것이며 조용히 무시해야 합니다. 이는 제공자에 패스키가 이미 존재함을 의미합니다. 조건부 생성 중에 NotAllowedErrorAbortError에도 동일하게 적용됩니다(Chrome의 조건부 생성 참조).

6.2 ConstraintError#

일반적인 의미: 인증자가 요청한 제약 조건을 충족할 수 없습니다.

일반적인 트리거:

  • 기기 화면 잠금 누락(특히 Android): 플랫폼에는 생체 인식 또는 PIN 확인이 필요하지만 기기에 잠금 화면이 구성되어 있지 않습니다.
  • 너무 엄격한 authenticatorAttachment 또는 상주 키 가정
  • 사용할 수 없는 세그먼트의 엄격한 userVerification 요구 사항

수정: (허용 가능한 경우) 제약을 완화하거나 대체 경로를 제공합니다. 누락된 화면 잠금의 경우, 조용히 실패하는 대신 이 조건을 감지하고 사용자를 안내하는 것을 고려하세요(Android).

6.3 DataError#

일반적인 의미: 입력이 잘못되었거나 일치하지 않습니다.

일반적인 트리거:

  • 인코딩 실수(base64 vs base64url)
  • 잘못된 자격 증명 ID / 챌린지 형식

수정: WebAuthn 옵션을 생성하는 경계에서 입력을 검증하고 정규화합니다. 실제로 성숙한 프로덕션 시스템에서 DataError는 사실상 없습니다. 옵션 생성을 테스트했다면 대시보드에서 이를 볼 수 없을 것입니다.

이러한 오류가 통제되고 있다면 다음 질문은 적용 범위입니다. 환경이 예상대로 WebAuthn을 수행할 수 없기 때문에 사용자가 실패하고 있습니까?

7. NotSupportedError: 사용자 에이전트가 공개 키 자격 증명을 지원하지 않습니다#

NotSupportedError는 신뢰성 신호가 아니라 범위 신호입니다. 대개 세그먼트가 요청한 작업을 수행할 수 없음을 의미합니다(너무 오래된 OS/브라우저, 기능 누락, 기능이 활성화되지 않음).

브라우저 콘솔에서 표시되는 내용:

출처오류 메시지
Chrome, EdgeNotSupportedError: The user agent does not support public key credentials.
FirefoxNotSupportedError: Resident credentials or empty 'allowCredentials' lists are not supported.
Chrome, Edge, FirefoxTypeError: PublicKeyCredential.parseCreationOptionsFromJSON is not a function
Chrome, Edge, FirefoxTypeError: PublicKeyCredential.parseRequestOptionsFromJSON is not a function
Chrome, Edge, FirefoxTypeError: credential.toJSON is not a function
SafariTypeError: Can only call PublicKeyCredential.toJSON on instances of PublicKeyCredential

처음 두 개는 순수한 NotSupportedError DOMException입니다. TypeError 항목은 기술적으로 예외 유형이 다르지만 브라우저나 환경이 요청한 것을 지원하지 않는다는 동일한 클래스의 문제를 나타냅니다. JSON 직렬화 TypeError 제품군은 실제 NotSupportedError DOMException 자체보다 훨씬 더 일반적입니다(아래 참조).

일반적인 원인:

  • 기본 WebAuthn을 지원하지 않는 오래된 브라우저/OS 버전
  • 해당 플랫폼에서 사용할 수 없는 WebAuthn 기능 요청
  • 지원되지 않는 기기에서 플랫폼 특정 흐름 시도

JSON 직렬화 제품군은 프로덕션 환경에서 NotSupportedError 클래스 오류의 가장 큰 원인입니다. 기술적으로 이러한 오류는 DOMException이 아닌 TypeError(메서드 누락)로 나타나지만, 여기에서 발생합니다. 두 가지 뚜렷한 근본 원인:

  1. 브라우저가 WebAuthn JSON 직렬화 메서드를 지원하지 않습니다. 브라우저에 navigator.credentials는 있지만 PublicKeyCredential.parseCreationOptionsFromJSON / parseRequestOptionsFromJSON은 없습니다. 이는 이전 버전의 Safari와 Chrome에 집중되어 이 오류 제품군의 대략 90%를 차지합니다. 클라이언트 라이브러리가 대체 없이 이러한 메서드에 의존하는 경우 상당한 오류 볼륨이 생성됩니다.
  2. 암호 관리자 확장 프로그램이 .toJSON()을 중단합니다. Bitwarden, LastPass 또는 1Password와 같은 확장은 세레모니를 가로채고 자격 증명처럼 보이지만 실제 PublicKeyCredential 인스턴스가 아닌 객체를 반환할 수 있습니다. 해당 객체에 .toJSON()을 호출하면 throw하거나 undefined를 반환하거나 객체가 완전히 null이 됩니다. 이는 제품군의 대략 10%를 차지하지만 오류 메시지가 브라우저(Safari: "PublicKeyCredential의 인스턴스에서만 호출할 수 있음"; Firefox: "PublicKeyCredential 인터페이스를 구현하지 않음")마다 다르기 때문에 디버깅하기가 특히 혼란스럽습니다.

처리는 직설적이고 빨라야 합니다.

  • 즉시 대체 비밀번호/OTP로 전환합니다.
  • 적용 범위 격차를 정량화할 수 있도록 세그먼트를 기록합니다.
  • 일관되게 실패하는 세그먼트에는 패스키 CTA를 표시하지 않습니다.

적용 범위가 양호해 보이지만 특정 세그먼트에서 여전히 실패가 발생하면 UnknownError로 나타나는 플랫폼 계층 문제를 다루고 있을 수 있습니다.

8. UnknownError: 자격 증명 관리자와 통신하는 동안 알 수 없는 오류가 발생했습니다#

UnknownError는 다른 범주에 깔끔하게 매핑되지 않는 인증자/OS 실패에 대한 포괄적인 오류입니다. 이는 일시적인 경우가 많지만 OS 업데이트 후에 급증할 수도 있습니다.

브라우저 콘솔에서 표시되는 내용:

출처오류 메시지
Chrome(Android)UnknownError: An unknown error occurred while talking to the credential manager.
모든 브라우저UnknownError: The operation failed for an unknown transient reason.
모든 브라우저UnknownError: Either the device has received unexpected request data, or the device has been reconfigured since the request was made.
모든 브라우저UnknownError: Something went wrong.
Chrome(LastPass)TypeError: Cannot use 'in' operator to search for 'type' in null
Safari(LastPass)TypeError: null is not an Object. (evaluating 'key in input')
Chrome(Bitwarden)FallbackRequested

실용적인 처리:

  • 재시도 친화적인 UX 사용("사용자를 탓하지 않음")
  • 가능한 경우 OS/빌드 번호 및 자격 증명 관리자/제공자 컨텍스트 캡처
  • OS 업데이트 후 세그먼트별 급증 주시

DOMException 범주에 깔끔하게 들어맞지 않는 틈새 오류 원인 중 하나: 암호 관리자 브라우저 확장 프로그램(Bitwarden, LastPass, 1Password 및 기타 등)은 WebAuthn API 호출을 가로채고 비표준 응답을 반환할 수 있습니다. 사용자 취소에 비해 규모는 작지만 일관되게 특정 사용자 세그먼트에 영향을 미치고 증상이 혼란스럽기 때문에 추적할 가치가 있습니다. 반환된 자격 증명 객체의 메서드 누락, 예기치 않은 오류 유형 또는 문서화된 WebAuthn 오류와 일치하지 않는 형식이 잘못된 응답 등이 있습니다. 이들은 종종 UnknownError 또는 분류되지 않은 예외로 나타납니다. OS 수준의 설명이 없는 특정 브라우저에 집중된 오류 급증이 나타나면 자격 증명 관리자 확장 프로그램이 관련되어 있는지 확인하세요.

지금까지 웹 브라우저 오류 이름을 다뤘습니다. 하지만 네이티브 앱도 제공한다면 오류 환경이 달라지며 어떤 면에서는 훨씬 좋습니다.

9. 네이티브 앱(iOS 및 Android)에 대한 한마디#

위의 모든 내용은 웹 브라우저를 다룹니다. 네이티브 앱 - ASAuthorization 프레임워크를 사용하는 iOS, Credential Manager를 사용하는 Android - 은 동일한 기본 오류 범주를 공유하지만 중요한 면에서 다릅니다.

  1. "자격 증명 없음"은 구별되는 신호입니다. 웹에서 브라우저는 개인 정보 보호를 위해 "사용할 수 있는 자격 증명 없음"과 "사용자 취소"를 동일한 NotAllowedError로 축소합니다. 네이티브의 경우 iOS(ASAuthorizationController)에서 preferImmediatelyAvailableCredentials를 사용하거나 Android(GetCredentialRequest)에서 setPreferImmediatelyAvailableCredentials(true)를 사용하면 기기에 이미 있는 자격 증명만 제시하고 없으면 즉시 실패하도록 OS에 지시합니다. 이를 통해 웹에서는 제공할 수 없는 깔끔한 "자격 증명 없음" 반환을 얻을 수 있습니다.

  2. 자격 증명 제공자 상태가 보입니다. 네이티브 플랫폼에서는 일부 조건에서 자격 증명 제공자(Google Password Manager, iCloud Keychain, 1Password 등)가 설치, 구성 또는 기본값으로 설정되지 않은 시기를 알려주고 이에 대응할 수 있습니다. 웹에서 이 정보는 불투명한 NotAllowedError 메시지 뒤에 숨겨져 있습니다.

  3. 오류 메시지가 더 구체적입니다. 사용자가 앱을 설치하여 의존 당사자와 신뢰 관계를 구축했기 때문에 OS는 더 구체적인 진단 세부 정보를 제공합니다. 웹 브라우저를 모호하게 만드는 개인 정보 보호 고려 사항은 앱이 이미 기기에 있는 경우 동일한 방식으로 적용되지 않습니다. iOS는 사용자의 기기 언어로 현지화된 메시지를 반환합니다. Android는 원인 체인과 함께 구조화된 오류 유형을 반환합니다. 이로 인해 디버깅이 쉬워지지만 오류 처리는 현지화 및 플랫폼별 오류 형식을 고려해야 합니다.

9.1 iOS(ASAuthorization 프레임워크)#

iOS는 ASAuthorization 프레임워크를 통해 패스키 오류를 표시합니다. 모든 오류는 authorizationController(controller:didCompleteWithError:) 델리게이트 콜백에 NSError 객체로 도착합니다.

메시지 문자열이 아닌 도메인 + 코드로 분류하세요. 기본 오류 도메인은 com.apple.AuthenticationServices.AuthorizationError(ASAuthorizationError.errorDomain)입니다. 오류를 let nsError = error as NSError로 캐스트하고 .domain.code에서 일치시킵니다. 프로덕션에서는 절대로 .localizedDescription과 일치시키지 마세요. Apple의 메시지는 30개 이상의 언어로 현지화되며 OS 버전에 따라 변경될 수 있습니다. 아래 나열된 메시지 문자열은 로그에서 오류를 인식하는 데 유용하지만 분류 기준은 아닙니다.

공개 ASAuthorizationError 코드:

코드이름버전의미
1000UnknowniOS 13프로덕션에 표시되지 않아야 합니다. 일반적인 포괄적 오류입니다.
1001CancelediOS 13사용자가 패스키 시트를 해제했습니다. 전반적으로 가장 일반적인 오류 - NotAllowedError와 동등합니다. userInfo가 비어 있고 근본적인 오류가 없는 깔끔한 신호입니다.
1002InvalidResponseiOS 13프레임워크 수준의 손상. 실제로 드뭅니다.
1003NotHandlediOS 13요청을 처리한 제공자가 없습니다. 권한과 자격 증명 제공자 구성을 확인하세요.
1004FailediOS 13일반적인 실패. localizedDescription에는 실제 이유(예: "식별자가 X인 응용 프로그램이 도메인 Y와 연결되어 있지 않습니다")가 포함됩니다. userInfo에는 FailureReason 문자열이 포함될 수 있지만 NSUnderlyingErrorKey가 항상 채워지는 것은 아닙니다. 도메인 연결 실패는 기본 오류에 대해 nil을 반환합니다.
1005NotInteractiveiOS 15preferImmediatelyAvailableCredentials를 사용할 때 사용할 수 있는 자격 증명이 없습니다. 이것은 깔끔한 "찾을 수 없음" 신호로, "이 기기에 패스키가 존재하지 않음"의 iOS 버전입니다. UI가 표시되지 않았습니다.
1006MatchedExcludedCredentialiOS 18이 기기의 이 RP에 대한 패스키가 이미 존재합니다. 중복 감지를 위한 깔끔한 신호 - 빈 userInfo, NSUnderlyingErrorKey 없음. 문자열 매칭 없이 분류가 작동합니다.
1007CredentialImportiOS 18.2자격 증명 가져오기 실패.
1008CredentialExportiOS 18.2자격 증명 내보내기 실패.
1009PreferSignInWithAppleiOS 26사용자가 패스키보다 Apple로 로그인을 선호합니다. iOS 26에 새로 도입되었습니다.
1010DeviceNotConfiguredForPasskeyCreationiOS 26기기에 암호 또는 iCloud Keychain 구성이 부족합니다. 알려진 iOS 26 시뮬레이터 버그: 구성이 올바른 경우에도 1010을 반환합니다(물리적 기기에서는 재현되지 않음).

프로덕션에서 가장 중요한 차이점: iOS 18 이후로 중복 자격 증명은 자체 오류 코드 1006(MatchedExcludedCredential)을 반환합니다. iOS 17 이전에서는 중복 자격 증명이 코드 1004(Failed) 안에 숨겨져 있었습니다. iOS 18 이상에서는 그 차이가 텍스트가 아닌 구조적(다른 오류 코드)입니다.

일반적인 런타임 오류(로그 수준 참조):

이러한 메시지는 특정 실패 시나리오의 localizedDescription 또는 userInfo에 나타납니다. 프로그램 방식의 분류가 아닌 로그 검색 및 디버깅에 사용하세요.

메시지(영어 로케일)기본 코드참고
Application with identifier <TeamID.BundleID> is not associated with domain X1004 (Failed)앱의 Associated Domains 권한이 의존 당사자와 일치하지 않습니다. 서버의 apple-app-site-association 파일을 수정하세요.
Couldn't communicate with a helper application.1004 (Failed)자격 증명 제공자 확장이 응답하지 못했습니다. 일시적 - 재시도가 적절합니다.
Request already in progress for specified application identifier.1004 (Failed)하나가 진행 중인 동안 중복된 ASAuthorization 요청이 발생했습니다. 앱의 경쟁 상태입니다.
Stolen Device Protection is enabled and biometry is required.1004 (Failed)iOS 17+ 도난당한 기기 보호는 낯선 위치에서 생체 인식 인증을 차단합니다. 개발자가 조치할 수는 없지만 사용자에게 표시할 가치가 있습니다.
(AuthenticationServicesCore.ASCABLEClient.ClientError error 2.)별도 도메인교차 기기 인증(하이브리드/CABLE) Bluetooth 핸드셰이크 실패.
(AuthenticationServicesCore.ASCABLEClient.ClientError error 3.)별도 도메인교차 기기 인증 Bluetooth 연결 실패.

현지화된 "자격 증명 없음" 메시지(코드 1005):

preferImmediatelyAvailableCredentials가 설정되고 패스키가 없으면 iOS는 사용자의 기기 언어로 현지화된 메시지와 함께 코드 1005(NotInteractive)를 반환합니다. 이것은 네이티브 앱에 고유합니다 - 웹 브라우저는 이 신호를 절대 노출하지 않습니다. 메시지는 항상 The operation couldn't be completed.로 시작하고 그 뒤에 현지화된 텍스트가 옵니다:

언어메시지
중국어(간체)没有可用于登录的凭证。
베트남어Không có sẵn thông tin để đăng nhập.
아랍어لا تتوفر بيانات اعتماد لتسجيل الدخول.
스페인어No hay ninguna credencial disponible para iniciar sesión.
중국어(번체)沒有可用於登入的憑證。
한국어로그인을 위한 자격 증명이 없습니다.
프랑스어(캐나다)Aucun identifiant disponible pour la connexion.
포르투갈어(브라질)Nenhuma credencial disponível para login.
프랑스어(프랑스)Aucune information d'identification n'est disponible pour procéder à la connexion.
태국어ไม่มีข้อมูลประจำตัวสำหรับเข้าสู่ระบบ
이탈리아어Non ci sono credenziali disponibili per l'accesso.
네덜란드어Geen inloggegevens beschikbaar.
일본어ログイン用の資格情報がありません。
터키어Oturum açmak için kullanılabilecek kimlik bilgisi yok.

영어 로케일 기기는 일반적으로 ASAuthorization 프레임워크가 현지화된 오류를 반환하기 전에 API 수준에서 "자격 증명 없음"을 해결하므로 위에는 영어 변형이 나타나지 않습니다. 프로그래밍 방식으로 이러한 문자열을 파싱하는 대신 항상 코드 1005에서 일치시킵니다.

9.2 Android(Credential Manager API)#

Android는 Credential Manager API(androidx.credentials)를 통해 패스키 오류를 표시합니다. 오류 메시지에는 기본 메시지와 종종 추가 세부 정보가 있는 cause가 포함됩니다. iOS와 비교할 때 Android는 더 구조화된 오류 유형과 구성 문제에 대한 더 명시적인 원인을 제공합니다.

사용자 취소 및 자격 증명 감지:

오류참고
User cancelled the operation사용자가 패스키 프롬프트를 해제했습니다. NotAllowedError와 동등합니다. 참고: 자격 증명 관리자는 또한 다른 코드 경로에서 User canceled the request(미국 철자법)를 반환합니다. 둘 다 동일합니다.
Excluded credential matches existing credential이 자격 증명 ID에 대한 패스키가 이미 존재합니다. InvalidStateError와 동등합니다. iOS와 달리 메시지가 사용자 취소와 다릅니다.
No create options available.적절한 자격 증명 제공자가 생성 요청을 처리할 수 없습니다. 일반적으로 Google Play Services가 오래되었거나 패스키 생성을 지원하는 자격 증명 제공자가 없음을 의미합니다.

구성 및 보안 오류:

오류참고
Passkeys not supported for this appDigital Asset Links(assetlinks.json)가 누락되었거나 앱의 서명 인증서 지문이 포함되어 있지 않습니다. SecurityError와 동등합니다.
Https failed: respCode=301, url=https://<domain>/.well-known/assetlinks.jsonassetlinks.json 파일이 HTTP 200 대신 리디렉션을 반환합니다. Android는 리디렉션 없이 정확한 URL에 파일을 요구합니다.
The incoming request cannot be validated자격 증명 관리자가 Digital Asset Links에 대해 요청을 확인할 수 없습니다.
RP ID cannot be validated.WebAuthn 옵션의 의존 당사자 ID가 assetlinks.json과 일치하지 않습니다.
Screen lock is missing.기기에 PIN, 패턴 또는 생체 인식이 구성되어 있지 않습니다. 패스키에는 사용자 확인이 필요합니다. ConstraintError와 동등합니다.
Cannot find an eligible account.기기의 Google 계정 중 패스키 생성 자격이 있는 계정이 없습니다(드물게, 일반적으로 맞춤형 엔터프라이즈 설정).

플랫폼 및 인증자 오류:

오류참고
Unsuccessful result from folsom activity.Google Play Services 내부 실패. "Folsom"은 패스키 작업을 위한 GMS 구성 요소입니다. 일시적 - 재시도가 적절합니다.
Can't find the proper key to decrypt the private key from WebauthnCredentialSpecifics.동기화된 패스키가 존재하지만 기기에서 비공개 키를 복호화할 수 없습니다. Google Password Manager 동기화 상태가 일치하지 않습니다. 자격 증명이 다른 기기에서 동기화되었지만 복호화 키를 사용할 수 없습니다. 개발자가 조치할 수 없습니다.
Operation was interrupted (원인: The UI was interrupted - please try again.)자격 증명 관리자 UI가 다른 활동(수신 전화, 화면 회전, 앱 백그라운드 전환)에 의해 중단되었습니다. AbortError와 동등합니다.
Unknown credential error특정 오류 유형이 적용되지 않을 때의 일반적인 포괄적 오류입니다. 대개 일시적입니다.
timeout (원인: Canceled)사용자가 생체 인식 확인을 완료하기 전에 자격 증명 관리자 작업이 시간 초과되었습니다.

10. 통합: 오류 분류 체계#

다음 다이어그램은 위에서 논의된 모든 계층(인프라, 환경, 작업 유형, 분류 및 감지)이 엔드투엔드로 어떻게 연결되는지 보여줍니다. 이는 오류 추적을 설계할 때 명심해야 할 멘탈 모델입니다.

핵심적인 통찰력: 원시 error.name은 환경의 어느 계층에서 오류를 생성했는지, 어떤 작업이 실행 중인지, 오류가 예상된 것인지 예상치 못한 것인지 알아야만 의미가 있습니다. 아래 섹션에서는 무엇을 로깅하고 그에 따라 어떻게 조치해야 하는지 다룹니다.

11. 오류를 디버깅할 수 있도록 로깅할 내용#

이 문서의 오류 분류 대부분은 클라이언트 측 신호만으로 수행할 수 있습니다. 프런트엔드 전용 관측 가능성 SDK는 대다수의 WebAuthn 오류를 분류할 수 있는 충분한 컨텍스트를 캡처합니다. 이것이 바로 Corbado의 관측 가능성 SDK가 설계된 방식이기도 합니다. 클라이언트 측 계층은 오류 귀속, 타이밍, 작업 컨텍스트 및 플랫폼 감지를 처리합니다. 서버 측 로깅은 백엔드에서만 볼 수 있는 오류에 대한 두 번째 계층을 추가합니다.

핵심 요구 사항: 모든 시도는 엔드투엔드로 조인할 수 있어야 합니다. 공유 상관 관계 ID(예: auth_flow_id)는 클라이언트 측 컨텍스트를 서버 확인 결과와 연결합니다.

11.1 클라이언트 측 신호(프런트엔드 SDK)#

신호중요한 이유
error.name + 정규화된 이유 버킷원시 브라우저 오류 + 사용자 지정 분류
작업 유형조건부 UI, 모달 로그인, 수동 생성, 조건부 생성, 자동 추가. **CDA(교차 기기 인증)**는 그 자체로 복잡한 영역입니다.
전체 환경 컨텍스트OS + 버전, 브라우저 + 버전, 하드웨어 브랜드/모델, 인증자 설정(예: GPM 활성화 여부?)
인증자 / 자격 증명 관리자 컨텍스트확장 프로그램 및 제공자 손상
작업 시작 후 오류 발생까지의 시간즉각적인 거부(<1초) vs 사용자 취소(1~15초) vs 시간 초과(30초 이상)
네트워크 / 연결 상태네트워크 오류는 종종 클라이언트 오류로 나타납니다. 사용자가 오프라인이었는지 추적하고 연결이 복원될 때 보내도록 로그를 대기열에 추가하세요.
QR/하이브리드 UI 표시 여부로컬 vs 교차 기기 실패
상관 관계 ID(auth_flow_id)서버 로그와 조인

11.2 서버 측 신호(백엔드 확인)#

서버 확인 실패는 브라우저가 자격 증명과 서명된 챌린지를 반환한 후에 발생합니다. 이러한 오류는 클라이언트 측 DOMException 이름과 같은 버킷에 혼합되는 것이 아니라 명시적인 코드가 있는 구조화된 오류여야 합니다. WebAuthn 서버 구현을 참조하세요.

신호중요한 이유
챌린지 불일치 / 만료된 챌린지세션 타이밍 또는 재생 문제
출처 / RP ID 불일치다중 도메인 구성 버그
잘못된 서명 / 자격 증명을 찾을 수 없음삭제되거나 손상된 자격 증명. 일반적인 사례: 사용자가 서버 측에서 이미 삭제한 패스키를 사용한 조건부 UI 로그인. 클라이언트와 서버 자격 증명 목록을 동기화 상태로 유지하려면 Signal API를 사용하세요.
사용자 핸들 불일치계정 매핑 문제
상관 관계 ID(auth_flow_id)클라이언트 측 컨텍스트와 조인

단계별 이탈과 단계 간 전환이 포함된 전체 퍼널 모델을 원한다면 드롭오프를 이해하기 위한 패스키 원격 분석을 참조하세요.

Substack Icon

최신 뉴스를 위해 Passkeys Substack을 구독하세요.

구독하기

이 데이터가 마련되면 결론은 간단해집니다. 대부분의 "오류"는 UX 수정, 적용 범위 수정 또는 구성 수정이 됩니다. 하지만 이 분류를 직접 구축하고 유지 관리하는 것은 상당한 지속적인 작업입니다.

12. 오류 이름을 넘어: Corbado가 원시 오류를 조치 가능한 신호로 변환하는 방법#

위의 로깅 체크리스트는 원시 신호를 캡처합니다. 대규모 프로덕션에서는 error.name만으로는 충분하지 않습니다. 브라우저와 OS 릴리스마다 오류 메시지가 변경되고, 암호 관리자 제공자가 세레모니 동작을 변경하는 업데이트를 배포하며, 기능 출시마다 새로운 오류 서명이 나타나기 때문에 이러한 분류를 직접 구축하는 것은 상당한 지속적인 작업입니다.

12.1 error.name만으로 충분하지 않은 이유#

동일한 NotAllowedError라도 브라우저가 분리하지 않는 세 가지 차원에 따라 여섯 가지 다른 의미를 가질 수 있습니다.

차원브라우저가 제공하는 것실제로 필요한 것예시
작업 컨텍스트NotAllowedError조건부 UI, 모달 로그인, 수동 생성, 조건부 생성 또는 자동 추가였습니까?Android는 로그인 해제(예상)와 생성 실패(예기치 않음)에 대해 동일한 "알 수 없는 오류"를 반환합니다.
타이밍기간 데이터 없음즉각적 거부(<1초) vs 사용자 취소(1~15초) vs 시간 초과(30초 이상)200ms = 환경 거부; 5초 = 사용자가 대화 상자를 보고 취소함; 35초 = 시간 초과
플랫폼 + 인증자일반적인 error.name모든 오류에 대한 OS, 브라우저, 버전, 자격 증명 관리자Chrome의 "사용자가 대화 상자를 닫음"과 Safari의 "자동 완성을 사용할 수 없음"은 모두 NotAllowedError로 나타납니다.

12.2 Corbado의 관측 가능성 SDK가 캡처하는 내용#

이것이 Corbado의 관측 가능성 SDK가 해결하도록 구축된 문제입니다. 이 SDK는 기존 패스키 구현 위에 위치하고, 모든 WebAuthn 서버 및 모든 IDP와 작동하며, 모든 WebAuthn 오류를 세 가지 차원 모두에 따라 자동으로 분류하는 경량 프런트엔드 통합입니다.

기능수행하는 작업
오류 귀속모든 세레모니 시도마다 OS, OS 버전, 브라우저, 브라우저 버전 및 인증자를 캡처합니다.
작업 모드각 오류를 특정 작업(조건부 UI, 모달 로그인, 수동 생성, 조건부 생성, 자동 추가)에 연결하여 동일한 NotAllowedError가 다른 근본 원인으로 확인되도록 합니다.
작업 시작 후 타이밍세레모니 시작부터의 시간을 기록하여 추측 없이 즉각적인 거부, 사용자 취소 및 시간 초과를 구분합니다.
지능형 오류 분류(이름뿐만 아니라) 전체 오류 컨텍스트를 매칭하고 다양한 환경(OS, 하드웨어, 설정)에 걸쳐 정규화합니다. CDA 대 로컬과 같은 뚜렷한 오류 그룹의 우선순위를 정하고 예상(사용자 중단) 오류와 예기치 않은(시스템 장애) 오류를 구별합니다.
암호 관리자 파편화자격 증명 관리자 확장 프로그램(Bitwarden, 1Password, LastPass)이 세레모니를 가로채고 비표준 응답을 반환할 때를 감지하여 플랫폼 실패에서 확장 프로그램으로 인한 실패를 분리합니다.

이것은 구현을 변경하지 않고 어떤 일이 일어나고 있는지 볼 수 있는 가시성인 관찰(Observe) 계층입니다.

12.3 디버깅하는 두 가지 방법: 하향식 및 상향식#

Corbado의 관리 콘솔은 두 가지 조사 경로를 지원합니다.

하향식(대시보드에서 근본 원인으로):

  1. 두 가지 뚜렷한 이상 유형 모니터링:
    • 예상 오류 증가(기준선 드리프트): 특정 환경(예: iPhone 15의 iOS 18.2)에서 "사용자 취소"가 점진적으로 증가했습니까? 이는 종종 OS 업데이트로 인해 발생한 UX 마찰을 나타냅니다.
    • 예기치 않은 오류 증가(스파이크): 완전히 새로운 오류 또는 실패의 갑작스러운 증가. 이는 대개 손상을 유발하는 변경(내부 IDP 스택 업데이트) 또는 새 브라우저 버전의 회귀를 나타냅니다.
  2. 영향에 따른 우선순위 지정:
    • P1: 로그인 문제. 로그인 성공률이 떨어지면 즉시 경고합니다.
    • P2: 생성 문제. 추세를 모니터링하되 생성 흐름에서 "사용자 취소" 스파이크로 인해 엔지니어를 깨우지 마세요.
  3. 환경 심층 분석: 세분화된 차원(하드웨어, 인증 설정)을 사용하여 문제가 전역적인지 아니면 "Android 14를 실행하는 삼성 기기"에 특정한지 격리합니다.
  4. 완화: 심각한 스파이크가 감지되면 **킬 스위치(Kill Switch)**를 준비하세요. 해당 특정 환경에 대한 패스키를 자동 또는 수동으로 비활성화하고 조사하는 동안 로그인 속도를 유지하기 위해 OTP/비밀번호로 대체합니다.

상향식(오류 패턴에서 영향으로):

  1. 오류 분류 보기에서 시작합니다. 분류된 오류 패턴과 그 볼륨을 검토합니다.
  2. 새로운 패턴이 나타나면(예: 새 브라우저 버전이 다른 오류 메시지를 출시하는 경우) 오류 매핑을 구체화합니다.
  3. 오류는 KPI 변화와 교차 연관되어 대시보드에 주석으로 나타나므로 특정 오류 패턴의 스파이크가 영향을 미친 지표에 자동으로 연결됩니다.

두 경로는 하나로 수렴합니다. 하향식은 무언가 잘못되었음을 알려주고 상향식은 그 이유를 알려줍니다. AI Analytics Assistant는 오류 데이터와 채택 지표 전반에 걸쳐 자연어로 질문할 수 있게 하여 두 경로를 연결합니다.

이러한 신호에 따라 조치하려는 팀은 **채택(Adopt)**으로 이동하여 패스키 인텔리전스를 추가함으로써 자동으로 세레모니를 게이팅하고, 등록 프롬프트를 최적화하고, 깨진 패스키 상태를 복구할 수 있습니다. 규제가 심한 환경이나 하이퍼스케일 배포의 경우 **엔터프라이즈(Enterprise)**는 단일 테넌트 호스팅, SIEM 통합 및 PSD2 준수 구성을 추가합니다.

Enterprise Icon

엔터프라이즈용 무료 passkey 백서를 받으세요.

무료로 받기

13. 결론#

WebAuthn 오류 이름은 판결이 아닙니다. 이들은 힌트일 뿐이며 작업 유형, 타이밍 및 플랫폼 컨텍스트와 연결할 때만 조치 가능해집니다.

  • 실제 프로덕션에서 가장 일반적인 WebAuthn 오류 이름은 무엇을 의미할까요? 대부분은 사용자 제어 흐름(NotAllowedError), 앱 수명 주기/동시성(AbortError), 보안 컨텍스트/구성(SecurityError) 또는 옵션/상태 버그(InvalidStateError, ConstraintError, DataError)와 같은 소수의 계층에 매핑됩니다. 대다수의 볼륨은 NotAllowedError이며, 그 대부분은 예상된 동작(사용자가 프롬프트를 해제함)입니다.
  • NotAllowedError를 어떻게 명확하게 구분할까요? 타이밍(즉각적 거부 vs 사용자 취소 vs 시간 초과), QR/하이브리드 표시기(가용성 불일치), 사용자 활성화 컨텍스트(특히 iOS/Safari에서) 및 작업 유형(조건부 UI vs 모달 로그인 vs 패스키 생성 vs 조건부 생성)을 사용합니다. 모든 NotAllowedError를 하나의 실패 모드로 취급하지 마세요.
  • 작업 유형이 중요한 이유는 무엇일까요? 조건부 UI 로그인 중에 발생하는 동일한 error.name은 조건부 생성이나 수동 패스키 생성(사용자가 대화 상자를 닫음) 중에 발생하는 것과는 완전히 다른 신호입니다. 오류와 함께 작업 유형을 기록하는 것이 일반적인 NotAllowedError를 조치 가능한 버킷으로 바꾸는 요소입니다.
  • 오류를 디버깅할 수 있게 하는 최소한의 컨텍스트는 무엇일까요? error.name, 작업 유형, 작업 시작부터 오류 발생까지의 시간, 흐름 유형, QR/하이브리드 UI 표시 여부, OS/브라우저/기기(버전 포함), 상관 관계 ID(auth_flow_id) 및 서버 확인 거부를 명시적 코드로 캡처하세요.

모든 오류 유형에 걸쳐 적용되는 두 가지 경험 법칙: 사용자에게 원시 브라우저 오류를 절대 표시하지 말고 항상 명확한 대체 경로를 제공하세요. 로컬 시도와 QR/하이브리드 교차 기기 시도를 분리하세요. 이들은 서로 다른 이유로 실패하며 다른 수정 사항이 필요하기 때문입니다. 대규모에서는 브라우저, OS 버전 및 자격 증명 관리자 전반에 걸쳐 오류 분류를 유지 관리하는 것이 지속적인 작업입니다. 처음부터 이것을 구축하는 대신 유지 관리되는 패턴 라이브러리가 있는 관측 가능성 SDK를 사용하는 것을 고려하세요.

Corbado

Corbado 소개

Corbado는 대규모로 consumer authentication을 운영하는 CIAM 팀을 위한 Authentication Intelligence Platform입니다. IDP 로그와 일반 analytics 도구가 보여주지 못하는 것을 볼 수 있게 해드립니다: 어떤 디바이스, OS 버전, 브라우저, credential manager가 passkey를 지원하는지, 왜 등록이 로그인으로 이어지지 않는지, WebAuthn 플로우가 어디서 실패하는지, OS나 브라우저 업데이트가 언제 조용히 로그인을 망가뜨리는지 — Okta, Auth0, Ping, Cognito 또는 자체 IDP를 교체하지 않고도 전부 파악할 수 있습니다. 두 가지 제품: Corbado Observe passkey 및 다른 모든 로그인 방식에 대한 observability를 더합니다. Corbado Connectanalytics가 내장된 managed passkey를 제공합니다 (기존 IDP와 함께). VicRoads는 Corbado로 500만+ 사용자에게 passkey를 운영하고 있습니다 (passkey 활성화율 +80%). Passkey 전문가와 상담하기

자주 묻는 질문(FAQ)#

사용자가 패스키 프롬프트를 취소하는 것과 기기에 패스키가 없는 것의 차이를 어떻게 알 수 있나요?#

웹에서 브라우저는 개인 정보 보호를 위해 두 가지 경우를 모두 NotAllowedError로 축소하므로 직접 구별할 수 없습니다. 대신 타이밍을 프록시로 사용하세요: 1초 미만의 오류는 일반적으로 환경 거부 또는 기능 누락을 의미하는 반면, 1-15초는 사용자가 대화 상자를 보고 닫았음을 시사합니다. 네이티브 iOS 및 Android 앱에서는 요청 전에 preferImmediatelyAvailableCredentials를 설정하면 UI가 표시되기 전에 깔끔한 "자격 증명 없음" 신호(iOS 코드 1005, Android GetCredentialRequest)가 제공됩니다.

대부분의 사용자에 대해 패스키가 잘 작동하는 것 같은데 WebAuthn 오류율이 왜 이렇게 높은가요?#

최적화된 대규모 패스키 배포에서 기록된 WebAuthn 오류의 95% 이상은 시스템 실패가 아니라 예상되는 사용자 중단입니다. "사용자가 프롬프트를 해제함"을 실제 실패와 분리하지 않고 원시 error.name 개수만 추적하면 오류 지표가 부풀려지고 NotAllowedError 볼륨 안에 숨겨진 실제 회귀가 가려집니다. 작업 유형별로 개수를 나누고 사용자 취소 버킷을 SecurityError, ConstraintErrorDataError와 같은 예기치 않은 오류와 별도로 취급하세요.

iOS에서 ASAuthorizationError 코드 1005는 무엇을 의미하며 어떻게 사용해야 하나요?#

iOS 코드 1005(NotInteractive)는 ASAuthorizationControllerpreferImmediatelyAvailableCredentials가 설정되었을 때 기기에서 패스키를 사용할 수 없었으며 사용자에게 UI가 표시되지 않았음을 의미합니다. 이는 개인 정보 보호 제약으로 인해 웹 브라우저가 제공할 수 없는 깔끔한 "이 기기에 패스키가 존재하지 않음" 신호입니다. Apple의 메시지는 30개 이상의 언어로 현지화되고 OS 버전에 따라 변경될 수 있으므로 localizedDescription이 아닌 항상 숫자 코드로 분류하세요.

조건부 생성 또는 자동 트리거 패스키 추가 흐름 중에 NotAllowedError를 어떻게 처리해야 하나요?#

조건부 생성 중에 NotAllowedError, AbortErrorInvalidStateError는 모두 예상되는 결과이며 오류로 표시되는 대신 조용히 무시되어야 합니다. NotAllowedError는 자동 완성을 사용할 수 없거나 페이지가 포커스를 잃었음을 나타내고, InvalidStateError는 패스키가 이미 제공자에 존재함을 의미합니다. 호출을 시도하기 전에 getClientCapabilities()를 사용하여 conditionalCreate 지원을 확인하고 문서 가시성 상태를 검사하여 오류 수가 부풀려지는 것을 방지하세요.

WebAuthn 실패를 실제로 디버깅할 수 있게 만들려면 error.name과 함께 어떤 컨텍스트를 로깅해야 하나요?#

작업 유형(조건부 UI, 모달 로그인, 수동 생성, 조건부 생성 또는 자동 추가), 작업 시작 후 오류 발생까지의 시간, OS 버전, 브라우저 버전, 하드웨어 모델, QR/하이브리드 UI 표시 여부 및 서버 측 로그와 결합할 상관 관계 ID를 캡처하세요. 챌린지 불일치, 잘못된 서명, 자격 증명을 찾을 수 없음과 같은 서버 확인 실패는 클라이언트 측 DOMException 이름과 동일한 버킷에 혼합되는 것이 아니라 명시적인 구조화된 코드로 로깅되어야 합니다. 이러한 신호가 함께 모여 방대한 대다수의 오류를 추측 없이 조치 가능한 버킷으로 분류할 수 있게 해줍니다.

Corbado가 패스키 도입 과정과 기존 인증 스택에 어떻게 맞는지 확인하세요.

Console 살펴보기

이 글 공유하기


LinkedInTwitterFacebook