نظرة تفصيلية على بيانات الاعتماد القابلة للاكتشاف

على الرغم من أنّ بيانات اعتماد FIDO، مثل مفاتيح المرور، تهدف إلى استبدال كلمات المرور، يمكن لمعظمها أيضًا تجنيب المستخدم عناء كتابة اسم مستخدم. يتيح ذلك للمستخدمين إمكانية المصادقة من خلال اختيار حساب من قائمة مفاتيح المرور المتوفرة لديهم للموقع الإلكتروني الحالي.

تم تصميم الإصدارات السابقة من مفاتيح الأمان كطرق مصادقة بخطوتين، وكان ذلك يتطلب معرّفات بيانات الاعتماد المحتملة، وبالتالي كان مطلوبًا إدخال اسم مستخدم. تُعرف بيانات الاعتماد التي يمكن لمفتاح الأمان العثور عليها بدون معرفة أرقام تعريفها باسم بيانات الاعتماد القابلة للاكتشاف. إنّ معظم بيانات اعتماد FIDO التي يتم إنشاؤها اليوم هي بيانات اعتماد قابلة للاكتشاف، لا سيما مفاتيح المرور المخزّنة في خدمة إدارة كلمات المرور أو على مفتاح أمان حديث.

لضمان إنشاء بيانات الاعتماد كمفاتيح مرور (بيانات اعتماد قابلة للاكتشاف)، حدِّد residentKey وrequireResidentKey عند إنشاء بيانات الاعتماد.

يمكن للجهات الموثوق بها استخدام بيانات الاعتماد القابلة للاكتشاف من خلال حذف 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، وهو إصدار قديم من المواصفة. اضبط هذا الخيار على true إذا كانت قيمة residentKey هي 'required'، وإلا اضبطه على false.

allowCredentials

يمكن للجهات المحظورة استخدام 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;
  
  // ...
}

إظهار ميزة الملء التلقائي لنموذج مفتاح المرور

يعمل أداة اختيار الحسابات المعروضة في نافذة منبثقة والموضّحة أعلاه بشكل جيد إذا كان معظم المستخدمين يستخدمون مفاتيح المرور وتتوفر لديهم على الجهاز المحلي. بالنسبة إلى المستخدم الذي لا يملك مفاتيح مرور على الجهاز، سيظل مربّع الحوار المنبثق ظاهرًا وسيطلب من المستخدم تقديم مفتاح مرور من جهاز آخر. أثناء نقل المستخدمين إلى مفاتيح المرور، قد تحتاج إلى تجنُّب واجهة المستخدم هذه للمستخدمين الذين لم يسبق لهم إعداد مفتاح مرور.

بدلاً من ذلك، يمكن دمج اختيار مفتاح المرور في طلبات الملء التلقائي للحقول في نموذج تسجيل الدخول التقليدي، إلى جانب أسماء المستخدمين وكلمات المرور المحفوظة. بهذه الطريقة، يمكن للمستخدم الذي لديه مفاتيح مرور "ملء" نموذج تسجيل الدخول من خلال اختيار مفتاح المرور، ويمكن للمستخدمين الذين لديهم أزواج محفوظة من اسم المستخدم وكلمة المرور اختيارها، وسيظل بإمكان المستخدمين الذين ليس لديهم أي منهما كتابة اسم المستخدم وكلمة المرور.

هذه التجربة مثالية للمستخدمين عندما يكون RP قيد عملية نقل بيانات باستخدام كلمات المرور ومفاتيح المرور بشكل مختلط.

لتحقيق هذه التجربة للمستخدم، بالإضافة إلى تمرير صفيف فارغ إلى السمة allowCredentials أو حذف المَعلمة، حدِّد mediation: 'conditional' في navigator.credentials.get() وشارِح حقل إدخال username بتنسيق HTML باستخدام 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="username webauthn" ...>

يمكنك التعرّف على كيفية إنشاء تجربة المستخدم هذه في تسجيل الدخول باستخدام مفتاح مرور من خلال ميزة الملء التلقائي للنموذج، بالإضافة إلى الدرس التطبيقي تنفيذ مفاتيح المرور باستخدام ميزة الملء التلقائي للنموذج في تطبيق ويب.

إعادة المصادقة

في بعض الحالات، مثل استخدام مفاتيح المرور لإعادة المصادقة، يكون معرّف المستخدم معروفًا. في هذه الحالة، نريد استخدام مفتاح مرور بدون أن يعرض المتصفّح أو نظام التشغيل أي شكل من أشكال أداة اختيار الحسابات. ويمكن إجراء ذلك من خلال تمرير قائمة بأرقام تعريف بيانات الاعتماد في المَعلمة 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، يمكن لموفّري خدمات الدفع توفير تجارب تسجيل دخول:

  • عرض أداة اختيار حساب في نافذة منبثقة
  • عرض ميزة الملء التلقائي لنموذج مفتاح المرور
  • إعادة المصادقة.

استخدام بيانات الاعتماد القابلة للاكتشاف بحكمة ومن خلال إجراء ذلك، يمكنك تصميم تجارب تسجيل دخول متقدّمة باستخدام مفاتيح المرور تبدو سلسة للمستخدمين وتزيد من احتمال تفاعلهم مع التطبيق.