بررسی دقیق اعتبارنامه‌های قابل‌کشف

درحالی‌که اعتبارنامه‌های FIDO مانند گذرکلیدها با هدف جایگزینی گذرواژه‌ها طراحی شده‌اند، اکثر آن‌ها می‌توانند کاربر را از تایپ کردن نام کاربری نیز بی‌نیاز کنند. این ویژگی به کاربران امکان می‌دهد با انتخاب حسابی از فهرست گذرکلیدهایی که برای وب‌سایت فعلی دارند، اصالت‌سنجی کنند.

نسخه‌های قبلی کلیدهای امنیتی به‌عنوان روش‌های اصالت‌سنجی دومرحله‌ای طراحی شده بودند و به شناسه‌های اطلاعات کاربری احتمالی نیاز داشتند، بنابراین وارد کردن نام کاربری الزامی بود. اطلاعات اعتباری‌ای که کلید امنیتی می‌تواند بدون دانستن شناسه آن‌ها پیدا کند، اطلاعات اعتباری قابل‌کشف نامیده می‌شود. اکثر اطلاعات اعتباری FIDO که امروزه ایجاد می‌شوند اطلاعات اعتباری قابل‌کشف هستند؛ به‌ویژه گذرکلیدهایی که در مدیر گذرواژه یا در کلید امنیتی مدرن ذخیره می‌شوند.

برای اطمینان از اینکه اطلاعات اعتباری شما به‌عنوان گذرکلید (اطلاعات اعتباری قابل‌کشف) ایجاد می‌شود، residentKey و requireResidentKey را هنگام ایجاد اطلاعات اعتباری مشخص کنید.

طرف‌های متکی (RP) می‌توانند با حذف کردن allowCredentials درطول اصالت‌سنجی گذرکلید، از اعتبارنامه‌های قابل‌کشف استفاده کنند. در این موارد، مرورگر یا سیستم فهرستی از گذرکلیدهای دردسترس را به کاربر نشان می‌دهد که با ویژگی user.name تنظیم‌شده در زمان ایجاد شناسایی می‌شوند. اگر کاربر یکی را انتخاب کند، مقدار user.id در امضای حاصل گنجانده خواهد شد. سپس سرور می‌تواند از آن یا شناسه اطلاعات کاربری برگردانده‌شده برای جستجوی حساب به‌جای نام کاربری تایپ‌شده استفاده کند.

رابط‌های کاربری گزینشگر حساب، مانند مواردی که قبلاً بحث شد، هرگز اطلاعات کاربری غیرقابل‌شناسایی را نشان نمی‌دهند.

‫requireResidentKey و residentKey

برای ایجاد گذرکلید، authenticatorSelection.residentKey و authenticatorSelection.requireResidentKey را در navigator.credentials.create() با مقادیر نشان‌داده‌شده در زیر مشخص کنید.

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': طرف متکی ترجیح می‌دهد اعتبارنامه قابل‌کشف ایجاد کند، اما اعتبارنامه غیرقابل‌کشف را هم می‌پذیرد.
  • ‫'discouraged': طرف محدود ترجیح می‌دهد اعتبارنامه غیرقابل‌شناسایی ایجاد کند، اما اعتبارنامه قابل‌شناسایی را می‌پذیرد.

requireResidentKey:

  • این ویژگی برای سازگاری با نسخه قدیمی WebAuthn Level 1 (نسخه قدیمی‌تر مشخصات) حفظ شده است. اگر residentKey 'required' است، این را روی true تنظیم کنید، درغیراین‌صورت آن را روی false تنظیم کنید.

allowCredentials

‫RPها می‌توانند از allowCredentials در navigator.credentials.get() برای کنترل تجربه اصالت‌سنجی گذرکلید استفاده کنند. معمولاً سه نوع تجربه اصالت‌سنجی گذرکلید وجود دارد:

با اطلاعات اعتباری قابل‌کشف، «طرف‌های متکی» می‌توانند یک انتخابگر حساب مدال نشان دهند تا کاربر حسابی را برای ورود به سیستم انتخاب کند و سپس درستی‌سنجی کاربر انجام شود. این گزینه برای جریان اصالت‌سنجی با گذرکلید که با فشار دادن دکمه اختصاصی اصالت‌سنجی با گذرکلید شروع می‌شود مناسب است.

برای دستیابی به این تجربه کاربری، آرایه خالی را به پارامتر allowCredentials در navigator.credentials.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 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;
  
  // ...
}

نمایش گذرکلید از تکمیل خودکار

انتخابگر حساب مدال که در بالا شرح داده شد، زمانی به‌خوبی کار می‌کند که اکثر کاربران از گذرکلید استفاده کنند و این گذرکلیدها در دستگاه محلی دردسترس باشند. برای کاربری که گذرکلید محلی ندارد، کادر گفتگوی موقتی همچنان نشان داده می‌شود و به کاربر پیشنهاد می‌کند گذرکلیدی از دستگاه دیگری ارائه دهد. هنگام انتقال کاربران به گذرکلید، ممکن است بخواهید از آن میانای کاربری برای کاربرانی که گذرکلید راه‌اندازی نکرده‌اند اجتناب کنید.

