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

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

비밀번호 대신 패스키를 사용하면 웹사이트에서 사용자 계정을 더 안전하고 간편하고 쉽게 사용할 수 있습니다. 패스키를 사용하면 사용자가 지문, 얼굴 또는 기기 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의 챌린지입니다. 이는 필수이지만 증명을 실행하지 않는 한 등록 중에 사용되지 않습니다. 증명은 여기에서 다루지 않는 고급 주제입니다.
  • 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
  • 공개 키

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

서버 측 패스키 등록에서 자세한 안내를 따르세요.

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

리소스