۱. قبل از شروع
استفاده از کلیدهای عبور به جای رمزهای عبور، راهی عالی برای وبسایتها است تا حسابهای کاربری خود را ایمنتر، سادهتر و آسانتر کنند. با استفاده از کلید عبور، کاربر میتواند با استفاده از ویژگی قفل صفحه دستگاه، مانند اثر انگشت، چهره یا پین دستگاه، وارد وبسایت یا برنامه شود. قبل از اینکه کاربر بتواند با آن وارد سیستم شود، باید یک کلید عبور ایجاد شود، به یک حساب کاربری مرتبط شود و کلید عمومی آن در سرور ذخیره شود.
در این آزمایشگاه کد، شما یک فرم ورود ساده مبتنی بر نام کاربری و رمز عبور را به فرمی تبدیل میکنید که از کلیدهای عبور پشتیبانی میکند و شامل موارد زیر است:
- دکمهای که پس از ورود کاربر، یک رمز عبور ایجاد میکند.
- رابط کاربری که فهرستی از رمزهای عبور ثبتشده را نمایش میدهد.
- فرم ورود به سیستم موجود که به کاربران اجازه میدهد با رمز عبور ثبتشده از طریق تکمیل خودکار فرم، وارد سیستم شوند.
پیشنیازها
- درک اولیه از جاوا اسکریپت
- آشنایی اولیه با API احراز هویت وب (WebAuthn)
آنچه یاد خواهید گرفت
- نحوه ایجاد کلید عبور.
- نحوه احراز هویت کاربران با رمز عبور.
- چگونه میتوان به یک فرم اجازه داد که به عنوان گزینه ورود، رمز عبور را پیشنهاد دهد.
۲. آماده شوید
در این آزمایشگاه کد، شما یک برنامه آزمایشی ناقص را از گیتهاب کپی میکنید و سپس پیادهسازی پشتیبانی از کلید عبور را به پایان میرسانید.
پروژه را کلون کنید
- پروژه را در گیتهاب باز کنید.
- پروژه را کپی یا دانلود کنید.

اجرای پروژه
- یک ترمینال باز کنید و
cd startدایرکتوری را تغییر دهید. - برای نصب وابستگیهای پروژه
npm installاجرا کنید. - پروژه را با
npm run build && IS_LOCAL=1 npm run startساخته و اجرا کنید. - آدرس http://localhost:8080/ را در مرورگر خود باز کنید.
بررسی وضعیت اولیه وبسایت
- در سایت، یک نام کاربری تصادفی وارد کنید و سپس روی «بعدی» کلیک کنید.
- یک رمز عبور تصادفی وارد کنید و سپس روی ورود کلیک کنید. رمز عبور نادیده گرفته میشود، اما شما همچنان احراز هویت شده و به صفحه اصلی هدایت میشوید.
- اگر میخواهید نام نمایشی خود را تغییر دهید، این کار را انجام دهید. این تمام کاری است که میتوانید در حالت اولیه انجام دهید.
- روی خروج از سیستم کلیک کنید.
در این حالت، کاربران باید هر بار که وارد سیستم میشوند، رمز عبور را وارد کنند. شما پشتیبانی از رمز عبور را به این فرم اضافه میکنید تا کاربران بتوانند با قابلیت قفل صفحه نمایش دستگاه وارد سیستم شوند.
برای اطلاعات بیشتر در مورد نحوه عملکرد کلیدهای عبور، به بخش «کلیدهای عبور چگونه کار میکنند؟» مراجعه کنید.
۳. قابلیت ایجاد رمز عبور را اضافه کنید
برای اینکه به کاربران اجازه دهید با کلید عبور احراز هویت کنند، باید به آنها امکان ایجاد و ثبت کلید عبور و ذخیره کلید عمومی آن در سرور را بدهید.

