אימות מחדש באמצעות מפתח גישה

תכונות באינטרנט: אימות באינטרנט

המדריך הזה מתמקד בשינויים, ומסביר איך להטמיע אימות נוסף או אימות מחדש של משתמש מחובר לפני שהוא מבצע שינויים רגישים בחשבון (למשל, עדכוני סיסמאות, העברות כספים).

ארכיטקטורת Delta Flow

בניגוד לאימות רגיל, אימות מחדש באמצעות מפתח גישה מגביל את תיבות הדו-שיח של מפתח הגישה אך ורק לפרטי הכניסה הרשומים מראש של המשתמש המחובר, כדי למנוע ערבוב חשבונות או זיוף של מפתח הגישה במהלך סשנים פעילים.

בצד השרת

דלתא ביצירת אפשרויות

יוצרים נקודת קצה (endpoint) שמאכלסת את הפרמטרים של פרטי הכניסה המותרים, במיוחד למשתמש הפעיל והמוכר:

הגבלת פרטי הכניסה: מאכלסים את מערך האפשרויות של allowCredentials ברשומות ספציפיות של PublicKeyCredentialDescriptor שממפות את כל מזהי פרטי הכניסה הרשומים של המשתמש המחובר. אם משאירים את השדה הזה ריק או משמיטים אותו, המערכת חוזרת להשתמש בפרטי כניסה שניתן לגלות, וזה פוגע באבטחת הסשן.

// Node.js step-up options generation example
router.post("/api/reauth/options", enforceActiveSession, async (req, res) => {
  const userPasskeys = await db.findCredentialsByUserId(req.user.id);

  const options = {
    challenge: serverGeneratedBase64UrlChallenge, // Random challenge stored in user session
    rpId: "example.com",
    // Enforce allowance strictly limited to the user's credentials list
    allowCredentials: userPasskeys.map((cred) => ({
      type: "public-key",
      id: cred.id,
      transports: cred.transports, // Speeds up resolution by indicating platform transports
    })),
  };
  return res.json(options);
});

ההפרש בין נקודות הקצה של האימות

מאמתים את ההצהרה שהוחזרה על ידי הלקוח:

אימות הבעלות על החשבון: נקודת הקצה לאימות חייבת לאמת באופן מפורש שמזהה פרטי הכניסה המאומתים שהלקוח מחזיר מפנה לרשומת פרטי כניסה שמורה, שמזהה המשתמש שמשויך אליה תואם בדיוק למשתמש הפעיל שמחובר לחשבון (storedCredential.passkeyUserId === req.user.id). אם מוחזר מפתח גישה תקין של משתמש אחר, צריך לדחות את האימות באופן מיידי.

הפרשי זרימה בצד הלקוח

האפליקציות בוחרות מבין שני ממשקי אימות מחדש, בהתאם לממשק המשתמש של העסקה:

א. תהליך עם כפתורים (בלי שדות להזנת קלט)

הפעילו אימות מחדש כשמשתמש לוחץ על כפתור "אימות זהות" או "המשך בעסקה".

<button id="reauth-btn" data-testid="reauth-button">Confirm Transaction</button>
let reauthAbortController = new AbortController();

async function triggerButtonReauth() {
  // Abort any background suggestion flows to avoid passkey prompt collisions
  reauthAbortController.abort();
  reauthAbortController = new AbortController();

  const optionsResponse = await fetch("/api/reauth/options", {
    method: "POST",
  });
  const optionsJSON = await optionsResponse.json();
  const publicKey =
    PublicKeyCredential.parseRequestOptionsFromJSON(optionsJSON);

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

    if (credential) {
      const encodedCredential = credential.toJSON();
      const verifyResponse = await fetch("/api/reauth/verify", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(encodedCredential),
      });

      if (verifyResponse.ok) {
        showTransactionSuccessUI();
      } else if (verifyResponse.status === 404 && PublicKeyCredential.signalUnknownCredential) {
        await PublicKeyCredential.signalUnknownCredential({
          rpId, // RP ID must match the one defined on the server
          credentialId: encodedCredential.id
        });
      }
    }
  } catch (err) {
    if (err.name === "NotAllowedError") {
      console.log("User cancelled reauthentication.");
    }
  }
}

document
  .getElementById("reauth-btn")
  .addEventListener("click", triggerButtonReauth);

שיטות חלופיות

זיהוי תכונות של מפתחות גישה – חלופה

תמיד צריך להתקין את webauthn-polyfills ולייבא אותו בהקשר. כל עוד יש תמיכה ב-PublicKeyCredential, יש תמיכה גם ב-PublicKeyCredential.getClientCapabilities.

import 'webauthn-polyfills';

Easy JSON Serialization Fallback

תמיד צריך להתקין את webauthn-polyfills ולייבא אותו בהקשר. כל עוד יש תמיכה ב-PublicKeyCredential, יש תמיכה גם ב-PublicKeyCredential.parseRequestOptionsFromJSON וב-PublicKeyCredential.prototype.toJSON.

import 'webauthn-polyfills';