비밀번호 없는 로그인의 패스키 생성

패스키를 사용하면 사용자 계정을 더 안전하고 간편하고 쉽게 사용할 수 있습니다.

비밀번호 대신 패스키를 사용하면 웹사이트에서 사용자 계정을 더 안전하고 간편하고 쉽게 사용할 수 있습니다. 패스키를 사용하면 사용자가 지문, 얼굴 또는 기기 PIN만으로 웹사이트 또는 앱에 로그인할 수 있습니다.

패스키를 생성하여 사용자 계정과 연결해야 하며, 공개 키를 서버에 저장해야 사용자가 로그인할 수 있습니다.

작동 방식

사용자에게 패스키를 만들라는 메시지가 표시되는 경우는 다음과 같습니다.

  • 사용자가 비밀번호를 사용하여 로그인하는 경우
  • 사용자가 다른 기기의 패스키를 사용하여 로그인하는 경우(즉, authenticatorAttachmentcross-platform임)
  • 사용자가 패스키를 관리할 수 있는 전용 페이지

패스키를 만들려면 WebAuthn API를 사용합니다.

패스키 등록 흐름의 네 가지 구성요소는 다음과 같습니다.

  • 백엔드: 패스키에 관한 공개 키 및 기타 메타데이터를 저장하는 계정 데이터베이스를 보유한 백엔드 서버입니다.
  • 프런트엔드: 브라우저와 통신하고 가져오기 요청을 백엔드로 전송하는 프런트엔드입니다.
  • 브라우저: JavaScript를 실행하는 사용자의 브라우저입니다.
  • 인증자: 패스키를 생성하고 저장하는 사용자의 인증자입니다. 여기에는 브라우저와 동일한 기기(예: Windows Hello를 사용하는 경우) 또는 휴대전화와 같은 다른 기기에 있는 비밀번호 관리자가 포함될 수 있습니다.
패스키 등록 다이어그램

기존 사용자 계정에 새 패스키를 추가하는 과정은 다음과 같습니다.

  1. 사용자가 웹사이트에 로그인합니다.
  2. 사용자는 로그인한 후 예를 들어 '패스키 생성' 버튼을 눌러 프런트엔드에서 패스키를 생성하도록 요청합니다.
  3. 프런트엔드는 패스키를 만들기 위해 백엔드에서 사용자 정보, 챌린지, 제외할 사용자 인증 정보 ID와 같은 정보를 요청합니다.
  4. 프런트엔드는 navigator.credentials.create()를 호출하여 패스키를 만듭니다. 이 호출은 프로미스를 반환합니다.
  5. 패스키는 사용자가 기기의 화면 잠금을 사용하겠다고 동의한 후에 생성됩니다. 프로미스가 해결되고 공개 키 사용자 인증 정보가 프런트엔드로 반환됩니다.
  6. 프런트엔드는 공개 키 사용자 인증 정보를 백엔드로 전송하고 향후 인증을 위해 사용자 계정과 연결된 사용자 인증 정보 ID 및 공개 키를 저장합니다.

호환성

WebAuthn은 대부분의 브라우저에서 지원되지만 약간의 차이가 있습니다. 기기 지원 - passkeys.dev를 참고하여 패스키 생성을 지원하는 브라우저와 운영체제 조합을 알아보세요.

새 패스키 만들기

새 패스키 생성 요청에 대해 프런트엔드가 작동하는 방식은 다음과 같습니다.

기능 감지

'새 패스키 만들기' 버튼을 표시하기 전에 다음을 확인하세요.

  • 브라우저가 PublicKeyCredential를 사용하여 WebAuthn을 지원합니다.

브라우저 지원

  • Chrome: 67
  • Edge: 18.
  • Firefox: 60
  • Safari: 13

소스

  • 기기가 PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()를 사용하여 플랫폼 인증자(패스키를 생성하고 패스키로 인증할 수 있음)를 지원합니다.

브라우저 지원

  • Chrome: 67
  • Edge: 18.
  • Firefox: 60
  • Safari: 13

소스

  • 브라우저는 PublicKeyCredenital.isConditionalMediationAvailable()를 사용하여 WebAuthn 조건부 UI를 지원합니다.

브라우저 지원

  • Chrome: 108
  • Edge: 108
  • Firefox: 119
  • Safari: 16

소스