شما میخواهید پس از ورود کاربر با رمز عبور، امکان ایجاد کلید عبور را فراهم کنید و یک رابط کاربری اضافه کنید که به کاربران اجازه دهد یک کلید عبور ایجاد کنند و لیستی از تمام کلیدهای عبور ثبت شده را در صفحه /home مشاهده کنند. در بخش بعدی، تابعی ایجاد میکنید که یک کلید عبور ایجاد و ثبت میکند.
تابع registerCredential() را ایجاد کنید.
- در ویرایشگر کد مورد نظر خود، پوشه
start) را باز کنید. - به فایل
public/client.jsبروید و سپس تا انتها اسکرول کنید. - بعد از کامنت مربوطه، تابع
registerCredential()زیر را اضافه کنید:
فایل public/client.js
// TODO: Add an ability to create a passkey: Create the registerCredential() function.
export async function registerCredential() {
// TODO: Add an ability to create a passkey: Obtain the challenge and other options from the server endpoint.
// TODO: Add an ability to create a passkey: Create a credential.
// TODO: Add an ability to create a passkey: Register the credential to the server endpoint.
};
این تابع یک کلید عبور روی سرور ایجاد و ثبت میکند.
چالش و سایر گزینهها را از نقطه پایانی سرور دریافت کنید
قبل از ایجاد کلید عبور، باید پارامترهایی را برای ارسال از سرور در WebAuthn درخواست کنید، از جمله یک چالش. WebAuthn یک API مرورگر است که به کاربر اجازه میدهد یک کلید عبور ایجاد کند و کاربر را با کلید عبور احراز هویت کند. خوشبختانه، شما از قبل یک نقطه پایانی سرور دارید که با چنین پارامترهایی در این codelab پاسخ میدهد.
- برای دریافت چالش و سایر گزینهها از نقطه پایانی سرور، کد زیر را به بدنه تابع
registerCredential()پس از کامنت مربوطه اضافه کنید:
فایل public/client.js
// TODO: Add an ability to create a passkey: Obtain the challenge and other options from the server endpoint.
const _options = await _fetch('/auth/registerRequest');
قطعه کد زیر شامل گزینههای نمونهای است که از سرور دریافت میکنید:
{
challenge: *****,
rp: {
id: "example.com",
},
user: {
id: *****,
name: "john78",
displayName: "John",
},
pubKeyCredParams: [{
alg: -7, type: "public-key"
},{
alg: -257, type: "public-key"
}],
excludeCredentials: [{
id: *****,
type: 'public-key',
transports: ['internal', 'hybrid'],
}],
authenticatorSelection: {
authenticatorAttachment: "platform",
requireResidentKey: true,
}
}
پروتکل بین سرور و کلاینت بخشی از مشخصات WebAuthn نیست. با این حال، سرور این codelab به گونهای طراحی شده است که JSON را برگرداند که تا حد امکان شبیه به دیکشنری PublicKeyCredentialCreationOptions باشد که به API مربوط به WebAuthn navigator.credentials.create() ارسال میشود.
جدول زیر جامع نیست، اما شامل پارامترهای مهم در دیکشنری PublicKeyCredentialCreationOptions است:
پارامترها | توضیحات |
یک چالش ایجاد شده توسط سرور در یک شیء | |
شناسه منحصر به فرد کاربر. این مقدار باید یک شیء | |
این فیلد باید یک شناسه منحصر به فرد برای حساب کاربری که توسط کاربر قابل تشخیص است، مانند آدرس ایمیل یا نام کاربری، داشته باشد. این شناسه در انتخابگر حساب کاربری نمایش داده میشود. (اگر از نام کاربری استفاده میکنید، از همان مقداری که در احراز هویت با رمز عبور استفاده میکنید، استفاده کنید.) | |
این فیلد یک نام اختیاری و کاربرپسند برای حساب کاربری است. نیازی نیست منحصر به فرد باشد و میتواند نام انتخابی کاربر باشد. اگر وبسایت شما مقدار مناسبی برای گنجاندن در اینجا ندارد، یک رشته خالی ارسال کنید. این ممکن است بسته به مرورگر در انتخابگر حساب نمایش داده شود. | |
شناسه طرف اتکاکننده (RP) یک دامنه است. یک وبسایت میتواند دامنه یا پسوند قابل ثبت خود را مشخص کند. برای مثال، اگر مبدأ یک RP، https://login.example.com:1337 باشد، شناسه RP میتواند | |
این فیلد الگوریتمهای کلید عمومی پشتیبانیشده توسط RP را مشخص میکند. توصیه میکنیم آن را روی | |
فهرستی از شناسههای اعتبارنامهی ثبتشدهی قبلی را ارائه میدهد تا از ثبت مجدد یک دستگاه جلوگیری شود. در صورت ارائه، عضو | |
روی مقدار | |
روی یک مقدار بولی | |
روی یک مقدار |
ایجاد اعتبارنامه
- در بدنه تابع
registerCredential()پس از کامنت مربوطه، برخی از پارامترهای کدگذاری شده با Base64URL، به ویژه رشتههایuser.idوchallengeو نمونههایی از رشتهidکه در آرایهexcludeCredentialsقرار دارند را به حالت دودویی (باینری) برگردانید. این کار را میتوان با تابعPublicKeyCredential.parseCreationOptionsFromJSON()انجام داد:
فایل public/client.js
// TODO: Add an ability to create a passkey: Create a credential.
// Deserialize and decode the `PublicKeyCredential.parseCreationOptionsFromJSON()`.
const options = PublicKeyCredential.parseCreationOptionsFromJSON(_options);
- در خط بعدی،
authenticatorSelection.authenticatorAttachmentرا روی"platform"وauthenticatorSelection.requireResidentKeyرا رویtrueتنظیم کنید. این کار فقط اجازه استفاده از یک احراز هویت کننده پلتفرم (خود دستگاه) با قابلیت شناسایی اعتبارنامه را میدهد.
فایل public/client.js
// Use platform authenticator and discoverable credential.
options.authenticatorSelection = {
authenticatorAttachment: 'platform',
requireResidentKey: true
}
- در خط بعدی، متد
navigator.credentials.create()را برای ایجاد یک اعتبارنامه فراخوانی کنید.
فایل public/client.js
// Invoke the WebAuthn create() method.
const cred = await navigator.credentials.create({
publicKey: options,
});
با این فراخوانی، مرورگر سعی میکند هویت کاربر را با قفل صفحه دستگاه تأیید کند .
اعتبارنامه را در نقطه پایانی سرور ثبت کنید
پس از اینکه کاربر هویت خود را تأیید کرد، یک کلید عبور ایجاد و ذخیره میشود. وبسایت یک شیء اعتبارنامه دریافت میکند که حاوی یک کلید عمومی است که میتوانید برای ثبت کلید عبور به سرور ارسال کنید.
قطعه کد زیر شامل یک نمونه شیء اعتبارنامه است:
{
"id": *****,
"rawId": *****,
"type": "public-key",
"response": {
"clientDataJSON": *****,
"attestationObject": *****,
"transports": ["internal", "hybrid"]
},
"authenticatorAttachment": "platform"
}
جدول زیر جامع نیست، اما شامل پارامترهای مهم در شیء PublicKeyCredential است:
پارامترها | توضیحات |
یک شناسه رمزگذاری شده Base64URL از کلید عبور ایجاد شده. این شناسه به مرورگر کمک میکند تا هنگام احراز هویت، تشخیص دهد که آیا کلید عبور منطبق در دستگاه وجود دارد یا خیر. این مقدار باید در پایگاه داده در backend ذخیره شود. | |
یک نسخه شیء | |
یک شیء | |
یک شیء گواهی رمزگذاری شده | |
فهرستی از روشهای انتقال داده که دستگاه پشتیبانی میکند: | |
وقتی این اعتبارنامه روی دستگاهی با قابلیت رمز عبور ایجاد شود |
برای ارسال شیء اعتبارنامه به سرور، مراحل زیر را دنبال کنید:
- پارامترهای دودویی اعتبارنامه را به صورت Base64URL کدگذاری کنید تا بتوان آن را به صورت رشته به سرور تحویل داد. میتوانید
.toJSON()برای انجام این کار استفاده کنید:
فایل public/client.js
// TODO: Add an ability to create a passkey: Register the credential to the server endpoint.
// Encode and serialize the `PublicKeyCredential`.
const credential = JSON.stringify(cred);
- در خط بعدی، شیء را به سرور ارسال کنید:
فایل public/client.js
return await _fetch('/auth/registerResponse', credential);
وقتی برنامه را اجرا میکنید، سرور HTTP code 200 را برمیگرداند که نشان میدهد اعتبارنامه ثبت شده است.
حالا شما تابع registerCredential() را به طور کامل دارید!
کد راهحل این بخش را بررسی کنید
فایل public/client.js
// TODO: Add an ability to create a passkey: Create the registerCredential() function.
export async function registerCredential() {
// TODO: Add an ability to create a passkey: Obtain the challenge and other options from the server endpoint.
const _options = await _fetch('/auth/registerRequest');
// TODO: Add an ability to create a passkey: Create a credential.
// Deserialize and decode the `PublicKeyCredential.parseCreationOptionsFromJSON()`.
const options = PublicKeyCredential.parseCreationOptionsFromJSON(_options);
// Use platform authenticator and discoverable credential.
options.authenticatorSelection = {
authenticatorAttachment: 'platform',
requireResidentKey: true
}
// Invoke the WebAuthn create() method.
const cred = await navigator.credentials.create({
publicKey: options,
});
// TODO: Add an ability to create a passkey: Register the credential to the server endpoint.
// Encode and serialize the `PublicKeyCredential`.
const credential = JSON.stringify(cred);
return await _fetch('/auth/registerResponse', credential);
};
۴. یک رابط کاربری برای ثبت و مدیریت اعتبارنامههای رمز عبور بسازید
حالا که تابع registerCredential() در دسترس است، به یک دکمه برای فراخوانی آن نیاز دارید. همچنین، باید لیستی از رمزهای عبور ثبت شده را نمایش دهید.

