مقدمة
كلنا تقريبا كنواجهو نفس المشكل: بزّاف ديال كلمات السر، صعيب نحفظوهم، ودايماً كايجينا تنبيه باختراقات وتسريبات. فـ2025 بدات Passkeys كتنتشر بقوة فالمتصفحات والخدمات الكبار (Google، GitHub، Apple…)، وكتعطيك دخول بلا كلمات سر، بسرعة وبأمان أعلى.
فهاد المقال القصير وبالدارجة، غنشرح شنو هي Passkeys، كيفاش كتخدم، كيفاش تفعلها خطوة بخطوة فـ Google وGitHub، ومعاها مثال برمجي بسيط بـ WebAuthn إلى كنتي بغيتي تضيفها فمشروعك. وفي الأخير نصائح مهمة باش ما تضيعش الوصول إلا تبدّلتي الجهاز.
شنو هي Passkeys؟
Passkeys هي طريقة دخول بلا كلمات سر، كتعتمد على مفاتيح تشفير عامة/خاصة. المفاتيح الخاصة كاتبقى غير فالجهاز ديالك (محميّة ببصمة/FaceID/Pin)، والموقع كيسجّل غير المفتاح العام. ملي تبغي تدخل، كتأكّد هويتك محلياً والجهاز كيوقّع طلب الدخول وكيصيفطو للموقع.
الفوائد باختصار:
- ماكاينش Passwords باش يتسرّبو أو يتسرقو.
- تجربة دخول سريعة (بصمة/وجه/Pin) حتى بلا إنترنت فأحيان كثيرة.
- تقدر تزامن المفاتيح بين أجهزتك عبر iCloud/Google Password Manager… على حسب النظام.
كيفاش كتخدم WebAuthn بفكرة بسيطة
- التسجيل: المتصفح كينادي
navigator.credentials.create()باش ينشئ Credential جديد. السيرفر كيرجع Challenge مؤقت وكيسجّل المفتاح العام ورقم المستخدِم. - الدخول: المتصفح كينادي
navigator.credentials.get()، والجهاز كيمضي Challenge بمفتاحك الخاص، والسيرفر كيتأكّد من التوقيع بالمفتاح العام المسجل.
فعّل Passkeys على Google (خطوات سريعة)
- دخل لحساب Google ديالك.
- انتقل إلى: الأمان → “مفاتيح المرور” (Passkeys).
- دير “إضافة Passkey” واختر الجهاز الحالي (بصمة/FaceID) أو مفتاح أمني (YubiKey).
- جرّب تسجيل الخروج والدخول غير بالبصمة.
فعّل Passkeys على GitHub
- الدخول → Settings → Password and authentication.
- ضمن Passkeys → New passkey.
- تبع التعليمات (المتصفح يطلب FaceID/بصمة).
- خليه إلى جانب 2FA (ما تحيدش 2FA) باش تزيد الأمان.
مثال عملي مختصر بـ JavaScript (الطرف العميل)
ملاحظة: WebAuthn كتحتاج HTTPS ودومين حقيقي (ولا localhost). هاد الكود يبيّن الفكرة العامة فقط.
// تسجيل مستخدم جديد (إنشاء Passkey)
async function registerPasskey() {
// 1) نجيبوا challenge ومعطيات من السيرفر
const options = await fetch('/webauthn/register/options').then(r => r.json());
// 2) نحول الحقول اللي فيها Base64URL لـ ArrayBuffer
options.challenge = base64urlToBuffer(options.challenge);
options.user.id = base64urlToBuffer(options.user.id);
if (options.excludeCredentials) {
options.excludeCredentials = options.excludeCredentials.map(c => ({
...c,
id: base64urlToBuffer(c.id)
}));
}
// 3) المتصفح ينشئ الاعتماد
const cred = await navigator.credentials.create({ publicKey: options });
// 4) نصيّغ البيانات للارسال للسيرفر
const attestation = {
id: cred.id,
rawId: bufferToBase64url(cred.rawId),
type: cred.type,
response: {
clientDataJSON: bufferToBase64url(cred.response.clientDataJSON),
attestationObject: bufferToBase64url(cred.response.attestationObject)
}
};
// 5) نرسل التأكيد للسيرفر
const res = await fetch('/webauthn/register/verify', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(attestation)
});
return res.ok;
}
// تسجيل الدخول (مصادقة)
async function loginWithPasskey() {
const requestOptions = await fetch('/webauthn/login/options').then(r => r.json());
requestOptions.challenge = base64urlToBuffer(requestOptions.challenge);
requestOptions.allowCredentials = (requestOptions.allowCredentials || []).map(c => ({
...c, id: base64urlToBuffer(c.id)
}));
const assertion = await navigator.credentials.get({ publicKey: requestOptions });
const data = {
id: assertion.id,
rawId: bufferToBase64url(assertion.rawId),
type: assertion.type,
response: {
clientDataJSON: bufferToBase64url(assertion.response.clientDataJSON),
authenticatorData: bufferToBase64url(assertion.response.authenticatorData),
signature: bufferToBase64url(assertion.response.signature),
userHandle: assertion.response.userHandle ? bufferToBase64url(assertion.response.userHandle) : null,
}
};
const res = await fetch('/webauthn/login/verify', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return res.ok;
}
// Helpers: Base64URL ⇄ ArrayBuffer
function base64urlToBuffer(base64url) {
const pad = '='.repeat((4 - (base64url.length % 4)) % 4);
const b64 = (base64url + pad).replace(/-/g, '+').replace(/_/g, '/');
const raw = atob(b64);
const buf = new ArrayBuffer(raw.length);
const view = new Uint8Array(buf);
for (let i = 0; i < raw.length; i++) view[i] = raw.charCodeAt(i);
return buf;
}
function bufferToBase64url(buf) {
const bytes = new Uint8Array(buf);
let str = '';
for (let i = 0; i < bytes.length; i++) str += String.fromCharCode(bytes[i]);
const b64 = btoa(str).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
return b64;
}
ملاحظات على الجانب الخادمي (Server)
باش تكمل العملية، خاص السيرفر ديالك يكوّن Challenge آمن، ويتحقق من التواقيع ويخزّن المفتاح العام لكل مستخدم. كاين لَيبريريات جاهزة فـ Node/Python/PHP كتسهّل WebAuthn. الهدف هنا نفهمو الفكرة ونبدو بسرعة.
أمان واسترجاع (Lost device?)
- فعّل Passkeys على أكثر من جهاز (تلفون + لابتوب)، وخلي 2FA شغال.
- خزّن مفاتيح إضافية (Security Key) كحل احتياطي.
- قبل تبديل الهاتف: تأكد من تفعيل المزامنة (iCloud/Google Password Manager) ونقل المفاتيح.
- احتفظ برموز Recovery للخدمات اللي كتوفّرها.
أسئلة شائعة
- واش نقدر نستغنى كلياً على كلمات السر؟ نعم فخدمات كثيرة، ولكن احتفظ بكلمة سر قوية للحالات الاستثنائية.
- شنو على الخصوصية؟ المفاتيح الخاصة ما كتخرجش من جهازك، والموقع كيعرف غير المفتاح العام.
- وخا ما عنديش إنترنت؟ في بعض الحالات يقدر يدخل أوفلاين حيث التأكيد محلي، لكن غالباً الخادم كيتطلب اتصال للتحقق النهائي.
خاتمة
Passkeys كتخفف علينا عذاب كلمات السر وكتزيد الأمان. جرّب تفعيلها دابا فـ Google وGitHub، وإذا كنت مطوّر جرّب المثال البسيط أعلاه، وغادي تلقى أنها سهلة أكثر مما كنت كتتصور. إلى بغيتي مقال مفصّل بالأكواد الكاملة للـ Server فـ Node.js أو Laravel، كتب لينا فالتعليقات.