WebAuthn 및 패스키 구현에 관한 개발자 가이드입니다. 치트 시트를 PDF로 다운로드하거나 이 웹사이트에서 모든 정보를 한곳에서 확인하세요.
Lukas R.
작성일: 2024년 3월 6일
업데이트: 2026년 7월 3일

이 페이지는 자동 번역되었습니다. 영어 원문은 여기.
전체 패스키 치트 시트를 무료로 다운로드하고 모든 인사이트를 얻어보세요.
궁극의 패스키 개발자 가이드
플랫폼 지원, 브라우저 동작, UX 모범 사례 및 연동 팁 등 패스키에 대한 모든 것을 다루는 개발자 중심의 레퍼런스를 확인하세요.

패스키를 이용한 인증은 등록(또는 증명 단계)과 로그인(또는 어서션 단계)이라는 두 가지 프로세스(세레모니)를 기반으로 합니다.
각 단계는 서버에서 생성한 무작위 챌린지를 요구하며, 이 챌린지는 인증기에 의해 서명되고 사용자를 확인하기 위해 WebAuthn 서버로 다시 전송됩니다.
Passkeys Debugger에서 passkey 흐름을 실험하세요.
등록 세레모니는 PublicKeyCredentialCreationOptions와 증명이라는 두 가지 핵심 객체를 사용합니다.
로그인 세레모니는 PublicKeyCredentialRequestOptions와 어서션이라는 두 가지 핵심 객체를 사용합니다.
실제로 얼마나 많은 사람이 passkeys를 쓰는지 확인하세요.
패스키를 이용한 등록 및 로그인에는 다음 네 가지 주요 객체가 있습니다.
이 섹션에서는 PublicKeyCredentialCreationOptions에서 사용되는 authenticatorSelection 객체에 대해서도 설명합니다.
Igor Gjorgjioski
Head of Digital Channels & Platform Enablement, VicRoads
We hit 80% mobile passkey activation across 5M+ users without replacing our IDP.
See how VicRoads scaled passkeys to 5M+ users — alongside their existing IDP.
Read the case studyPublicKeyCredentialCreationOptions는 증명 단계(등록)의 핵심 객체입니다. WebAuthn 서버에서 생성되어 반환됩니다.
{ "PublicKeyCredentialCreationOptions": { "rp": { "id": "passkeys.eu", "name": "Corbado Passkeys Demo" }, "user": { "displayName": "john.doe", "id": "dXNyLZ….DU10Tc", "name": "john@doe.com" }, "challenge": "888fix4Bus...pHHr3Y", "pubKeyCredParams": [ { "alg": -7, "type": "public-key" }, { "alg": -257, "type": "public-key" } ], "excludeCredentials": [], "authenticatorSelection": { "authenticatorAttachment": "platform", "residentKey": "required", "userVerification": "required" }, "attestation": "none", "extensions": {} } }
이 객체는 다음 속성을 포함합니다.
최신 뉴스를 위해 Passkeys Substack을 구독하세요.
PublicKeyCredentialRequestOptions는 어서션 단계(로그인)의 핵심 객체입니다. WebAuthn 서버에서 생성되어 반환됩니다.
{ "publicKeyCredentialRequestOptions": { "challenge": "pT7HMA-…dFPHk", "timeout": 500, "rpId": "passkeys.eu", "userVerification": "preferred", "allowCredentials": [], "extensions": [] } }
이 객체는 다음 속성을 포함합니다.
증명 / 등록 세레모니 중에 인증기는 이 등록 응답을 반환합니다. Passkeys Debugger에서 직접 테스트해 볼 수 있습니다.
{ "authenticatorAttachment": "platform", "id": "JKZbixUfKN_aZtimefYT-OjH5dw", "rawId": "JKZbixUfKN_aZtimefYT-OjH5dw", "response": { "attestationObject": { "fmt": "none", "attStmt": {}, "authData": { "rpIdHash": "PpZrl-Wqt-OFfBpyy2SraN1m7LT0GZORwGA7-6ujYkM", "flags": { "userPresent": true, "userVerified": true, "backupEligible": true, "backupStatus": true, "attestedData": true, "extensionData": false }, "counter": 0, "aaguid": { "raw": "fbfc3007-154e-4ecc-8c0b-6e020557d7bd", "name": "iCloud Keychain" }, "credentialID": "JKZbixUfKN_aZtimefYT-OjH5dw", "credentialPublicKey": "pQECAyYgASFYIPWLalDzyxIDmAADvfK8iNM5To50kh7TyPH-teEz8RMdIlgg3D7bPIWQJ8z-WFn3zdYZzJw9c7mhPdmflQqD9vV7efA", "parsedCredentialPublicKey": { "keyType": "EC2 (2)", "algorithm": "ES256 (-7)", "curve": 1, "x": "9YtqUPPLEgOYAAO98ryI0zlOjnSSHtPI8f614TPxEx0", "y": "3D7bPIWQJ8z-WFn3zdYZzJw9c7mhPdmflQqD9vV7efA" } } }, "clientDataJSON": { "type": "webauthn.create", "challenge": "k2p6f-upzP_hc6NZvmMAxiI0VSTeQIeXXVRGW62LTj0", "origin": "https://www.passkeys-debugger.io", "crossOrigin": false }, "transports": ["hybrid", "internal"], "authenticatorData": "PpZrl-Wqt-OFfBpyy2SraN1m7LT0GZORwGA7-6ujYkNdAAAAAPv8MAcVTk7MjAtuAgVX170AFCSmW4sVHyjf2mbYpnn2E_jox-XcpQECAyYgASFYIPWLalDzyxIDmAADvfK8iNM5To50kh7TyPH-teEz8RMdIlgg3D7bPIWQJ8z-WFn3zdYZzJw9c7mhPdmflQqD9vV7efA", "publicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE9YtqUPPLEgOYAAO98ryI0zlOjnSSHtPI8f614TPxEx3cPts8hZAnzP5YWffN1hnMnD1zuaE92Z-VCoP29Xt58A", "publicKeyAlgorithm": -7 }, "type": "public-key", "clientExtensionResults": {} }
증명에는 attestationObject, algorithm 및 transport 플래그와 같은 몇 가지 중요한 구성 요소가 포함되어 있습니다.
출처: W3C의 WebAuthn 사양
attestationObject는 CBOR 인코딩된 객체로, 새로 생성된 자격 증명, 공개 키 및 기타 관련 데이터에 대한 정보를 포함합니다.
확장에 대해 자세히 알아보세요.
패스키는 COSE 알고리즘으로 생성되며, 증명 객체의 parsedCredentialPublicKey의 algorithm 속성에 사용된 알고리즘을 나타냅니다.
다음은 가장 관련성이 높은 COSE 알고리즘에 대한 개요입니다.
transports 속성은 인증기가 클라이언트와 통신할 수 있는 메커니즘을 나타냅니다. 일반적인 샘플 값 조합은 다음과 같습니다.
어서션 / 로그인 세레모니 중에 인증기는 이 로그인 응답을 반환합니다. Passkeys Debugger에서 직접 테스트해 볼 수 있습니다.
{ "id": "JKZbixUfKN_aZtimefYT-OjH5dw", "rawId": "JKZbixUfKN_aZtimefYT-OjH5dw", "type": "public-key", "authenticatorAttachment": "platform", "response": { "authenticatorData": { "rpIdHash": "PpZrl-Wqt-OFfBpyy2SraN1m7LT0GZORwGA7-6ujYkM", "flags": { "userPresent": true, "userVerified": true, "backupEligible": true, "backupStatus": true, "attestedData": false, "extensionData": false }, "counter": 0 }, "clientDataJSON": { "type": "webauthn.get", "challenge": "GCVkITWbe2l2dttsn_DgJYvH9QPHPDo0ygWgcgI6B7U", "origin": "https://www.passkeys-debugger.io", "crossOrigin": false, "other_keys_can_be_added_here": "do not compare clientDataJSON against a template. See https://goo.gl/yabPex" }, "signature": "MEQCIA-orC8N2KKWOxyY17BWP8lB-Be5to9btXRnJZf2SLhXAiBGxJe5Eu5LwOTbsyzAYmIXHOhlC3pN7s7Q1fRLvEW57g", "userHandle": "_FKz1uwqmR_3yGq6hJntzoIFwFC_d1u_53YRELh0KlE" } }
어서션에는 flags, signature 및 userHandle과 같은 몇 가지 중요한 구성 요소가 포함되어 있습니다.
다음은 가장 관련성이 높은 플래그와 그 조합에 대한 개요입니다.
서명은 로그인하려는 사용자가 실제로 개인 키를 가지고 있는지 확인하는 데 사용됩니다. 서명은 authenticatorData와 clientDataHash(즉, ClientDataJSON의 SHA-256 버전)를 연결하고 그 결과를 (인증기 내의) 개인 키로 서명하여 생성됩니다. 공개 키로 확인하기 위해 authenticatorData와 clientDataHash도 연결합니다. 확인 결과가 참을 반환하면 인증에 성공한 것입니다.
userHandle은 실제 user_id입니다. user_id에 대한 자세한 내용은 섹션 4.1 데이터베이스 스키마를 참조하세요.
authenticatorSelection 객체를 통해 서버는 다음 값을 사용하여 인증기 및 자격 증명 생성에 대한 설정을 지시할 수 있습니다.
상주 키(검색 가능한 자격 증명이라고도 함): 상주 키는 인증기에 저장되고 인증 중에 검색됩니다. 이렇게 하면 클라이언트가 가능한 키 목록을 검색할 수 있으며, 이것이 텍스트 패스키로 Conditional UI를 구현할 때 상주 키가 필요한 이유입니다. 비상주 키(검색 불가능한 자격 증명이라고도 함): 비상주 키의 경우 자격 증명 ID가 서버에 저장되고 인증 중에 제공됩니다. 자격 증명 ID는 내부 구조가 구현에 따라 달라지는 불투명한 식별자입니다. 인증기는 개인 키를 직접 저장하거나 암호화된 키 래핑을 사용하거나 내부 비밀에서 키를 파생시킬 수 있습니다. 정확한 메커니즘은 인증기 구현에 따라 다릅니다.
경고: "Preferred"로 설정된 경우 사용자 또는 사용자의 기기는 인증 프로세스에서 사용자 확인을 건너뛸 수 있습니다(자세한 내용은 이 기사를 참조하세요).
Conditional UI(패스키 자동 완성)는 사용자가 신뢰 당사자에 등록된 상주 키를 가지고 있을 때 사용 가능한 패스키를 사용자를 위한 선택 드롭다운에 표시합니다. 패스키의 사용성을 향상시키지만 추가 개발 노력이 필요하며 모든 OS / 브라우저 조합에서 사용할 수 있는 것은 아닙니다.
일반적인 로그인과 마찬가지로 Conditional UI도 PublicKeyCredentialRequestOptions 및 어서션 객체를 사용합니다.
Conditional UI는 (아직) 모든 운영 체제 및 브라우저 조합에서 사용할 수 있는 것은 아닙니다. 다음은 현재 브라우저 지원 범위(2024년 3월 기준)에 대한 개요입니다.
최신 개요는 이 웹사이트를 참조하세요.
Conditional UI 메소드의 전체적이고 간결한 코드는 다음과 같습니다.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Conditional UI</title> </head> <body> <input type="text" id="username" autocomplete="username webauthn" /> <script> async function passkeyLogin() { try { // WebAuthn 서버에서 챌린지를 포함한 요청 옵션을 검색합니다. let options = await WebAuthnClient.getPublicKeyRequestOptions(); const credential = await navigator.credentials.get({ publicKey: options.publicKeyCredentialRequestOptions, mediation: "conditional", }); const userData = await WebAuthnClient.sendSignedChallenge(credential); window.location.href = "/logged-in"; } catch (error) { console.log(error); } } passkeyLogin(); </script> </body> </html>
Conditional UI는 상주 키 / 검색 가능한 자격 증명에서만 작동합니다.
Conditional UI 로그인을 시작하려면 별도의 서버 엔드포인트를 제공하는 것이 좋습니다.
클라이언트는 여러 요구 사항을 충족해야 합니다.
오류를 방지하려면 서버는 먼저 이 함수를 사용하여 클라이언트의 가용성을 테스트해야 합니다. 실제 트래픽에서 감지 및 수명 주기 문제는 종종 NotAllowedError 또는 AbortError로 나타납니다. 네이티브 자격 증명 관리자 패스키 오류를 포함하여 예상되는 오류와 예상치 못한 오류를 분류하려면 이 WebAuthn 오류 가이드를 사용하세요.
// 출처: https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredential/isConditionalMediationAvailable#examples // `window.PublicKeyCredential`의 가용성은 WebAuthn을 사용할 수 있음을 의미합니다. if (window.PublicKeyCredential && PublicKeyCredential.isConditionalMediationAvailable) { // 조건부 중재가 가능한지 확인합니다. const isCMA = await PublicKeyCredential.isConditionalMediationAvailable(); if (isCMA) { // WebAuthn 인증 시작 엔드포인트 호출 let options = await WebAuthnClient.getPublicKeyRequestOptions(); const credential = await navigator.credentials.get({ publicKey: options.publicKeyCredentialRequestOptions, mediation: "conditional", }); /* ... */ } }
입력 필드는 진행 중인 요청에 패스키를 채우도록 클라이언트에 신호를 보내는 HTML 자동 완성 토큰을 수신해야 합니다. 패스키 외에도 자동 완성 토큰은 사용자 이름 및 비밀번호와 같은 기존 토큰과 페어링될 수 있습니다.
<label for="name">사용자 이름:</label> <input type="text" name="name" autocomplete="username webauthn" /> <label for="password">비밀번호:</label> <input type="password" name="password" autocomplete="current-password webauthn" />
WebAuthn 서버에 대한 필수적이거나 표준화된 데이터베이스 스키마는 없습니다. 그러나 이 예제 데이터베이스 스키마를 사용하여 필수 정보를 저장하고 WebAuthn 서버의 모든 기능을 제공할 수 있습니다.
굵게 표시된 속성은 최소한의 실행 가능한 구현에 필수적이며, 다른 속성은 선택적이지만 유용한 기능에만 필요합니다.
User DisplayName (user.displayName): 일반적으로 사용자의 전체 이름인 사용자 친화적이고 읽기 쉬운 이름입니다. 사용자에게 표시되지만 인증 중에는 사용되지 않습니다.
User Name (user.name): 일반적으로 이메일 주소 또는 사용자 이름인 고유하고 읽기 쉬운 이름입니다. 사용자에게 표시될 수 있지만 인증 중에는 사용되지 않습니다.
**신뢰 당사자 ID(rpID)**는 패스키 내에 저장된 도메인으로, 패스키가 올바른 도메인(브라우저 URL, 네이티브 앱의 경우 이 기사 참조)에서만 작동하도록 보장합니다. 인증 중에 rpID는 브라우저 URL에 대해 확인되며 다음 두 가지 경우에만 허용됩니다.
다음은 허용되거나 허용되지 않는 조합의 예입니다.
다음은 패스키 구현에 유용한 도구 및 웹사이트 목록입니다.
chrome://device-log를 통해 Chrome에서만 사용 가능).기술적 구현을 넘어 패스키 UX를 최적화하는 전략은 패스키 생성 모범 사례 및 패스키 로그인 모범 사례 가이드를 참조하세요.
단 몇 줄의 코드로 모든 애플리케이션에 패스키를 구현하려면 Corbado Complete(신규 앱의 경우) 또는 Corbado Connect(기존 앱의 경우)를 사용할 수도 있습니다.
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 전문가와 상담하기 →
Conditional UI는 인증을 시작하기 전에 PublicKeyCredential.isConditionalMediationAvailable()을 통해 브라우저 지원 여부를 확인해야 합니다. 입력 필드는 autocomplete="username webauthn" HTML 토큰을 포함해야 하며 사용자는 상주 키(검색 가능한 자격 증명)를 등록해야 합니다. Conditional UI 로그인 흐름을 처리하기 위해 별도의 서버 엔드포인트를 사용하는 것이 좋습니다.
최소한 등록 중에 인증기에서 생성된 Credential ID와 어서션 객체에서 userHandle로 반환되는 User ID(user_id)를 저장해야 합니다. Credential ID를 사용하여 연관된 사용자 계정을 찾고 userHandle을 비교하여 인증을 확인합니다. 시간이 지남에 따라 변경될 수 있으므로 비교를 위해 user.name을 사용하지 마세요.
상주 키(검색 가능한 자격 증명)는 인증기 자체에 저장되고 인증 중에 검색되며, 이는 Conditional UI가 작동하는 데 필요합니다. 비상주 키는 자격 증명 ID를 서버에 저장하고 인증 중에 인증기로 보냅니다. authenticatorSelection의 residentKey 필드는 "required", "preferred" 또는 "discouraged" 값으로 이 동작을 제어합니다.
userVerification 필드는 로그인 중에 인증기가 사용자를 확인해야 하는지 여부를 제어하며 "required", "preferred"(기본값) 또는 "discouraged" 값을 허용합니다. "preferred"로 설정하면 사용자 또는 사용자의 기기가 인증 프로세스 중에 확인을 완전히 건너뛸 수 있어 보안이 약화될 수 있습니다. "required"로 설정하면 인증이 완료되기 전에 항상 확인이 발생합니다.
관련 글
목차