اضافه کردن حفره یا سوراخ HTML
- در ویرایشگر خود، به فایل
views/home.htmlبروید. - بعد از کامنت مربوطه، یک جاینگهدار رابط کاربری اضافه کنید که دکمهای برای ثبت رمز عبور و لیستی از رمزهای عبور را نمایش دهد:
نمایشها/خانه.html
<!-- TODO: Add an ability to create a passkey: Add placeholder HTML. -->
<section>
<h3>Your registered passkeys:</h3>
<div id="list"></div>
</section>
<p id="message" class="instructions"></p>
<mdui-button id="create-passkey" class="hidden" icon="fingerprint" type="button">Create a passkey</mdui-button>
عنصر div#list محل قرارگیری لیست را مشخص میکند.
پشتیبانی از رمز عبور را بررسی کنید
برای اینکه گزینه ایجاد کلید عبور فقط برای کاربرانی که دستگاههایشان از کلید عبور پشتیبانی میکند نمایش داده شود، ابتدا باید بررسی کنید که آیا WebAuthn در دسترس است یا خیر. در این صورت، باید کلاس hidden را حذف کنید تا دکمه «ایجاد کلید عبور» نمایش داده شود.
برای بررسی اینکه آیا یک محیط از کلیدهای عبور پشتیبانی میکند، این مراحل را دنبال کنید:
- در انتهای فایل
views/home.htmlو پس از کامنت مربوطه، یک شرط بنویسید که در صورتtruewindow.PublicKeyCredential،PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailableوPublicKeyCredential.isConditionalMediationAvailableاجرا شود.
نمایشها/خانه.html
// TODO: Add an ability to create a passkey: Check for passkey support.
const createPasskey = $('#create-passkey');
// Feature detections
if (window.PublicKeyCredential &&
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&
PublicKeyCredential.isConditionalMediationAvailable) {
- در بدنهی شرط، بررسی کنید که آیا دستگاه میتواند کلید عبور ایجاد کند و سپس بررسی کنید که آیا میتوان کلید عبور را در تکمیل خودکار فرم پیشنهاد داد یا خیر.
نمایشها/خانه.html
try {
const capabilities = await PublicKeyCredential.getClientCapabilities();
// Is conditional UI available in this browser?
if (capabilities.conditionalGet === true &&
capabilities.passkeyPlatformAuthenticator === true) {
- اگر همه شرایط برقرار باشد، دکمه ایجاد رمز عبور نمایش داده میشود. در غیر این صورت، یک پیام هشدار نمایش داده میشود.
نمایشها/خانه.html
createPasskey.classList.remove('hidden');
} else {
// If conditional UI isn't available, show a message.
$('#message').innerText = 'This device does not support passkeys.';
}
} catch (e) {
console.error(e);
}
} else {
// If WebAuthn isn't available, show a message.
$('#message').innerText = 'This device does not support passkeys.';
}
کلیدهای عبور ثبت شده را در یک لیست نمایش دهید
- یک تابع
renderCredentials()تعریف کنید که کلیدهای عبور ثبتشده را از سرور دریافت کرده و آنها را در یک لیست رندر کند. خوشبختانه، شما از قبل نقطه پایانی سرور/auth/getKeysرا برای دریافت کلیدهای عبور ثبتشده برای کاربر وارد شده دارید.
نمایشها/خانه.html
// TODO: Add an ability to create a passkey: Render registered passkeys in a list.
async function renderCredentials() {
const res = await _fetch('/auth/getKeys');
const list = $('#list');
const creds = res.length > 0 ? html`
<mdui-list>
${res.map(cred => html`
<mdui-list-item>
${cred.name || 'Unnamed'}
<mdui-button-icon data-cred-id="${cred.id}" data-name="${cred.name || 'Unnamed'}" @click="${rename}" icon="edit" slot="end-icon"></mdui-button-icon>
<mdui-button-icon data-cred-id="${cred.id}" @click="${remove}" icon="delete" slot="end-icon"></mdui-button-icon>
</mdui-list-item>`)}
</mdui-list>` : html`
<mdui-list>
<mdui-list-item>No credentials found.</mdui-list-item>
</mdui-list>`;
render(creds, list);
};
- در خط بعدی، تابع
renderCredentials()را فراخوانی کنید تا به محض ورود کاربر به صفحه/homeکلیدهای عبور ثبت شده را به عنوان مقداردهی اولیه نمایش دهد.
نمایشها/خانه.html
renderCredentials();
ایجاد و ثبت رمز عبور
برای ایجاد و ثبت یک کلید عبور، باید تابع registerCredential() را که قبلاً پیادهسازی کردهاید، فراخوانی کنید.
برای اجرای تابع registerCredential() هنگام کلیک بر روی دکمهی Create a passkey ، مراحل زیر را دنبال کنید:
- در فایل بعد از HTML جایگذاری شده، عبارت
importزیر را پیدا کنید:
نمایشها/خانه.html
import {
$,
_fetch,
loading,
updateCredential,
unregisterCredential,
} from '/client.js';
- در انتهای بدنهی دستور
import، تابعregisterCredential()را اضافه کنید.
نمایشها/خانه.html
// TODO: Add an ability to create a passkey: Create and register a passkey.
import {
$,
_fetch,
loading,
updateCredential,
unregisterCredential,
registerCredential
} from '/client.js';
- در انتهای فایل، پس از کامنت مربوطه، یک تابع
register()تعریف کنید که تابعregisterCredential()و یک رابط کاربری در حال بارگذاری را فراخوانی میکند وrenderCredentials()را پس از ثبت نام فراخوانی میکند. این کار روشن میکند که مرورگر یک رمز عبور ایجاد میکند و در صورت بروز مشکل، یک پیام خطا نشان میدهد.
نمایشها/خانه.html
// TODO: Add an ability to create a passkey: Create and register a passkey.
async function register() {
try {
// Start the loading UI.
loading.start();
// Start creating a passkey.
await registerCredential();
// Stop the loading UI.
loading.stop();
// Render the updated passkey list.
renderCredentials();
- در بدنه تابع
register()، استثنائات را دریافت کنید. متدnavigator.credentials.create()وقتی یک کلید عبور از قبل روی دستگاه وجود داشته باشد، خطایInvalidStateErrorرا نشان میدهد. این موضوع با آرایهexcludeCredentialsبررسی میشود. در این حالت، یک پیام مرتبط به کاربر نشان میدهید. همچنین وقتی کاربر دیالوگ احراز هویت را لغو میکند، خطایNotAllowedErrorرا نشان میدهد. در این حالت، شما آن را بیسروصدا نادیده میگیرید.
نمایشها/خانه.html
} catch (e) {
// Stop the loading UI.
loading.stop();
// An InvalidStateError indicates that a passkey already exists on the device.
if (e.name === 'InvalidStateError') {
alert('A passkey already exists for this device.');
// A NotAllowedError indicates that the user canceled the operation.
} else if (e.name === 'NotAllowedError') {
Return;
// Show other errors in an alert.
} else {
alert(e.message);
console.error(e);
}
}
};
- در خط بعد از تابع
register()، تابعregister()را به یک رویدادclickبرای دکمهی Create a passkey متصل کنید.
نمایشها/خانه.html
createPasskey.addEventListener('click', register);
کد راهحل این بخش را بررسی کنید
نمایشها/خانه.html
<!-- TODO: Add an ability to create a passkey: Add placeholder HTML. -->
<section>
<h3>Your registered passkeys:</h3>
<div id="list"></div>
</section>
<p id="message" class="instructions"></p>
<mdui-button id="create-passkey" icon="fingerprint" type="button">Create a passkey</mdui-button>
نمایشها/خانه.html
// TODO: Add an ability to create a passkey: Create and register a passkey.
import {
$,
_fetch,
loading,
updateCredential,
unregisterCredential,
registerCredential
} from '/client.js';
نمایشها/خانه.html
// TODO: Add an ability to create a passkey: Check for passkey support.
const createPasskey = $('#create-passkey');
// Is WebAuthn available in this browser?
if (window.PublicKeyCredential &&
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&
PublicKeyCredential.isConditionalMediationAvailable) {
try {
const capabilities = await PublicKeyCredential.getClientCapabilities();
// Is conditional UI available in this browser?
if (capabilities.conditionalGet === true &&
capabilities.passkeyPlatformAuthenticator === true) {
// If conditional UI is available, reveal the Create a passkey button.
createPasskey.classList.remove('hidden');
} else {
// If conditional UI isn't available, show a message.
$('#message').innerText = 'This device does not support passkeys.';
}
} catch (e) {
console.error(e);
}
} else {
// If WebAuthn isn't available, show a message.
$('#message').innerText = 'This device does not support passkeys.';
}
// TODO: Add an ability to create a passkey: Render registered passkeys in a list.
async function renderCredentials() {
const res = await _fetch('/auth/getKeys');
const list = $('#list');
const creds = html`${res.length > 0 ? html`
<mdui-list>
${res.map(cred => html`
<mdui-list-item>
${cred.name || 'Unnamed'}
<mdui-button-icon data-cred-id="${cred.id}" data-name="${cred.name || 'Unnamed'}" @click="${rename}" icon="edit" slot="end-icon"></mdui-button-icon>
<mdui-button-icon data-cred-id="${cred.id}" @click="${remove}" icon="delete" slot="end-icon"></mdui-button-icon>
</mdui-list-item>`)}
</mdui-list>` : html`
<mdui-list>
<mdui-list-item>No credentials found.</mdui-list-item>
</mdui-list>`}`;
render(creds, list);
};
renderCredentials();
// TODO: Add an ability to create a passkey: Create and register a passkey.
async function register() {
try {
// Start the loading UI.
loading.start();
// Start creating a passkey.
await registerCredential();
// Stop the loading UI.
loading.stop();
// Render the updated passkey list.
renderCredentials();
} catch (e) {
// Stop the loading UI.
loading.stop();
// An InvalidStateError indicates that a passkey already exists on the device.
if (e.name === 'InvalidStateError') {
alert('A passkey already exists for this device.');
// A NotAllowedError indicates the user canceled the operation.
} else if (e.name === 'NotAllowedError') {
return;
// Show other errors in an alert.
} else {
alert(e.message);
console.error(e);
}
}
};
createPasskey.addEventListener('click', register);
امتحانش کن
اگر تا اینجا تمام مراحل را دنبال کرده باشید، قابلیت ایجاد، ثبت و نمایش رمز عبور را در وبسایت پیادهسازی کردهاید!
برای امتحان کردن آن، این مراحل را دنبال کنید:
- در سایت، با یک نام کاربری و رمز عبور تصادفی وارد شوید.
- روی ایجاد کلید عبور کلیک کنید.
- هویت خود را با قفل صفحه دستگاه تأیید کنید.
- تأیید کنید که یک رمز عبور ثبت شده و در بخش «رمزهای عبور ثبت شده شما» در صفحه وب نمایش داده میشود.

