검색 가능한 사용자 인증 정보 자세히 알아보기

패스키와 같은 FIDO 사용자 인증 정보는 비밀번호 대체를 목적으로 하지만, 대부분의 경우 사용자가 사용자 이름을 입력하지 않아도 됩니다. 이렇게 하면 사용자가 현재 웹사이트에 대한 패스키 목록에서 계정을 선택하여 인증할 수 있습니다.

이전 버전의 보안 키는 2단계 인증 방법으로 설계되었으며, 잠재적 사용자 인증 정보의 ID가 필요했기 때문에 사용자 이름을 입력해야 합니다. 보안 키가 ID를 모르더라도 찾을 수 있는 사용자 인증 정보를 검색 가능한 사용자 인증 정보라고 합니다. 현재 생성되는 대부분의 FIDO 사용자 인증 정보는 검색 가능한 사용자 인증 정보이며, 특히 비밀번호 관리자나 최신 보안 키에 저장된 패스키입니다.

사용자 인증 정보를 검색할 수 있도록 하려면 패스키를 만들 때 residentKeyrequireResidentKey를 지정합니다.

신뢰 당사자 (RP)는 패스키 인증 중에 allowCredentials를 생략하여 검색 가능한 사용자 인증 정보를 사용할 수 있습니다. 이러한 경우 브라우저나 시스템은 생성 시 설정된 user.name 속성으로 식별되는 사용 가능한 패스키 목록을 사용자에게 표시합니다. 사용자가 하나를 선택하면 user.id 값이 결과 서명에 포함됩니다. 그러면 서버는 입력된 사용자 인증 정보 또는 반환된 사용자 인증 정보 ID를 사용하여 입력된 사용자 이름 대신 계정을 조회할 수 있습니다.

앞에서 설명한 것과 같이 계정 선택기 UI는 검색할 수 없는 사용자 인증 정보를 표시하지 않습니다.

requireResidentKeyresidentKey

검색 가능한 사용자 인증 정보를 만들려면 다음과 같이 표시된 값을 사용하여 navigator.credentials.create()에서 authenticatorSelection.residentKeyauthenticatorSelection.requireResidentKey를 지정합니다.

async function register () {
  // ...

  const publicKeyCredentialCreationOptions = {
    // ...
    authenticatorSelection: {
      authenticatorAttachment: 'platform',
      residentKey: 'required',
      requireResidentKey: true,
    }
  };

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

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;

  // ...
}

residentKey:

  • 'required': 검색 가능한 사용자 인증 정보를 만들어야 합니다. 생성할 수 없으면 NotSupportedError이 반환됩니다.
  • 'preferred': RP가 검색 가능한 사용자 인증 정보 생성을 선호하지만 검색할 수 없는 사용자 인증 정보는 허용합니다.
  • 'discouraged': RP가 검색할 수 없는 사용자 인증 정보를 만드는 것을 선호하지만 검색 가능한 사용자 인증 정보는 허용합니다.

requireResidentKey:

  • 이 속성은 이전 버전 사양인 WebAuthn 레벨 1과 하위 호환성을 위해 유지됩니다. residentKey'required'이면 true로 설정하고 그렇지 않으면 false로 설정합니다.

allowCredentials

RP는 navigator.credentials.get()allowCredentials를 사용하여 패스키 인증 환경을 제어할 수 있습니다. 패스키 인증 환경에는 일반적으로 세 가지 유형이 있습니다.

검색 가능한 사용자 인증 정보를 통해 RP는 사용자가 로그인할 계정을 선택하고 그 다음에 사용자를 확인할 수 있는 모달 계정 선택기를 표시할 수 있습니다. 이는 패스키 인증 전용 버튼을 눌러 시작되는 패스키 인증 흐름에 적합합니다.

이 사용자 환경을 제공하려면 빈 배열을 생략하거나 navigator.credentials.get()allowCredentials 매개변수에 전달합니다.

async function authenticate() {
  // ...

  const publicKeyCredentialRequestOptions = {
    // Server generated challenge:
    challenge: ****,
    // The same RP ID as used during registration:
    rpId: 'example.com',
    // You can omit `allowCredentials` as well:
    allowCredentials: []
  };

  const credential = await navigator.credentials.get({
    publicKey: publicKeyCredentialRequestOptions,
    signal: abortController.signal
  });

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;
  
  // ...
}

패스키 양식 자동 완성 표시

대부분의 사용자가 패스키를 사용하고 로컬 기기에서 사용할 수 있는 경우 위에서 설명한 모달 계정 선택기가 잘 작동합니다. 로컬 패스키가 없는 사용자의 경우에도 모달 대화상자가 계속 표시되며 사용자에게 다른 기기의 패스키를 제시하라는 메시지가 표시됩니다. 사용자를 패스키로 전환하는 동안 패스키를 설정하지 않은 사용자에게는 해당 UI를 사용하지 않는 것이 좋습니다.

대신 선택한 패스키가 저장된 사용자 이름 및 비밀번호와 함께 기존 로그인 양식의 입력란에 대한 자동 완성 프롬프트로 폴딩될 수 있습니다. 이렇게 하면 패스키가 있는 사용자는 패스키를 선택하여 로그인 양식을 '채울' 수 있고, 저장된 사용자 이름/비밀번호 쌍을 가진 사용자는 해당 패스키를 선택할 수 있으며, 둘 다 없는 사용자는 사용자 이름과 비밀번호를 계속 입력할 수 있습니다.

