이 페이지는 자동 번역되었습니다. 영어 원문은 여기.
프로덕션 환경에서 WebAuthn 오류는 혼란스럽습니다. 브라우저가 근본적인 원인을 다양하게 나타낼 수 있는 소수의 DOMException 이름(예: NotAllowedError)만 노출하기 때문입니다. 게다가 최적화된 대규모 배포에서 95% 이상을 차지하는 대다수의 "오류"는 실제로는 예상된 동작(사용자가 운영 체제의 패스키 프롬프트를 중단함)입니다.
Passkeys Debugger에서 passkey 흐름을 실험하세요.
중요: 개인 정보 보호를 위해 브라우저는 사용자가 적극적으로 취소했는지 아니면 패스키가 존재하지 않는지 구분하지 않습니다. 그러나 웹과 네이티브 플랫폼 모두의 일부 상황에서 충분한 컨텍스트가 있다면 타이밍과 같은 신호를 사용하여 이러한 사례 중 일부를 구별할 수 있습니다.
이러한 이름에 대한 표준 정의를 원한다면 MDN DOMException부터 시작하세요. 이러한 예외를 발생시키는 WebAuthn 관련 조건(및 브라우저가 강제해야 하는 사항)에 대해서는 W3C 웹 인증 사양을 참조하세요.
모든 오류를 "버그"로 취급하면 잘못된 조치를 취하게 됩니다.
NotAllowedError 속에 숨겨진 실제 회귀를 놓치게 됩니다.이 글에서는 다음 질문에 답합니다.
NotAllowedError를 어떻게 조치 가능한 버킷(취소 vs 시간 초과 vs 가용성)으로 구분할 수 있을까요?NotAllowedError는 근본 원인이 아니라 표면적 신호입니다. 컨텍스트에 따라 취소, 시간 초과, "로컬 자격 증명 없음" 또는 누락된 사용자 활성화를 의미할 수 있습니다.NotAllowedError라도 조건부 UI 로그인, 모달 로그인, 수동 패스키 생성, 조건부 생성 및 자동 트리거 추가 중에 다른 의미를 갖습니다.<1초), 사용자 취소(1~15초), 시간 초과(30초 이상)는 근본적으로 다른 범주입니다.AbortError는 일반적으로 수명 주기/동시성 문제(탐색, 리렌더링, 진행 중인 여러 요청)입니다.SecurityError는 거의 항상 구성/컨텍스트 문제이며 성숙한 프로덕션 배포에서는 드뭅니다.error.name과 함께 작업 유형, 타이밍, 플랫폼 컨텍스트를 캡처하세요.디버깅을 풀기 위한 빠른 매핑이 필요하다면 이 표부터 시작하세요. 이는 팀이 대시보드와 지원 티켓에서 실제로 보는 내용에 맞춰져 있습니다.
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에 대해 더 깊이 살펴보겠습니다.
NotAllowedError는 종종 "패스키 실패"처럼 보이지만, 보통 사용자가 OS UI를 완료하지 않았다는 플랫폼의 알림입니다. 핵심은 조치할 수 있는 버킷으로 나누는 것입니다.
브라우저 콘솔에서 표시되는 내용:
| 출처 | 오류 메시지 |
|---|---|
| Chrome, Edge | NotAllowedError: The operation either timed out or was not allowed. See: https://www.w3.org/TR/webauthn-2/#sctn-privacy-considerations-client. |
| Safari, WebKit | NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission. |
| Safari, WebKit | NotAllowedError: This request has been cancelled by the user. |
| Chrome, Edge | NotAllowedError: The operation is not allowed at this time because the page does not have focus. |
| Safari, WebKit | NotAllowedError: The document is not focused. |
| Firefox | NotAllowedError: Operation failed. |
이들 모두는 error.name === "NotAllowedError"로 나타납니다. error.message는 브라우저 엔진과 근본 원인에 따라 다르지만 결과는 동일합니다. 즉, 세레모니가 완료되지 않았습니다.
이것은 로그인 및 패스키 생성 모두에 적용됩니다. 로그인 중(조건부 UI, allowList가 있거나 없는 모달)에 NotAllowedError는 보통 사용자가 세레모니를 완료하지 않았음을 의미합니다. 패스키 생성 중에는 동일한 오류가 다른 이유로 발생합니다. 사용자가 생성 대화 상자를 닫았거나(조건부 생성이 작동하지 않음), 자동 트리거 추가 중에 페이지가 포커스를 잃었습니다. 작업 유형에 따라 오류가 의미하는 바와 취해야 할 조치가 달라집니다.
타이밍은 종종 과소평가되는 신호입니다. 클릭 후 1초 미만의 오류는 보통 즉각적인 거부(환경에서 할 수 없음, 문서에 포커스가 없음, 기능 누락)를 의미합니다. 몇 초 후의 오류는 사용자 취소입니다(사용자가 대화 상자를 보고 계속 진행하지 않기로 결정함). 30초 이상 지난 후의 오류는 시간 초과입니다. 네이티브 플랫폼에서 타이밍은 특히 중요합니다. 인증자 왕복, 생체 인식 프롬프트, 자격 증명 관리자 핸드오프에는 모두 "작동하지 않음"과 "사용자가 자리를 비움"을 구분하는 데 도움이 되는 고유한 기간이 있습니다. 여전히 패스키가 있었는지 쉽게 구분할 수는 없습니다.
완벽한 신호가 필요한 것은 아닙니다. 모든 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로 축소되는 것을 볼 수 있습니다. 어느 쪽이든 시간 초과를 "세레모니가 완료되지 않음"으로 간주하고 타이밍과 컨텍스트를 사용하여 분류하세요.
OS 시트가 해제되거나 시간이 초과되면 UI가 즉시 복구되고 정상적으로 반응해야 합니다. 실용적인 규칙 세트:
기본 그 이상:
"취소"가 진정으로 많다면 다음 단계는 그 이면에 있는 근본 원인(프롬프트 타이밍, 깜짝 QR, 낮은 가용성)을 수정하는 것입니다.
지표를 빠르게 이동시키는 변경 사항부터 시작하세요.
NotAllowedError 버킷이 부풀려지지 않도록 합니다. 가장 기본적인 게이트로 isUVPAA()로 시작한 다음, 더 세밀한 확인(조건부 생성, 조건부 가져오기, 하이브리드 전송, 플랫폼 인증자)을 위해 getClientCapabilities()를 사용합니다. 감지 API가 OS 업데이트로 인해 깨질 수 있다는 점에 유의하세요. iOS 26.2에는 패스키가 정상적으로 작동함에도 불구하고 모든 WKWebView 기반 브라우저에서 isUVPAA()가 false를 반환하는 WebKit 버그가 포함되어 출시되어 iOS 사용자의 10-25%에서 갑작스러운 NotAllowedError 급증을 초래했습니다(getClientCapabilities()로 패스키 감지 수정 참조).오류 이름은 계속 변하는 목표입니다. 더 세분화된 WebAuthn 오류를 추가하기 위한 제안("사용 가능한 자격 증명 없음"을 "사용자 취소"와 분리하기 위해)이 진행 중입니다. 2026년 2월 현재 이는 어느 브라우저에도 구현되지 않았으므로 컨텍스트와 타이밍을 기반으로 자체적인 이유 버킷을 구축하는 것이 여전히 가치가 있습니다. 이 작업을 추적하려면 WebAuthn 문제 #2062 및 "새로운 오류 코드" 설명자를 참조하세요.
나머지 오류 이름은 빈도는 적지만 나타날 때 이해할 가치가 있습니다.
AbortError는 NotAllowedError와 비교하여 볼륨 측면에서 드물지만 발생하면 매우 진단적입니다. 이는 일반적으로 앱이 요청을 무효화했기 때문에 세레모니가 완료되지 않았음을 의미합니다(탐색 발생, 상태 변경 또는 두 번째 요청 시작).
브라우저 콘솔에서 표시되는 내용:
| 출처 | 오류 메시지 |
|---|---|
| Chrome, Edge | AbortError: The operation was aborted. |
| Chrome, Edge | AbortError: Aborted by AbortSignal. |
| Firefox | AbortError: signal is aborted without reason |
| Firefox | AbortError: Operation timed out. |
| Safari, WebKit | AbortError: The user aborted a request. |
| Chrome | AbortError: CredentialContainer request is not allowed. |
일반적인 프로덕션 원인:
AbortController.abort() 호출이를 해결하려면 세레모니를 "임계 영역"으로 만드는 데 집중하세요.
임베디드 표면이나 다중 도메인 앱에 집중된 AbortError가 표시되는 경우 확인해야 할 다음 버킷은 SecurityError입니다.
SecurityError는 브라우저가 "이 컨텍스트는 요청한 작업을 수행하도록 허용되지 않습니다."라고 알려주는 것입니다. 실제로 이는 거의 항상 사용자 행동이 아닌 구성 문제입니다. 성숙한 프로덕션 배포에서 SecurityError는 드뭅니다. 이러한 문제는 통합 테스트 중에 일반적으로 포착되기 때문입니다. 프로덕션 환경에 나타난다면 대개 적절한 WebAuthn 구성 없이 새 도메인, 임베딩 컨텍스트 또는 배포 대상이 추가되었음을 의미합니다.
일반적인 원인:
.well-known/webauthn 또는 .well-known/assetlinks.json이 잘못 구성되었거나, 없거나, 일시적으로 사용할 수 없음. 브라우저가 이러한 파일을 가져오는 임계 창 동안의 네트워크 문제는 실패를 유발합니다. 일반적인 사각지대: 홈페이지가 유지 보수를 위해 다운된 경우 well-known 파일도 오프라인이 되어 이를 사용하는 모든 의존 당사자의 패스키 세레모니가 중단됩니다.브라우저 콘솔에서 표시되는 내용:
| 출처 | 오류 메시지 |
|---|---|
| Chrome, Edge | SecurityError: 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 인증서 오류입니다.
가장 빠른 디버깅 루프는 다음과 같습니다.
publickey-credentials-create / publickey-credentials-get): MDN Permissions-PolicySecurityError가 처리되고 나면 심각하게 다루어야 할 다음 버킷은 종종 구현 버그를 나타내는 오류인 InvalidStateError, ConstraintError 및 DataError입니다.

