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

패스키와 같은 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()에서 반환된 약속이 결과와 함께 확인됩니다. 사용자가 패스키를 선택하지 않으면 약속은 해결되지 않습니다.

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를 함께 사용하여 다음과 같은 로그인 환경을 달성할 수 있습니다.

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

검색 가능한 사용자 인증 정보를 현명하게 사용합니다. 이렇게 하면 사용자가 원활하게 이용하고 참여할 가능성이 더 높은 정교한 패스키 로그인 환경을 설계할 수 있습니다.