이 사용자 환경은 RP가 비밀번호와 패스키를 함께 사용하는 마이그레이션 중일 때 이상적입니다.

이 사용자 환경을 구현하려면 allowCredentials 속성에 빈 배열을 전달하거나 매개변수를 생략하는 것 외에도 navigator.credentials.get()에서 mediation: 'conditional'를 지정하고 HTML username 입력란에 autocomplete="username webauthn"로, password 입력란에 autocomplete="password webauthn"로 주석을 추가합니다.

navigator.credentials.get()를 호출해도 UI가 표시되지 않지만 사용자가 주석이 달린 입력란에 포커스를 맞추면 사용 가능한 모든 패스키가 자동 완성 옵션에 포함됩니다. 사용자가 하나를 선택하면 일반 기기 잠금 해제 확인을 거치게 되며, 이후 .get()에서 반환한 promise가 결과로 확인됩니다. 사용자가 패스키를 선택하지 않으면 Promise가 결정되지 않습니다.

async function authenticate() {
  // ...

  const publicKeyCredentialRequestOptions = {
    // Server generated challenge:
    challenge: ****,
    // The same RP ID as used during registration:
    rpId: 'example.com',
    // You can omit `allowCredentials` as well:
    allowCredentials: []
  };

  const cred = await navigator.credentials.get({
    publicKey: publicKeyCredentialRequestOptions,
    signal: abortController.signal,
    // Specify 'conditional' to activate conditional UI
    mediation: 'conditional'
  });

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;
  
  // ...
}
<input type="text" name="username" autocomplete="username webauthn" ...>

이러한 사용자 환경을 빌드하는 방법은 양식 자동 완성을 통해 패스키로 로그인웹 앱에서 양식 자동 완성으로 패스키 구현하기 Codelab을 참고하세요.

재인증

재인증에 패스키를 사용하는 경우와 같은 일부 경우에는 사용자의 식별자가 이미 알려져 있습니다. 이 경우 브라우저나 OS에서 계정 선택기를 표시하지 않고 패스키를 사용하려고 합니다. 이렇게 하려면 allowCredentials 매개변수에 사용자 인증 정보 ID 목록을 전달하면 됩니다.

이 경우, 명명된 사용자 인증 정보 중 하나라도 로컬에서 사용할 수 있는 경우 기기 잠금 해제를 요청하는 메시지가 사용자에게 즉시 표시됩니다. 그렇지 않은 경우 유효한 사용자 인증 정보가 포함된 다른 기기 (휴대전화 또는 보안 키)를 제시하라는 메시지가 표시됩니다.

이러한 사용자 환경을 제공하려면 로그인한 사용자의 사용자 인증 정보 ID 목록을 제공하세요. 사용자가 이미 알려져 있으므로 RP에서 이를 쿼리할 수 있어야 합니다. navigator.credentials.get()allowCredentials 속성에 사용자 인증 정보 ID를 PublicKeyCredentialDescriptor 객체로 제공합니다.

async function authenticate() {
  // ...

  const publicKeyCredentialRequestOptions = {
    // Server generated challenge:
    challenge: ****,
    // The same RP ID as used during registration:
    rpId: 'example.com',
    // Provide a list of PublicKeyCredentialDescriptors:
    allowCredentials: [{
      id: ****,
      type: 'public-key',
      transports: [
        'internal',
        'hybrid'
      ]
    }, {
      id: ****,
      type: 'public-key',
      transports: [
        'internal',
        'hybrid'
      ]
    }, ...]
  };

  const credential = await navigator.credentials.get({
    publicKey: publicKeyCredentialRequestOptions,
    signal: abortController.signal
  });

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;
  
  // ...
}

PublicKeyCredentialDescriptor 객체는 다음으로 구성됩니다.

  • id: RP가 패스키 등록 시 획득한 공개 키 사용자 인증 정보의 ID입니다.
  • type: 이 필드는 일반적으로 'public-key'입니다.
  • transports: 이 사용자 인증 정보를 보유한 기기에서 지원하는 전송의 힌트로, 브라우저에서 사용자에게 외부 기기를 표시하도록 요청하는 UI를 최적화하는 데 사용됩니다. 이 목록이 제공되는 경우 목록에 각 사용자 인증 정보를 등록하는 동안 getTransports()를 호출한 결과가 포함되어야 합니다.

요약

검색 가능한 사용자 인증 정보를 사용하면 사용자 이름 입력을 건너뛸 수 있어 훨씬 더 사용자 친화적인 패스키 로그인 환경을 만들 수 있습니다. RP는 residentKey, requireResidentKey, allowCredentials의 조합을 사용하여 다음과 같은 로그인 환경을 달성할 수 있습니다.

  • 모달 계정 선택기를 표시합니다.
  • 패스키 양식 자동 완성을 표시합니다.
  • 재인증.

검색 가능한 사용자 인증 정보를 현명하게 사용하세요. 이를 통해 사용자에게 원활하고 참여도가 높은 정교한 패스키 로그인 환경을 설계할 수 있습니다.