// Availability of `window.PublicKeyCredential` means WebAuthn is usable.  
// `isUserVerifyingPlatformAuthenticatorAvailable` means the feature detection is usable.  
// `​​isConditionalMediationAvailable` means the feature detection is usable.  
if (window.PublicKeyCredential &&  
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&  
    PublicKeyCredential.​​isConditionalMediationAvailable) {  
  // Check if user verifying platform authenticator is available.  
  Promise.all([  
    PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),  
    PublicKeyCredential.​​isConditionalMediationAvailable(),  
  ]).then(results => {  
    if (results.every(r => r === true)) {  
      // Display "Create a new passkey" button  
    }  
  });  
}  

모든 조건이 충족될 때까지는 이 브라우저에서 패스키가 지원되지 않습니다. 그때까지는 '새 패스키 만들기' 버튼이 표시되지 않습니다.

백엔드에서 중요한 정보 가져오기

사용자가 버튼을 클릭하면 중요한 정보를 가져와 백엔드에서 navigator.credentials.create()를 호출합니다.

  • challenge: 이 등록에 관한 서버에서 생성한 ArrayBuffer의 챌린지입니다. 이는 필수이지만 이 Codelab에서 다루지 않는 고급 주제인 증명을 실행하지 않는 한 등록 중에 사용되지 않습니다.
  • user.id: 사용자의 고유 ID입니다. 이 값은 이메일 주소나 사용자 이름과 같은 개인 식별 정보가 포함되지 않은 ArrayBuffer여야 합니다. 계정당 생성된 임의의 16바이트 값이 좋습니다.
  • user.name: 이 필드에는 이메일 주소나 사용자 이름 등 사용자가 인식할 계정의 고유 식별자가 있어야 합니다. 계정 선택기에 표시됩니다. 사용자 이름을 사용하는 경우 비밀번호 인증에서와 동일한 값을 사용하세요.
  • user.displayName: 이 필드는 계정의 사용자 친화적인 필수 이름입니다. 고유하지 않아도 되며 사용자가 선택한 이름일 수 있습니다. 사이트에 여기에 포함할 수 있는 적합한 값이 없는 경우 빈 문자열을 전달합니다. 브라우저에 따라 계정 선택기에 표시될 수 있습니다.
  • excludeCredentials: 이미 등록된 사용자 인증 정보 ID 목록을 제공하여 동일한 기기를 등록하지 못하게 합니다. 제공되는 경우 transports 구성원에는 각 사용자 인증 정보를 등록하는 동안 getTransports()를 호출한 결과가 포함되어야 합니다.

WebAuthn API를 호출하여 패스키 생성

navigator.credentials.create()를 호출하여 새 패스키를 만듭니다. API는 모달 대화상자를 표시하는 사용자 상호작용을 기다리는 약속을 반환합니다.

브라우저 지원

  • Chrome: 60
  • Edge: 18.
  • Firefox: 60
  • Safari: 13.

소스

const publicKeyCredentialCreationOptions = {
  challenge: *****,
  rp: {
    name: "Example",
    id: "example.com",
  },
  user: {
    id: *****,
    name: "john78",
    displayName: "John",
  },
  pubKeyCredParams: [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}],
  excludeCredentials: [{
    id: *****,
    type: 'public-key',
    transports: ['internal'],
  }],
  authenticatorSelection: {
    authenticatorAttachment: "platform",
    requireResidentKey: true,
  }
};

const credential = await navigator.credentials.create({
  publicKey: publicKeyCredentialCreationOptions
});

// Encode and send the credential to the server for verification.  