تغییر نام و حذف رمزهای عبور ثبت شده
شما باید بتوانید رمزهای عبور ثبت شده در لیست را تغییر نام دهید یا حذف کنید. میتوانید نحوه عملکرد آن را در کدی که همراه با codelab ارائه میشود، بررسی کنید.
در کروم، میتوانید رمزهای عبور ثبتشده را از chrome://settings/passkeys در دسکتاپ یا از password manager در تنظیمات در اندروید حذف کنید.
برای اطلاعات بیشتر در مورد نحوه تغییر نام و حذف رمزهای عبور ثبت شده در سایر پلتفرمها، به صفحات پشتیبانی مربوطه برای آن پلتفرمها مراجعه کنید.
۵. قابلیت احراز هویت با رمز عبور را اضافه کنید
اکنون کاربران میتوانند یک کلید عبور ایجاد و ثبت کنند و آماده استفاده از آن به عنوان راهی برای تأیید اعتبار در وبسایت شما به صورت ایمن هستند. اکنون باید قابلیت تأیید اعتبار با کلید عبور را به وبسایت خود اضافه کنید.
تابع authenticate() را ایجاد کنید
- در فایل
public/client.jsپس از کامنت مربوطه، تابعی به نامauthenticate()ایجاد کنید که به صورت محلی کاربر و سپس در مقابل سرور را تأیید کند:
فایل public/client.js
// TODO: Add an ability to authenticate with a passkey: Create the authenticate() function.
export async function authenticate() {
// TODO: Add an ability to authenticate with a passkey: Obtain the challenge and other options from the server endpoint.
// TODO: Add an ability to authenticate with a passkey: Locally verify the user and get a credential.
// TODO: Add an ability to authenticate with a passkey: Verify the credential.
};
دریافت چالش و سایر گزینهها از سرور نهایی
قبل از اینکه از کاربر بخواهید احراز هویت کند، باید پارامترهایی را برای ارسال WebAuthn از سرور درخواست کنید، از جمله یک چالش.
- در بدنه تابع
authenticate()پس از کامنت مربوطه، تابع_fetch()را برای ارسال یک درخواستPOSTبه سرور فراخوانی کنید:
فایل public/client.js
// TODO: Add an ability to authenticate with a passkey: Obtain the challenge and other options from the server endpoint.
// Base64URL decode the challenge.
const options = PublicKeyCredential.parseRequestOptionsFromJSON(_options);
سرور این codelab به گونهای طراحی شده است که JSON را تا حد امکان مشابه دیکشنری PublicKeyCredentialRequestOptions که به WebAuthn navigator.credentials.get() API ارسال میشود، برگرداند. قطعه کد زیر شامل گزینههای نمونهای است که باید دریافت کنید:
{
"challenge": *****,
"rpId": "localhost",
"allowCredentials": []
}
جدول زیر جامع نیست، اما شامل پارامترهای مهم در دیکشنری PublicKeyCredentialRequestOptions است:
پارامترها | توضیحات |
یک چالش ایجاد شده توسط سرور در یک شیء | |
شناسه RP یک دامنه است. یک وبسایت میتواند دامنه یا پسوند قابل ثبت خود را مشخص کند. این مقدار باید با پارامتر | |
این ویژگی برای یافتن احراز هویتکنندگان واجد شرایط برای این احراز هویت استفاده میشود. برای اینکه مرورگر بتواند انتخابگر حساب را نمایش دهد، یک آرایه خالی به آن ارسال کنید یا آن را نامشخص بگذارید. درباره نحوه رفتار | |
روی یک مقدار |
کاربر را به صورت محلی تأیید کنید و اعتبارنامه دریافت کنید
- در بدنه تابع
authenticate()پس از کامنت مربوطه، پارامترchallengeرا به دودویی تبدیل کنید:
فایل public/client.js
// TODO: Add an ability to authenticate with a passkey: Locally verify the user and get a credential.
// Base64URL decode the challenge.
options.challenge = base64url.decode(options.challenge);
- یک آرایه خالی به پارامتر
allowCredentialsارسال کنید تا هنگام احراز هویت کاربر، یک انتخابگر حساب کاربری باز شود:
فایل public/client.js
// An empty allowCredentials array invokes an account selector by discoverable credentials.
options.allowCredentials = [];
انتخابگر حساب از اطلاعات کاربر که به همراه رمز عبور ذخیره شده است، استفاده میکند.
- متد
navigator.credentials.get()را به همراه گزینهmediation: 'conditional'فراخوانی کنید:
فایل public/client.js
// Invoke the WebAuthn get() method.
const cred = await navigator.credentials.get({
publicKey: options,
// Request a conditional UI.
mediation: 'conditional'
});
این گزینه به مرورگر دستور میدهد که به عنوان بخشی از تکمیل خودکار فرم، کلیدهای عبور را به صورت مشروط پیشنهاد دهد.
اعتبارنامه را تأیید کنید
پس از اینکه کاربر هویت خود را به صورت محلی تأیید کرد، باید یک شیء اعتبارنامه دریافت کنید که حاوی امضایی است که میتوانید آن را روی سرور تأیید کنید.
قطعه کد زیر شامل یک نمونه شیء PublicKeyCredential است:
{
"id": *****,
"rawId": *****,
"type": "public-key",
"response": {
"clientDataJSON": *****,
"authenticatorData": *****,
"signature": *****,
"userHandle": *****
},
authenticatorAttachment: "platform"
}
جدول زیر جامع نیست، اما شامل پارامترهای مهم در شیء PublicKeyCredential است:
پارامترها | توضیحات |
شناسهی رمزگذاریشدهی Base64URL مربوط به اعتبارنامهی رمز عبورِ احراز هویتشده. | |
یک نسخه شیء | |
یک شیء | |
یک شیء | |
یک شیء | |
یک شیء | |
وقتی این اعتبارنامه از دستگاه محلی میآید، یک رشته |
برای ارسال شیء اعتبارنامه به سرور، مراحل زیر را دنبال کنید:
- در بدنه تابع
authenticate()پس از توضیحات مربوطه، پارامترهای دودویی اعتبارنامه را کدگذاری کنید تا بتوان آن را به صورت رشته به سرور تحویل داد. میتوانید.toJSON()برای انجام این کار استفاده کنید:
فایل public/client.js
// TODO: Add an ability to authenticate with a passkey: Verify the credential.
// Encode and serialize the `PublicKeyCredential`.
const credential = JSON.stringify(cred);
- ارسال شیء به سرور:
فایل public/client.js
return await _fetch(`/auth/signinResponse`, credential);
وقتی برنامه را اجرا میکنید، سرور HTTP code 200 را برمیگرداند که نشان میدهد اعتبارنامه تأیید شده است.
حالا شما تابع authentication() را به طور کامل دارید!
کد راهحل این بخش را بررسی کنید
فایل public/client.js
// TODO: Add an ability to authenticate with a passkey: Create the authenticate() function.
export async function authenticate() {
// TODO: Add an ability to authenticate with a passkey: Obtain the
challenge and other options from the server endpoint.
const options = await _fetch('/auth/signinRequest');
// TODO: Add an ability to authenticate with a passkey: Locally verify
the user and get a credential.
// Base64URL decode the challenge.
options.challenge = base64url.decode(options.challenge);
// The empty allowCredentials array invokes an account selector
by discoverable credentials.
options.allowCredentials = [];
// Invoke the WebAuthn get() function.
const cred = await navigator.credentials.get({
publicKey: options,
// Request a conditional UI.
mediation: 'conditional'
});
// TODO: Add an ability to authenticate with a passkey: Verify the credential.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// Base64URL encode some values.
const clientDataJSON = base64url.encode(cred.response.clientDataJSON);
const authenticatorData =
base64url.encode(cred.response.authenticatorData);
const signature = base64url.encode(cred.response.signature);
const userHandle = base64url.encode(cred.response.userHandle);
credential.response = {
clientDataJSON,
authenticatorData,
signature,
userHandle,
};
return await _fetch(`/auth/signinResponse`, credential);
};
۶. اضافه کردن رمز عبور به قابلیت تکمیل خودکار فرمها در مرورگر
وقتی کاربر برمیگردد، شما میخواهید که کاربر تا حد امکان به راحتی و با امنیت کامل وارد سیستم شود. اگر دکمهی « ورود با کلید عبور» را به صفحهی ورود اضافه کنید، کاربر میتواند دکمه را فشار دهد، یک کلید عبور را در انتخابگر حساب مرورگر انتخاب کند و از قفل صفحه برای تأیید هویت استفاده کند.
با این حال، انتقال از رمز عبور به کلید عبور برای همه کاربران به طور همزمان اتفاق نمیافتد. این بدان معناست که تا زمانی که همه کاربران به کلیدهای عبور منتقل نشوند، نمیتوانید از شر رمزهای عبور خلاص شوید، بنابراین باید فرم ورود مبتنی بر رمز عبور را تا آن زمان رها کنید. اگرچه، اگر فرم رمز عبور و دکمه کلید عبور را باقی بگذارید، کاربران مجبور خواهند بود بدون نیاز به انتخاب بین یکی از آنها برای ورود به سیستم، یکی را انتخاب کنند. در حالت ایدهآل، شما یک فرآیند ورود ساده میخواهید.
اینجاست که رابط کاربری شرطی وارد عمل میشود. رابط کاربری شرطی یک ویژگی WebAuthn است که در آن میتوانید یک فیلد ورودی فرم ایجاد کنید تا علاوه بر رمزهای عبور، یک کلید عبور را به عنوان بخشی از موارد تکمیل خودکار پیشنهاد دهد. اگر کاربری روی کلید عبور در پیشنهادات تکمیل خودکار ضربه بزند، از کاربر خواسته میشود که از قفل صفحه نمایش دستگاه برای تأیید هویت خود به صورت محلی استفاده کند. این یک تجربه کاربری یکپارچه است زیرا عملکرد کاربر تقریباً مشابه ورود به سیستم مبتنی بر رمز عبور است.