Passkeys 치트시트. passkey 프로그램을 위한 실무 가이드, 도입 패턴, KPI.
이러한 오류는 성숙한 패스키 구현에서는 드물어야 합니다. 이들이 나타나면 일반적으로 세그먼트에 대한 옵션 생성이 잘못되었거나 흐름이 잘못된 상태임을 나타냅니다.
브라우저 콘솔에서 표시되는 내용:
| 출처 | 오류 메시지 |
|---|---|
| Safari, WebKit | InvalidStateError: The user attempted to register an authenticator that contains one of the credentials already registered with the relying party. |
| Chrome, Edge | InvalidStateError: At least one credential matches an entry of the excludeCredentials list in the platform attached authenticator. |
| Chrome, Edge | InvalidStateError: A request is already pending. |
| Firefox | InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable |
일반적인 의미:
실용적인 처리:
excludeCredentials가 사용자의 모든 기존 자격 증명 ID를 나열하는지 확인합니다(excludeCredentials 참조).InvalidStateError는 예상된 것이며 조용히 무시해야 합니다. 이는 제공자에 패스키가 이미 존재함을 의미합니다. 조건부 생성 중에 NotAllowedError 및 AbortError에도 동일하게 적용됩니다(Chrome의 조건부 생성 참조).일반적인 의미: 인증자가 요청한 제약 조건을 충족할 수 없습니다.
일반적인 트리거:
authenticatorAttachment 또는 상주 키 가정userVerification 요구 사항수정: (허용 가능한 경우) 제약을 완화하거나 대체 경로를 제공합니다. 누락된 화면 잠금의 경우, 조용히 실패하는 대신 이 조건을 감지하고 사용자를 안내하는 것을 고려하세요(Android).
일반적인 의미: 입력이 잘못되었거나 일치하지 않습니다.
일반적인 트리거:
수정: WebAuthn 옵션을 생성하는 경계에서 입력을 검증하고 정규화합니다. 실제로 성숙한 프로덕션 시스템에서 DataError는 사실상 없습니다. 옵션 생성을 테스트했다면 대시보드에서 이를 볼 수 없을 것입니다.
이러한 오류가 통제되고 있다면 다음 질문은 적용 범위입니다. 환경이 예상대로 WebAuthn을 수행할 수 없기 때문에 사용자가 실패하고 있습니까?
NotSupportedError는 신뢰성 신호가 아니라 범위 신호입니다. 대개 세그먼트가 요청한 작업을 수행할 수 없음을 의미합니다(너무 오래된 OS/브라우저, 기능 누락, 기능이 활성화되지 않음).
브라우저 콘솔에서 표시되는 내용:
| 출처 | 오류 메시지 |
|---|---|
| Chrome, Edge | NotSupportedError: The user agent does not support public key credentials. |
| Firefox | NotSupportedError: Resident credentials or empty 'allowCredentials' lists are not supported. |
| Chrome, Edge, Firefox | TypeError: PublicKeyCredential.parseCreationOptionsFromJSON is not a function |
| Chrome, Edge, Firefox | TypeError: PublicKeyCredential.parseRequestOptionsFromJSON is not a function |
| Chrome, Edge, Firefox | TypeError: credential.toJSON is not a function |
| Safari | TypeError: Can only call PublicKeyCredential.toJSON on instances of PublicKeyCredential |
처음 두 개는 순수한 NotSupportedError DOMException입니다. TypeError 항목은 기술적으로 예외 유형이 다르지만 브라우저나 환경이 요청한 것을 지원하지 않는다는 동일한 클래스의 문제를 나타냅니다. JSON 직렬화 TypeError 제품군은 실제 NotSupportedError DOMException 자체보다 훨씬 더 일반적입니다(아래 참조).
일반적인 원인:
JSON 직렬화 제품군은 프로덕션 환경에서 NotSupportedError 클래스 오류의 가장 큰 원인입니다. 기술적으로 이러한 오류는 DOMException이 아닌 TypeError(메서드 누락)로 나타나지만, 여기에서 발생합니다. 두 가지 뚜렷한 근본 원인:
navigator.credentials는 있지만 PublicKeyCredential.parseCreationOptionsFromJSON / parseRequestOptionsFromJSON은 없습니다. 이는 이전 버전의 Safari와 Chrome에 집중되어 이 오류 제품군의 대략 90%를 차지합니다. 클라이언트 라이브러리가 대체 없이 이러한 메서드에 의존하는 경우 상당한 오류 볼륨이 생성됩니다..toJSON()을 중단합니다. Bitwarden, LastPass 또는 1Password와 같은 확장은 세레모니를 가로채고 자격 증명처럼 보이지만 실제 PublicKeyCredential 인스턴스가 아닌 객체를 반환할 수 있습니다. 해당 객체에 .toJSON()을 호출하면 throw하거나 undefined를 반환하거나 객체가 완전히 null이 됩니다. 이는 제품군의 대략 10%를 차지하지만 오류 메시지가 브라우저(Safari: "PublicKeyCredential의 인스턴스에서만 호출할 수 있음"; Firefox: "PublicKeyCredential 인터페이스를 구현하지 않음")마다 다르기 때문에 디버깅하기가 특히 혼란스럽습니다.처리는 직설적이고 빨라야 합니다.
적용 범위가 양호해 보이지만 특정 세그먼트에서 여전히 실패가 발생하면 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 |
실용적인 처리:
DOMException 범주에 깔끔하게 들어맞지 않는 틈새 오류 원인 중 하나: 암호 관리자 브라우저 확장 프로그램(Bitwarden, LastPass, 1Password 및 기타 등)은 WebAuthn API 호출을 가로채고 비표준 응답을 반환할 수 있습니다. 사용자 취소에 비해 규모는 작지만 일관되게 특정 사용자 세그먼트에 영향을 미치고 증상이 혼란스럽기 때문에 추적할 가치가 있습니다. 반환된 자격 증명 객체의 메서드 누락, 예기치 않은 오류 유형 또는 문서화된 WebAuthn 오류와 일치하지 않는 형식이 잘못된 응답 등이 있습니다. 이들은 종종 UnknownError 또는 분류되지 않은 예외로 나타납니다. OS 수준의 설명이 없는 특정 브라우저에 집중된 오류 급증이 나타나면 자격 증명 관리자 확장 프로그램이 관련되어 있는지 확인하세요.
지금까지 웹 브라우저 오류 이름을 다뤘습니다. 하지만 네이티브 앱도 제공한다면 오류 환경이 달라지며 어떤 면에서는 훨씬 좋습니다.
위의 모든 내용은 웹 브라우저를 다룹니다. 네이티브 앱 - ASAuthorization 프레임워크를 사용하는 iOS, Credential Manager를 사용하는 Android - 은 동일한 기본 오류 범주를 공유하지만 중요한 면에서 다릅니다.
"자격 증명 없음"은 구별되는 신호입니다. 웹에서 브라우저는 개인 정보 보호를 위해 "사용할 수 있는 자격 증명 없음"과 "사용자 취소"를 동일한 NotAllowedError로 축소합니다. 네이티브의 경우 iOS(ASAuthorizationController)에서 preferImmediatelyAvailableCredentials를 사용하거나 Android(GetCredentialRequest)에서 setPreferImmediatelyAvailableCredentials(true)를 사용하면 기기에 이미 있는 자격 증명만 제시하고 없으면 즉시 실패하도록 OS에 지시합니다. 이를 통해 웹에서는 제공할 수 없는 깔끔한 "자격 증명 없음" 반환을 얻을 수 있습니다.
자격 증명 제공자 상태가 보입니다. 네이티브 플랫폼에서는 일부 조건에서 자격 증명 제공자(Google Password Manager, iCloud Keychain, 1Password 등)가 설치, 구성 또는 기본값으로 설정되지 않은 시기를 알려주고 이에 대응할 수 있습니다. 웹에서 이 정보는 불투명한 NotAllowedError 메시지 뒤에 숨겨져 있습니다.
오류 메시지가 더 구체적입니다. 사용자가 앱을 설치하여 의존 당사자와 신뢰 관계를 구축했기 때문에 OS는 더 구체적인 진단 세부 정보를 제공합니다. 웹 브라우저를 모호하게 만드는 개인 정보 보호 고려 사항은 앱이 이미 기기에 있는 경우 동일한 방식으로 적용되지 않습니다. iOS는 사용자의 기기 언어로 현지화된 메시지를 반환합니다. Android는 원인 체인과 함께 구조화된 오류 유형을 반환합니다. 이로 인해 디버깅이 쉬워지지만 오류 처리는 현지화 및 플랫폼별 오류 형식을 고려해야 합니다.
iOS는 ASAuthorization 프레임워크를 통해 패스키 오류를 표시합니다. 모든 오류는 authorizationController(controller:didCompleteWithError:) 델리게이트 콜백에 NSError 객체로 도착합니다.
메시지 문자열이 아닌 도메인 + 코드로 분류하세요. 기본 오류 도메인은 com.apple.AuthenticationServices.AuthorizationError(ASAuthorizationError.errorDomain)입니다. 오류를 let nsError = error as NSError로 캐스트하고 .domain 및 .code에서 일치시킵니다. 프로덕션에서는 절대로 .localizedDescription과 일치시키지 마세요. Apple의 메시지는 30개 이상의 언어로 현지화되며 OS 버전에 따라 변경될 수 있습니다. 아래 나열된 메시지 문자열은 로그에서 오류를 인식하는 데 유용하지만 분류 기준은 아닙니다.
공개 ASAuthorizationError 코드:
| 코드 | 이름 | 버전 | 의미 |
|---|---|---|---|
| 1000 | Unknown | iOS 13 | 프로덕션에 표시되지 않아야 합니다. 일반적인 포괄적 오류입니다. |
| 1001 | Canceled | iOS 13 | 사용자가 패스키 시트를 해제했습니다. 전반적으로 가장 일반적인 오류 - NotAllowedError와 동등합니다. userInfo가 비어 있고 근본적인 오류가 없는 깔끔한 신호입니다. |
| 1002 | InvalidResponse | iOS 13 | 프레임워크 수준의 손상. 실제로 드뭅니다. |
| 1003 | NotHandled | iOS 13 | 요청을 처리한 제공자가 없습니다. 권한과 자격 증명 제공자 구성을 확인하세요. |
| 1004 | Failed | iOS 13 | 일반적인 실패. localizedDescription에는 실제 이유(예: "식별자가 X인 응용 프로그램이 도메인 Y와 연결되어 있지 않습니다")가 포함됩니다. userInfo에는 FailureReason 문자열이 포함될 수 있지만 NSUnderlyingErrorKey가 항상 채워지는 것은 아닙니다. 도메인 연결 실패는 기본 오류에 대해 nil을 반환합니다. |
| 1005 | NotInteractive | iOS 15 | preferImmediatelyAvailableCredentials를 사용할 때 사용할 수 있는 자격 증명이 없습니다. 이것은 깔끔한 "찾을 수 없음" 신호로, "이 기기에 패스키가 존재하지 않음"의 iOS 버전입니다. UI가 표시되지 않았습니다. |
| 1006 | MatchedExcludedCredential | iOS 18 | 이 기기의 이 RP에 대한 패스키가 이미 존재합니다. 중복 감지를 위한 깔끔한 신호 - 빈 userInfo, NSUnderlyingErrorKey 없음. 문자열 매칭 없이 분류가 작동합니다. |
| 1007 | CredentialImport | iOS 18.2 | 자격 증명 가져오기 실패. |
| 1008 | CredentialExport | iOS 18.2 | 자격 증명 내보내기 실패. |
| 1009 | PreferSignInWithApple | iOS 26 | 사용자가 패스키보다 Apple로 로그인을 선호합니다. iOS 26에 새로 도입되었습니다. |
| 1010 | DeviceNotConfiguredForPasskeyCreation | iOS 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 X | 1004 (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에서 일치시킵니다.
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 app | Digital Asset Links(assetlinks.json)가 누락되었거나 앱의 서명 인증서 지문이 포함되어 있지 않습니다. SecurityError와 동등합니다. |
Https failed: respCode=301, url=https://<domain>/.well-known/assetlinks.json | assetlinks.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) | 사용자가 생체 인식 확인을 완료하기 전에 자격 증명 관리자 작업이 시간 초과되었습니다. |
다음 다이어그램은 위에서 논의된 모든 계층(인프라, 환경, 작업 유형, 분류 및 감지)이 엔드투엔드로 어떻게 연결되는지 보여줍니다. 이는 오류 추적을 설계할 때 명심해야 할 멘탈 모델입니다.
핵심적인 통찰력: 원시 error.name은 환경의 어느 계층에서 오류를 생성했는지, 어떤 작업이 실행 중인지, 오류가 예상된 것인지 예상치 못한 것인지 알아야만 의미가 있습니다. 아래 섹션에서는 무엇을 로깅하고 그에 따라 어떻게 조치해야 하는지 다룹니다.
이 문서의 오류 분류 대부분은 클라이언트 측 신호만으로 수행할 수 있습니다. 프런트엔드 전용 관측 가능성 SDK는 대다수의 WebAuthn 오류를 분류할 수 있는 충분한 컨텍스트를 캡처합니다. 이것이 바로 Corbado의 관측 가능성 SDK가 설계된 방식이기도 합니다. 클라이언트 측 계층은 오류 귀속, 타이밍, 작업 컨텍스트 및 플랫폼 감지를 처리합니다. 서버 측 로깅은 백엔드에서만 볼 수 있는 오류에 대한 두 번째 계층을 추가합니다.
핵심 요구 사항: 모든 시도는 엔드투엔드로 조인할 수 있어야 합니다. 공유 상관 관계 ID(예: auth_flow_id)는 클라이언트 측 컨텍스트를 서버 확인 결과와 연결합니다.
| 신호 | 중요한 이유 |
|---|---|
error.name + 정규화된 이유 버킷 | 원시 브라우저 오류 + 사용자 지정 분류 |
| 작업 유형 | 조건부 UI, 모달 로그인, 수동 생성, 조건부 생성, 자동 추가. **CDA(교차 기기 인증)**는 그 자체로 복잡한 영역입니다. |
| 전체 환경 컨텍스트 | OS + 버전, 브라우저 + 버전, 하드웨어 브랜드/모델, 인증자 설정(예: GPM 활성화 여부?) |
| 인증자 / 자격 증명 관리자 컨텍스트 | 확장 프로그램 및 제공자 손상 |
| 작업 시작 후 오류 발생까지의 시간 | 즉각적인 거부(<1초) vs 사용자 취소(1~15초) vs 시간 초과(30초 이상) |
| 네트워크 / 연결 상태 | 네트워크 오류는 종종 클라이언트 오류로 나타납니다. 사용자가 오프라인이었는지 추적하고 연결이 복원될 때 보내도록 로그를 대기열에 추가하세요. |
| QR/하이브리드 UI 표시 여부 | 로컬 vs 교차 기기 실패 |
상관 관계 ID(auth_flow_id) | 서버 로그와 조인 |
서버 확인 실패는 브라우저가 자격 증명과 서명된 챌린지를 반환한 후에 발생합니다. 이러한 오류는 클라이언트 측 DOMException 이름과 같은 버킷에 혼합되는 것이 아니라 명시적인 코드가 있는 구조화된 오류여야 합니다. WebAuthn 서버 구현을 참조하세요.
| 신호 | 중요한 이유 |
|---|---|
| 챌린지 불일치 / 만료된 챌린지 | 세션 타이밍 또는 재생 문제 |
| 출처 / RP ID 불일치 | 다중 도메인 구성 버그 |
| 잘못된 서명 / 자격 증명을 찾을 수 없음 | 삭제되거나 손상된 자격 증명. 일반적인 사례: 사용자가 서버 측에서 이미 삭제한 패스키를 사용한 조건부 UI 로그인. 클라이언트와 서버 자격 증명 목록을 동기화 상태로 유지하려면 Signal API를 사용하세요. |
| 사용자 핸들 불일치 | 계정 매핑 문제 |
상관 관계 ID(auth_flow_id) | 클라이언트 측 컨텍스트와 조인 |
단계별 이탈과 단계 간 전환이 포함된 전체 퍼널 모델을 원한다면 드롭오프를 이해하기 위한 패스키 원격 분석을 참조하세요.
최신 뉴스를 위해 Passkeys Substack을 구독하세요.
이 데이터가 마련되면 결론은 간단해집니다. 대부분의 "오류"는 UX 수정, 적용 범위 수정 또는 구성 수정이 됩니다. 하지만 이 분류를 직접 구축하고 유지 관리하는 것은 상당한 지속적인 작업입니다.
위의 로깅 체크리스트는 원시 신호를 캡처합니다. 대규모 프로덕션에서는 error.name만으로는 충분하지 않습니다. 브라우저와 OS 릴리스마다 오류 메시지가 변경되고, 암호 관리자 제공자가 세레모니 동작을 변경하는 업데이트를 배포하며, 기능 출시마다 새로운 오류 서명이 나타나기 때문에 이러한 분류를 직접 구축하는 것은 상당한 지속적인 작업입니다.
error.name만으로 충분하지 않은 이유#동일한 NotAllowedError라도 브라우저가 분리하지 않는 세 가지 차원에 따라 여섯 가지 다른 의미를 가질 수 있습니다.
| 차원 | 브라우저가 제공하는 것 | 실제로 필요한 것 | 예시 |
|---|---|---|---|
| 작업 컨텍스트 | NotAllowedError | 조건부 UI, 모달 로그인, 수동 생성, 조건부 생성 또는 자동 추가였습니까? | Android는 로그인 해제(예상)와 생성 실패(예기치 않음)에 대해 동일한 "알 수 없는 오류"를 반환합니다. |
| 타이밍 | 기간 데이터 없음 | 즉각적 거부(<1초) vs 사용자 취소(1~15초) vs 시간 초과(30초 이상) | 200ms = 환경 거부; 5초 = 사용자가 대화 상자를 보고 취소함; 35초 = 시간 초과 |
| 플랫폼 + 인증자 | 일반적인 error.name | 모든 오류에 대한 OS, 브라우저, 버전, 자격 증명 관리자 | Chrome의 "사용자가 대화 상자를 닫음"과 Safari의 "자동 완성을 사용할 수 없음"은 모두 NotAllowedError로 나타납니다. |
이것이 Corbado의 관측 가능성 SDK가 해결하도록 구축된 문제입니다. 이 SDK는 기존 패스키 구현 위에 위치하고, 모든 WebAuthn 서버 및 모든 IDP와 작동하며, 모든 WebAuthn 오류를 세 가지 차원 모두에 따라 자동으로 분류하는 경량 프런트엔드 통합입니다.
| 기능 | 수행하는 작업 |
|---|---|
| 오류 귀속 | 모든 세레모니 시도마다 OS, OS 버전, 브라우저, 브라우저 버전 및 인증자를 캡처합니다. |
| 작업 모드 | 각 오류를 특정 작업(조건부 UI, 모달 로그인, 수동 생성, 조건부 생성, 자동 추가)에 연결하여 동일한 NotAllowedError가 다른 근본 원인으로 확인되도록 합니다. |
| 작업 시작 후 타이밍 | 세레모니 시작부터의 시간을 기록하여 추측 없이 즉각적인 거부, 사용자 취소 및 시간 초과를 구분합니다. |
| 지능형 오류 분류 | (이름뿐만 아니라) 전체 오류 컨텍스트를 매칭하고 다양한 환경(OS, 하드웨어, 설정)에 걸쳐 정규화합니다. CDA 대 로컬과 같은 뚜렷한 오류 그룹의 우선순위를 정하고 예상(사용자 중단) 오류와 예기치 않은(시스템 장애) 오류를 구별합니다. |
| 암호 관리자 파편화 | 자격 증명 관리자 확장 프로그램(Bitwarden, 1Password, LastPass)이 세레모니를 가로채고 비표준 응답을 반환할 때를 감지하여 플랫폼 실패에서 확장 프로그램으로 인한 실패를 분리합니다. |
이것은 구현을 변경하지 않고 어떤 일이 일어나고 있는지 볼 수 있는 가시성인 관찰(Observe) 계층입니다.
Corbado의 관리 콘솔은 두 가지 조사 경로를 지원합니다.
하향식(대시보드에서 근본 원인으로):
상향식(오류 패턴에서 영향으로):
두 경로는 하나로 수렴합니다. 하향식은 무언가 잘못되었음을 알려주고 상향식은 그 이유를 알려줍니다. AI Analytics Assistant는 오류 데이터와 채택 지표 전반에 걸쳐 자연어로 질문할 수 있게 하여 두 경로를 연결합니다.
이러한 신호에 따라 조치하려는 팀은 **채택(Adopt)**으로 이동하여 패스키 인텔리전스를 추가함으로써 자동으로 세레모니를 게이팅하고, 등록 프롬프트를 최적화하고, 깨진 패스키 상태를 복구할 수 있습니다. 규제가 심한 환경이나 하이퍼스케일 배포의 경우 **엔터프라이즈(Enterprise)**는 단일 테넌트 호스팅, SIEM 통합 및 PSD2 준수 구성을 추가합니다.
엔터프라이즈용 무료 passkey 백서를 받으세요.
WebAuthn 오류 이름은 판결이 아닙니다. 이들은 힌트일 뿐이며 작업 유형, 타이밍 및 플랫폼 컨텍스트와 연결할 때만 조치 가능해집니다.
NotAllowedError), 앱 수명 주기/동시성(AbortError), 보안 컨텍스트/구성(SecurityError) 또는 옵션/상태 버그(InvalidStateError, ConstraintError, DataError)와 같은 소수의 계층에 매핑됩니다. 대다수의 볼륨은 NotAllowedError이며, 그 대부분은 예상된 동작(사용자가 프롬프트를 해제함)입니다.NotAllowedError를 어떻게 명확하게 구분할까요? 타이밍(즉각적 거부 vs 사용자 취소 vs 시간 초과), QR/하이브리드 표시기(가용성 불일치), 사용자 활성화 컨텍스트(특히 iOS/Safari에서) 및 작업 유형(조건부 UI vs 모달 로그인 vs 패스키 생성 vs 조건부 생성)을 사용합니다. 모든 NotAllowedError를 하나의 실패 모드로 취급하지 마세요.error.name은 조건부 생성이나 수동 패스키 생성(사용자가 대화 상자를 닫음) 중에 발생하는 것과는 완전히 다른 신호입니다. 오류와 함께 작업 유형을 기록하는 것이 일반적인 NotAllowedError를 조치 가능한 버킷으로 바꾸는 요소입니다.error.name, 작업 유형, 작업 시작부터 오류 발생까지의 시간, 흐름 유형, QR/하이브리드 UI 표시 여부, OS/브라우저/기기(버전 포함), 상관 관계 ID(auth_flow_id) 및 서버 확인 거부를 명시적 코드로 캡처하세요.모든 오류 유형에 걸쳐 적용되는 두 가지 경험 법칙: 사용자에게 원시 브라우저 오류를 절대 표시하지 말고 항상 명확한 대체 경로를 제공하세요. 로컬 시도와 QR/하이브리드 교차 기기 시도를 분리하세요. 이들은 서로 다른 이유로 실패하며 다른 수정 사항이 필요하기 때문입니다. 대규모에서는 브라우저, OS 버전 및 자격 증명 관리자 전반에 걸쳐 오류 분류를 유지 관리하는 것이 지속적인 작업입니다. 처음부터 이것을 구축하는 대신 유지 관리되는 패턴 라이브러리가 있는 관측 가능성 SDK를 사용하는 것을 고려하세요.
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 Connect는 analytics가 내장된 managed passkey를 제공합니다 (기존 IDP와 함께). VicRoads는 Corbado로 500만+ 사용자에게 passkey를 운영하고 있습니다 (passkey 활성화율 +80%). Passkey 전문가와 상담하기 →
웹에서 브라우저는 개인 정보 보호를 위해 두 가지 경우를 모두 NotAllowedError로 축소하므로 직접 구별할 수 없습니다. 대신 타이밍을 프록시로 사용하세요: 1초 미만의 오류는 일반적으로 환경 거부 또는 기능 누락을 의미하는 반면, 1-15초는 사용자가 대화 상자를 보고 닫았음을 시사합니다. 네이티브 iOS 및 Android 앱에서는 요청 전에 preferImmediatelyAvailableCredentials를 설정하면 UI가 표시되기 전에 깔끔한 "자격 증명 없음" 신호(iOS 코드 1005, Android GetCredentialRequest)가 제공됩니다.
최적화된 대규모 패스키 배포에서 기록된 WebAuthn 오류의 95% 이상은 시스템 실패가 아니라 예상되는 사용자 중단입니다. "사용자가 프롬프트를 해제함"을 실제 실패와 분리하지 않고 원시 error.name 개수만 추적하면 오류 지표가 부풀려지고 NotAllowedError 볼륨 안에 숨겨진 실제 회귀가 가려집니다. 작업 유형별로 개수를 나누고 사용자 취소 버킷을 SecurityError, ConstraintError 및 DataError와 같은 예기치 않은 오류와 별도로 취급하세요.
iOS 코드 1005(NotInteractive)는 ASAuthorizationController에 preferImmediatelyAvailableCredentials가 설정되었을 때 기기에서 패스키를 사용할 수 없었으며 사용자에게 UI가 표시되지 않았음을 의미합니다. 이는 개인 정보 보호 제약으로 인해 웹 브라우저가 제공할 수 없는 깔끔한 "이 기기에 패스키가 존재하지 않음" 신호입니다. Apple의 메시지는 30개 이상의 언어로 현지화되고 OS 버전에 따라 변경될 수 있으므로 localizedDescription이 아닌 항상 숫자 코드로 분류하세요.
조건부 생성 중에 NotAllowedError, AbortError 및 InvalidStateError는 모두 예상되는 결과이며 오류로 표시되는 대신 조용히 무시되어야 합니다. NotAllowedError는 자동 완성을 사용할 수 없거나 페이지가 포커스를 잃었음을 나타내고, InvalidStateError는 패스키가 이미 제공자에 존재함을 의미합니다. 호출을 시도하기 전에 getClientCapabilities()를 사용하여 conditionalCreate 지원을 확인하고 문서 가시성 상태를 검사하여 오류 수가 부풀려지는 것을 방지하세요.
작업 유형(조건부 UI, 모달 로그인, 수동 생성, 조건부 생성 또는 자동 추가), 작업 시작 후 오류 발생까지의 시간, OS 버전, 브라우저 버전, 하드웨어 모델, QR/하이브리드 UI 표시 여부 및 서버 측 로그와 결합할 상관 관계 ID를 캡처하세요. 챌린지 불일치, 잘못된 서명, 자격 증명을 찾을 수 없음과 같은 서버 확인 실패는 클라이언트 측 DOMException 이름과 동일한 버킷에 혼합되는 것이 아니라 명시적인 구조화된 코드로 로깅되어야 합니다. 이러한 신호가 함께 모여 방대한 대다수의 오류를 추측 없이 조치 가능한 버킷으로 분류할 수 있게 해줍니다.
관련 글
목차