위에 설명되지 않은 매개변수는 다음과 같습니다.

  • rp.id: RP ID는 도메인이며 웹사이트에서 도메인 또는 등록 가능한 접미사를 지정할 수 있습니다. 예를 들어 RP의 출처가 https://login.example.com:1337인 경우 RP ID는 login.example.com 또는 example.com일 수 있습니다. RP ID가 example.com으로 지정된 경우 사용자는 login.example.com 또는 example.com의 하위 도메인에서 인증할 수 있습니다.

  • rp.name: RP의 이름입니다.

  • pubKeyCredParams: 이 입력란은 RP에서 지원되는 공개 키 알고리즘을 지정합니다. [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}]로 설정하는 것이 좋습니다. P-256 및 RSA PKCS#1을 사용하는 ECDSA 지원을 지정하며, 지원을 통해 완전한 적용 범위를 제공합니다.

  • authenticatorSelection.authenticatorAttachment: 패스키 생성이 비밀번호에서 업그레이드된 경우(예: 로그인 후 프로모션) "platform"로 설정합니다. "platform"는 RP가 USB 보안 키 등을 삽입하라는 메시지를 표시하지 않는 플랫폼 인증자(플랫폼 기기에 삽입된 인증자)를 원함을 나타냅니다. 사용자가 패스키를 만드는 더 간단한 옵션이 있습니다.

  • authenticatorSelection.requireResidentKey: 부울 'true'로 설정합니다. 검색 가능한 사용자 인증 정보 (상주 키)는 사용자 정보를 패스키에 저장하고 사용자가 인증 시 계정을 선택할 수 있도록 합니다. 검색 가능한 사용자 인증 정보 심층 분석에서 검색 가능한 사용자 인증 정보에 대해 자세히 알아보세요.

  • authenticatorSelection.userVerification: 기기 화면 잠금을 사용하는 사용자 확인이 "required"인지, "preferred"인지, "discouraged"인지를 나타냅니다. 기본값은 "preferred"이며, 이는 인증자가 사용자 인증을 건너뛸 수 있음을 의미합니다. "preferred"로 설정하거나 속성을 생략합니다.

반환된 공개 키 사용자 인증 정보를 백엔드로 전송

사용자가 기기의 화면 잠금을 사용하겠다고 동의하면 패스키가 생성되고 프로미스가 해결되어 프런트엔드에 PublicKeyCredential 객체를 반환합니다.

약속은 다양한 이유로 거부될 수 있습니다. Error 객체의 name 속성을 확인하여 이러한 오류를 처리할 수 있습니다.

  • InvalidStateError: 기기에 이미 패스키가 있습니다. 사용자에게 오류 대화상자가 표시되지 않으며 사이트에서 이를 오류로 처리해서는 안 됩니다. 사용자가 로컬 기기를 등록하기를 원했고 기기가 등록되었기 때문입니다.
  • NotAllowedError: 사용자가 작업을 취소했습니다.
  • 기타 예외: 예상치 못한 문제가 발생했습니다. 브라우저에서 사용자에게 오류 대화상자를 표시합니다.

공개 키 사용자 인증 정보 객체에는 다음 속성이 포함되어 있습니다.

  • id: 생성된 패스키의 Base64URL로 인코딩된 ID입니다. 이 ID는 인증 시 브라우저에서 기기에 일치하는 패스키가 있는지 확인하는 데 도움이 됩니다. 이 값은 백엔드의 데이터베이스에 저장해야 합니다.
  • rawId: 사용자 인증 정보 ID의 ArrayBuffer 버전입니다.
  • response.clientDataJSON: ArrayBuffer로 인코딩된 클라이언트 데이터입니다.
  • response.attestationObject: ArrayBuffer로 인코딩된 증명 객체입니다. 여기에는 RP ID, 플래그, 공개 키와 같은 중요한 정보가 포함되어 있습니다.
  • authenticatorAttachment: 이 사용자 인증 정보가 패스키 지원 기기에서 생성되면 "platform"을 반환합니다.
  • type: 이 필드는 항상 "public-key"로 설정됩니다.

라이브러리를 사용하여 백엔드에서 공개 키 사용자 인증 정보 객체를 처리하는 경우 base64url로 부분적으로 인코딩한 후 전체 객체를 백엔드로 전송하는 것이 좋습니다.

사용자 인증 정보 저장

백엔드에서 공개 키 사용자 인증 정보를 수신하면 FIDO 라이브러리에 전달하여 객체를 처리합니다.

그런 다음 사용자 인증 정보에서 가져온 정보를 나중에 사용할 수 있도록 데이터베이스에 저장할 수 있습니다. 다음 목록에는 일반적으로 저장할 속성이 포함되어 있습니다.

  • 사용자 인증 정보 ID(기본 키)
  • 사용자 ID
  • 공개 키

공개 키 사용자 인증 정보에는 데이터베이스에 저장할 수 있는 다음 정보도 포함됩니다.

서버 측 패스키 등록에서 자세한 안내를 따릅니다.

사용자를 인증하려면 양식 자동 완성을 통해 패스키로 로그인을 참고하세요.

리소스