فعال کردن رابط کاربری شرطی
برای فعال کردن یک رابط کاربری شرطی، تنها کاری که باید انجام دهید این است که یک توکن webauthn را در ویژگی autocomplete یک فیلد ورودی اضافه کنید. با تنظیم توکن، میتوانید متد navigator.credentials.get() را با رشته mediation: 'conditional' فراخوانی کنید تا رابط کاربری قفل صفحه به صورت شرطی فعال شود.
- برای فعال کردن یک رابط کاربری شرطی، فیلدهای ورودی نام کاربری موجود را با کد HTML زیر پس از کامنت مربوطه در فایل
view/index.htmlجایگزین کنید:
نمایش/فهرست.html
<!-- TODO: Add passkeys to the browser autofill: Enable conditional UI. -->
<mdui-text-field id="username" label="Username" name="username" autocomplete="username webauthn" autofocus></mdui-text-field>
تشخیص ویژگیها، فراخوانی WebAuthn و فعال کردن یک رابط کاربری مشروط
- در فایل
view/index.htmlپس از کامنت مربوطه، عبارتimportموجود را با کد زیر جایگزین کنید:
نمایش/فهرست.html
// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI.
import {
$,
_fetch,
loading,
authenticate
} from "/client.js";
این کد تابع authenticate() را که قبلاً پیادهسازی کردهاید، وارد میکند.
- تأیید کنید که شیء
window.PulicKeyCredentialدر دسترس است و متدPublicKeyCredential.isConditionalMediationAvailable()مقدارtrueرا برمیگرداند، و سپس تابعauthenticate()را فراخوانی کنید:
نمایش/فهرست.html
// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI.
if (window.PublicKeyCredential &&
PublicKeyCredential.getClientCapabilities) {
try {
// Is conditional UI available in this browser?
const capabilities = await PublicKeyCredential.getClientCapabilities();
if (capabilities.conditionalGet) {
// If conditional UI is available, invoke the authenticate() function.
const user = await authenticate();
if (user) {
// Proceed only when authentication succeeds.
$("#username").value = user.username;
loading.start();
location.href = "/home";
} else {
throw new Error("User not found.");
}
}
} catch (e) {
loading.stop();
// A NotAllowedError indicates that the user canceled the operation.
if (e.name !== "NotAllowedError") {
console.error(e);
alert(e.message);
}
}
}
کد راهحل این بخش را بررسی کنید
نمایش/فهرست.html
<!-- TODO: Add passkeys to the browser autofill: Enable conditional UI. -->
<mdui-text-field id="username" label="Username" name="username" autocomplete="username webauthn" autofocus></mdui-text-field>
نمایش/فهرست.html
// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI.
import {
$,
_fetch,
loading,
authenticate
} from '/client.js';
نمایش/فهرست.html
// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI.
// Is WebAuthn available on this browser?
if (window.PublicKeyCredential &&
PublicKeyCredential.getClientCapabilities) {
try {
// Is conditional UI available in this browser?
const capabilities = await PublicKeyCredential.getClientCapabilities();
if (capabilities.conditionalGet) {
// If conditional UI is available, invoke the authenticate() function.
const user = await authenticate();
if (user) {
// Proceed only when authentication succeeds.
$('#username').value = user.username;
loading.start();
location.href = '/home';
} else {
throw new Error('User not found.');
}
}
} catch (e) {
loading.stop();
// A NotAllowedError indicates that the user canceled the operation.
if (e.name !== 'NotAllowedError') {
console.error(e);
alert(e.message);
}
}
}
امتحانش کن
شما ایجاد، ثبت، نمایش و احراز هویت رمزهای عبور را در وبسایت خود پیادهسازی کردهاید.
برای امتحان کردن آن، این مراحل را دنبال کنید:
- به برگه پیشنمایش بروید.
- در صورت لزوم، از سیستم خارج شوید.
- روی کادر متن نام کاربری کلیک کنید. یک کادر محاورهای ظاهر میشود.
- حسابی را که میخواهید با آن وارد سیستم شوید، انتخاب کنید.
- هویت خود را با قفل صفحه دستگاه تأیید کنید. شما به صفحه
/homeهدایت میشوید و وارد سیستم میشوید.

۷. تبریک میگویم!
این آزمایشگاه کد را تمام کردید! اگر سوالی دارید، میتوانید آنها را در فهرست پستی FIDO-DEV یا در StackOverflow با برچسب passkey بپرسید.