درعوض، انتخاب گذرکلید ممکن است در پیام‌واره‌های تکمیل خودکار برای فیلدهای فرم ورود به سیستم سنتی، در کنار نام‌های کاربری و گذرواژه‌های ذخیره‌شده، گنجانده شود. به‌این‌ترتیب، کاربری که گذرکلید دارد می‌تواند با انتخاب گذرکلیدش فرم ورود به سیستم را «تکمیل» کند، کاربرانی که جفت نام کاربری/گذرواژه ذخیره‌شده دارند می‌توانند آن‌ها را انتخاب کنند، و کاربرانی که هیچ‌کدام را ندارند همچنان می‌توانند نام کاربری و گذرواژه خود را تایپ کنند.

این تجربه کاربری زمانی ایده‌آل است که طرف معتمد درحال انتقال با استفاده ترکیبی از گذرواژه‌ها و گذرکلیدها باشد.

برای دستیابی به این تجربه کاربری، علاوه‌بر ارسال آرایه خالی به ویژگی allowCredentials یا حذف پارامتر، mediation: 'conditional' را در navigator.credentials.get() مشخص کنید و فیلد ورودی HTML username را با autocomplete="username webauthn" یا فیلد ورودی password را با autocomplete="password webauthn" حاشیه‌نویسی کنید.

فراخوانی navigator.credentials.get() باعث نمایش هیچ واسط کاربری‌ای نمی‌شود، اما اگر کاربر روی فیلد ورودی حاشیه‌نویسی‌شده تمرکز کند، هر گذرکلید دردسترسی در گزینه‌های تکمیل خودکار گنجانده خواهد شد. اگر کاربر یکی را انتخاب کند، از درستی‌سنجی باز کردن قفل دستگاه معمولی عبور می‌کند و تنها پس‌از آن، وعده برگردانده‌شده توسط .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=&qu>ot;username webauthn" ...

می‌توانید نحوه ساختن این تجربه کاربری را در ورود به سیستم با گذرکلید ازطریق تکمیل خودکار فرم و همچنین codelab پیاده‌سازی گذرکلید با تکمیل خودکار فرم در برنامه وب یاد بگیرید.

اصالت‌سنجی مجدد

در برخی موارد، مثلاً هنگام استفاده از گذرکلید برای اصالت‌سنجی مجدد، شناسه کاربر ازقبل مشخص است. در این مورد، می‌خواهیم از گذرکلید بدون اینکه مرورگر یا سیستم‌عامل هیچ نوعی از انتخابگر حساب را نشان دهد استفاده کنیم. این کار را می‌توان با ارسال فهرستی از شناسه‌های اطلاعات کاربری در پارامتر allowCredentials انجام داد.

در این صورت، اگر هرکدام از اطلاعات اعتباری نام‌برده به‌صورت محلی دردسترس باشد، بلافاصله از کاربر خواسته می‌شود قفل دستگاه را باز کند. اگر این‌طور نباشد، از کاربر خواسته می‌شود دستگاه دیگری (تلفن یا کلید امنیتی) را که دارای اطلاعات کاربری معتبر است ارائه دهد.

برای دستیابی به این تجربه کاربری، فهرستی از شناسه‌های اطلاعات کاربری را برای کاربر واردشونده به سیستم ارائه دهید. «طرف معتمد» باید بتواند آن‌ها را پُرسمان کند زیرا کاربر ازقبل شناخته شده است. شناسه‌های اطلاعات کاربری را به‌عنوان اشیاء PublicKeyCredentialDescriptor در ویژگی allowCredentials در navigator.credentials.get() ارائه دهید.

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: شناسه اطلاعات کاربری کلید عمومی که «طرف محدود» در ثبت گذرکلید دریافت کرده است.
  • type: این فیلد معمولاً 'public-key' است.
  • transports: اشاره‌ای به انتقال‌های پشتیبانی‌شده توسط دستگاهی که این اطلاعات کاربری را نگه می‌دارد، که توسط مرورگرها برای بهینه‌سازی واسط کاربری استفاده می‌شود که از کاربر می‌خواهد دستگاه خارجی را ارائه دهد. این فهرست، درصورت ارائه، باید حاوی نتیجه فراخوانی getTransports() درطول ثبت هر اطلاعات اعتباری باشد.

خلاصه

اطلاعات اعتباری قابل‌کشف با امکان رد کردن وارد کردن نام کاربری، تجربه ورود به سیستم با گذرکلید را بسیار کاربرپسندتر می‌کند. با ترکیب residentKey،‏ requireResidentKey، و allowCredentials، طرف‌های معتمد می‌توانند به تجربه‌های ورود به سیستمی دست یابند که:

  • نمایش انتخابگر حساب مدال.
  • تکمیل خودکار فرم گذرکلید را نشان دهید.
  • اصالت‌سنجی مجدد.

از اطلاعات کاربری قابل‌شناسایی عاقلانه استفاده کنید. با انجام این کار، می‌توانید تجربه‌های پیچیده ورود به سیستم با گذرکلید را طراحی کنید که کاربران آن را یکپارچه می‌بینند و احتمالاً بیشتر با آن تعامل برقرار می‌کنند.