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

على الرغم من أنّ بيانات اعتماد 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;
  
  // ...
}

عرض الملء التلقائي لنموذج مفتاح المرور

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

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

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

بالإضافة إلى تمرير مصفوفة فارغة إلى السمة 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، يمكن لموفّري خدمات الدفع توفير تجارب تسجيل دخول:

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

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