1. قبل از شروع
Web Authentication API که با نام WebAuthn نیز شناخته میشود، به شما امکان میدهد اعتبارنامههای کلید عمومی با محدوده مبدا را برای احراز هویت کاربران ایجاد و استفاده کنید.
API از استفاده از احراز هویت BLE، NFC و رومینگ USB-U2F یا FIDO2 - که به عنوان کلیدهای امنیتی نیز شناخته می شود - و همچنین یک تأیید کننده پلت فرم که به کاربران امکان می دهد با اثر انگشت یا قفل صفحه خود احراز هویت کنند، پشتیبانی می کند.
در این کد لبه، شما یک وب سایت با یک قابلیت احراز هویت مجدد ساده می سازید که از حسگر اثر انگشت استفاده می کند. احراز هویت مجدد از دادههای حساب محافظت میکند زیرا کاربرانی را که قبلاً وارد یک وبسایت شدهاند میخواهد وقتی سعی میکنند وارد بخشهای مهم وبسایت شوند یا پس از مدت زمان مشخصی دوباره از وبسایت بازدید کنند، دوباره احراز هویت کنند.
پیش نیازها
- درک اولیه از نحوه عملکرد WebAuthn
- مهارت های اولیه برنامه نویسی با جاوا اسکریپت
کاری که خواهی کرد
- وب سایتی با قابلیت احراز هویت مجدد ساده بسازید که از حسگر اثر انگشت استفاده می کند
آنچه شما نیاز دارید
- یکی از دستگاه های زیر:
- یک دستگاه اندروید، ترجیحا با سنسور بیومتریک
- یک iPhone یا iPad با Touch ID یا Face ID در iOS 14 یا بالاتر
- MacBook Pro یا Air با Touch ID در macOS Big Sur یا بالاتر
- Windows 10 19H1 یا بالاتر با راه اندازی Windows Hello
- یکی از مرورگرهای زیر:
- Google Chrome 67 یا بالاتر
- مایکروسافت اج 85 یا بالاتر
- سافاری 14 یا بالاتر
2. راه اندازی شوید
در این کد لبه از سرویسی به نام glitch استفاده می کنید. اینجاست که میتوانید کدهای سرویس گیرنده و سمت سرور را با جاوا اسکریپت ویرایش کنید و آنها را فوراً اجرا کنید.
به https://glitch.com/edit/#!/webauthn-codelab-start بروید .
ببینید چگونه کار می کند
برای مشاهده وضعیت اولیه وب سایت مراحل زیر را دنبال کنید:
- کلیک نمایش > در یک پنجره جدید برای دیدن وب سایت زنده .
- نام کاربری دلخواه خود را وارد کنید و روی Next کلیک کنید.
- یک رمز عبور وارد کنید و روی Sign-in کلیک کنید.
رمز عبور نادیده گرفته شده است، اما شما هنوز احراز هویت هستید. شما در صفحه اصلی فرود می آیید.
- روی Try reauth کلیک کنید و مراحل دوم، سوم و چهارم را تکرار کنید.
- روی خروج کلیک کنید.
توجه داشته باشید که هر بار که سعی میکنید وارد شوید، باید رمز عبور را وارد کنید. این رمز کاربری را شبیهسازی میکند که قبل از اینکه بتواند به بخش مهمی از وبسایت دسترسی پیدا کند، نیاز به احراز هویت مجدد دارد.
کد را دوباره میکس کنید
- به WebAuthn / FIDO2 API Codelab بروید .
- روی نام پروژه خود > Remix Project کلیک کنید پروژه را منشعب کنید و با نسخه خود در یک URL جدید ادامه دهید.
3. ثبت اعتبار با اثر انگشت
شما باید یک اعتبار ایجاد شده توسط یک UVPA، یک احراز هویت که در دستگاه تعبیه شده است و هویت کاربر را تأیید می کند، ثبت کنید. این معمولا به عنوان حسگر اثر انگشت بسته به دستگاه کاربر دیده می شود.
شما این ویژگی را به صفحه /home
اضافه می کنید:
تابع registerCredential()
را ایجاد کنید
یک تابع registerCredential()
ایجاد کنید که یک اعتبار جدید را ثبت می کند.
public/client.js
export const registerCredential = async () => {
};
چالش و گزینه های دیگر را از نقطه پایانی سرور دریافت کنید
قبل از اینکه از کاربر بخواهید یک اعتبارنامه جدید ثبت کند، از سرور درخواست کنید که پارامترها را برای ارسال در WebAuthn، از جمله یک چالش، بازگرداند. خوشبختانه، شما در حال حاضر یک نقطه پایانی سرور دارید که با چنین پارامترهایی پاسخ می دهد.
کد زیر را به registerCredential()
اضافه کنید.
public/client.js
const opts = {
attestation: 'none',
authenticatorSelection: {
authenticatorAttachment: 'platform',
userVerification: 'required',
requireResidentKey: false
}
};
const options = await _fetch('/auth/registerRequest', opts);
پروتکل بین سرور و کلاینت بخشی از مشخصات WebAuthn نیست. با این حال، این کد لبه برای همسویی با مشخصات WebAuthn طراحی شده است و شی JSON که به سرور ارسال می کنید بسیار شبیه به PublicKeyCredentialCreationOptions
است تا برای شما بصری باشد. جدول زیر شامل پارامترهای مهمی است که می توانید به سرور منتقل کنید و توضیح می دهد که آنها چه کاری انجام می دهند:
مولفه های | توضیحات | ||
| اولویت برای انتقال گواهی - | ||
| آرایه ای از | ||
| | احراز هویت موجود را فیلتر کنید. اگر می خواهید یک احراز هویت به دستگاه متصل شود، از " | |
| تعیین کنید که آیا تأیید اعتبار کاربر محلی « | ||
| اگر اعتبار ایجاد شده باید برای UX انتخابگر حساب آینده در دسترس باشد، از |
برای کسب اطلاعات بیشتر در مورد این گزینه ها، به 5.4 مراجعه کنید. گزینههای Credential Creation (لغت نامه PublicKeyCredentialCreationOptions
) .
موارد زیر نمونه گزینه هایی هستند که از سرور دریافت می کنید.
{
"rp": {
"name": "WebAuthn Codelab",
"id": "webauthn-codelab.glitch.me"
},
"user": {
"displayName": "User Name",
"id": "...",
"name": "test"
},
"challenge": "...",
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
}, {
"type": "public-key",
"alg": -257
}
],
"timeout": 1800000,
"attestation": "none",
"excludeCredentials": [
{
"id": "...",
"type": "public-key",
"transports": [
"internal"
]
}
],
"authenticatorSelection": {
"authenticatorAttachment": "platform",
"userVerification": "required"
}
}
اعتبارنامه ایجاد کنید
- از آنجایی که این گزینه ها به صورت کدگذاری شده برای عبور از پروتکل HTTP تحویل داده می شوند، برخی از پارامترها را به باینری بازگردانید، به طور خاص،
user.id
،challenge
و نمونه هایی ازid
موجود در آرایهexcludeCredentials
:
public/client.js
options.user.id = base64url.decode(options.user.id);
options.challenge = base64url.decode(options.challenge);
if (options.excludeCredentials) {
for (let cred of options.excludeCredentials) {
cred.id = base64url.decode(cred.id);
}
}
- برای ایجاد یک اعتبار جدید، متد
navigator.credentials.create()
را فراخوانی کنید.
با این تماس، مرورگر با احراز هویت کننده تعامل دارد و سعی می کند هویت کاربر را با UVPA تأیید کند.
public/client.js
const cred = await navigator.credentials.create({
publicKey: options,
});
هنگامی که کاربر هویت خود را تأیید کرد، باید یک شی اعتبار دریافت کنید که می توانید آن را به سرور ارسال کنید و تأیید کننده را ثبت کنید.
اعتبارنامه را در نقطه پایانی سرور ثبت کنید
در اینجا نمونه ای از شی اعتبارنامه است که باید دریافت می کردید.
{
"id": "...",
"rawId": "...",
"type": "public-key",
"response": {
"clientDataJSON": "...",
"attestationObject": "..."
}
}
- مانند زمانی که یک شی گزینه برای ثبت اعتبار دریافت کردید، پارامترهای باینری اعتبارنامه را رمزگذاری کنید تا بتوان آن را به عنوان یک رشته به سرور تحویل داد:
public/client.js
const credential = {};
credential.id = cred.id;
credential.rawId = base64url.encode(cred.rawId);
credential.type = cred.type;
if (cred.response) {
const clientDataJSON =
base64url.encode(cred.response.clientDataJSON);
const attestationObject =
base64url.encode(cred.response.attestationObject);
credential.response = {
clientDataJSON,
attestationObject,
};
}
- شناسه اعتبار را به صورت محلی ذخیره کنید تا بتوانید هنگام بازگشت کاربر از آن برای احراز هویت استفاده کنید:
public/client.js
localStorage.setItem(`credId`, credential.id);
- شی را به سرور ارسال کنید و اگر
HTTP code 200
، اعتبار جدید را با موفقیت ثبت کنید.
public/client.js
return await _fetch('/auth/registerResponse' , credential);
شما اکنون تابع registerCredential()
کامل را دارید!
کد نهایی این بخش
public/client.js
...
export const registerCredential = async () => {
const opts = {
attestation: 'none',
authenticatorSelection: {
authenticatorAttachment: 'platform',
userVerification: 'required',
requireResidentKey: false
}
};
const options = await _fetch('/auth/registerRequest', opts);
options.user.id = base64url.decode(options.user.id);
options.challenge = base64url.decode(options.challenge);
if (options.excludeCredentials) {
for (let cred of options.excludeCredentials) {
cred.id = base64url.decode(cred.id);
}
}
const cred = await navigator.credentials.create({
publicKey: options
});
const credential = {};
credential.id = cred.id;
credential.rawId = base64url.encode(cred.rawId);
credential.type = cred.type;
if (cred.response) {
const clientDataJSON =
base64url.encode(cred.response.clientDataJSON);
const attestationObject =
base64url.encode(cred.response.attestationObject);
credential.response = {
clientDataJSON,
attestationObject
};
}
localStorage.setItem(`credId`, credential.id);
return await _fetch('/auth/registerResponse' , credential);
};
...
4. برای ثبت، دریافت و حذف اعتبارنامه، UI بسازید
داشتن لیستی از اعتبارنامه ها و دکمه های ثبت شده برای حذف آنها خوب است.
ایجاد جایبان UI
UI را به لیست اعتبارنامه ها و یک دکمه برای ثبت اعتبار جدید اضافه کنید. بسته به در دسترس بودن یا نبودن ویژگی، کلاس hidden
را از پیام هشدار یا دکمه ثبت اعتبار جدید حذف می کنید. ul#list
جایگاهی برای افزودن لیستی از اعتبارنامه های ثبت شده است.
views/home.html
<p id="uvpa_unavailable" class="hidden">
This device does not support User Verifying Platform Authenticator. You can't register a credential.
</p>
<h3 class="mdc-typography mdc-typography--headline6">
Your registered credentials:
</h3>
<section>
<div id="list"></div>
</section>
<mwc-button id="register" class="hidden" icon="fingerprint" raised>Add a credential</mwc-button>
تشخیص ویژگی و در دسترس بودن UVPA
این مراحل را برای بررسی در دسترس بودن UVPA دنبال کنید:
-
window.PublicKeyCredential
را بررسی کنید تا بررسی کنید که آیا WebAuthn در دسترس است یا خیر. - با
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
تماس بگیرید تا بررسی کنید که آیا UVPA موجود است یا خیر. اگر آنها در دسترس هستند، دکمه ثبت اعتبار جدید را نشان می دهید. اگر یکی از آنها در دسترس نباشد، پیام هشدار را نشان می دهید.
views/home.html
const register = document.querySelector('#register');
if (window.PublicKeyCredential) {
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
.then(uvpaa => {
if (uvpaa) {
register.classList.remove('hidden');
} else {
document
.querySelector('#uvpa_unavailable')
.classList.remove('hidden');
}
});
} else {
document
.querySelector('#uvpa_unavailable')
.classList.remove('hidden');
}
دریافت و نمایش یک لیست از اعتبار
- یک
getCredentials()
ایجاد کنید تا بتوانید اعتبارنامه های ثبت شده را دریافت کرده و در یک لیست نمایش دهید. خوشبختانه، شما در حال حاضر یک نقطه پایانی مفید در سرور/auth/getKeys
دارید که می توانید اعتبارنامه های ثبت شده برای کاربر وارد شده را از آن دریافت کنید.
JSON برگشتی شامل اطلاعات اعتبارنامه، مانند id
و publicKey
است. می توانید HTML بسازید تا آنها را به کاربر نشان دهید.
views/home.html
const getCredentials = async () => {
const res = await _fetch('/auth/getKeys');
const list = document.querySelector('#list');
const creds = html`${res.credentials.length > 0 ? res.credentials.map(cred => html`
<div class="mdc-card credential">
<span class="mdc-typography mdc-typography--body2">${cred.credId}</span>
<pre class="public-key">${cred.publicKey}</pre>
<div class="mdc-card__actions">
<mwc-button id="${cred.credId}" @click="${removeCredential}" raised>Remove</mwc-button>
</div>
</div>`) : html`
<p>No credentials found.</p>
`}`;
render(creds, list);
};
- برای نمایش اعتبارنامه های موجود به محض اینکه کاربر در صفحه
/home
قرار گرفت،getCredentials()
فراخوانی کنید.
views/home.html
getCredentials();
اعتبارنامه را حذف کنید
در لیست اعتبارنامه ها، یک دکمه برای حذف هر اعتبار اضافه کرده اید. برای حذف آنها می توانید درخواستی به /auth/removeKey
به همراه پارامتر query credId
کنید.
public/client.js
export const unregisterCredential = async (credId) => {
localStorage.removeItem('credId');
return _fetch(`/auth/removeKey?credId=${encodeURIComponent(credId)}`);
};
-
unregisterCredential
را به بیانیهimport
موجود اضافه کنید.
views/home.html
import { _fetch, unregisterCredential } from '/client.js';
- هنگامی که کاربر روی Remove کلیک می کند، یک تابع برای تماس اضافه کنید.
views/home.html
const removeCredential = async e => {
try {
await unregisterCredential(e.target.id);
getCredentials();
} catch (e) {
alert(e);
}
};
ثبت اعتبار
هنگامی که کاربر روی Add a credential کلیک می کند، می توانید registerCredential()
را برای ثبت یک اعتبار جدید فراخوانی کنید.
-
registerCredential
را به بیانیهimport
موجود اضافه کنید.
views/home.html
import { _fetch, registerCredential, unregisterCredential } from '/client.js';
-
registerCredential()
را با گزینه هایی برایnavigator.credentials.create()
فراخوانی کنید.
فراموش نکنید که پس از ثبت نام با فراخوانی getCredentials()
لیست اعتبار را تمدید کنید.
views/home.html
register.addEventListener('click', e => {
registerCredential().then(user => {
getCredentials();
}).catch(e => alert(e));
});
اکنون باید بتوانید یک اعتبارنامه جدید ثبت کنید و اطلاعات مربوط به آن را نمایش دهید. می توانید آن را در وب سایت زنده خود امتحان کنید.
کد نهایی این بخش
views/home.html
...
<p id="uvpa_unavailable" class="hidden">
This device does not support User Verifying Platform Authenticator. You can't register a credential.
</p>
<h3 class="mdc-typography mdc-typography--headline6">
Your registered credentials:
</h3>
<section>
<div id="list"></div>
<mwc-fab id="register" class="hidden" icon="add"></mwc-fab>
</section>
<mwc-button raised><a href="/reauth">Try reauth</a></mwc-button>
<mwc-button><a href="/auth/signout">Sign out</a></mwc-button>
</main>
<script type="module">
import { _fetch, registerCredential, unregisterCredential } from '/client.js';
import { html, render } from 'https://unpkg.com/lit-html@1.0.0/lit-html.js?module';
const register = document.querySelector('#register');
if (window.PublicKeyCredential) {
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
.then(uvpaa => {
if (uvpaa) {
register.classList.remove('hidden');
} else {
document
.querySelector('#uvpa_unavailable')
.classList.remove('hidden');
}
});
} else {
document
.querySelector('#uvpa_unavailable')
.classList.remove('hidden');
}
const getCredentials = async () => {
const res = await _fetch('/auth/getKeys');
const list = document.querySelector('#list');
const creds = html`${res.credentials.length > 0 ? res.credentials.map(cred => html`
<div class="mdc-card credential">
<span class="mdc-typography mdc-typography--body2">${cred.credId}</span>
<pre class="public-key">${cred.publicKey}</pre>
<div class="mdc-card__actions">
<mwc-button id="${cred.credId}" @click="${removeCredential}" raised>Remove</mwc-button>
</div>
</div>`) : html`
<p>No credentials found.</p>
`}`;
render(creds, list);
};
getCredentials();
const removeCredential = async e => {
try {
await unregisterCredential(e.target.id);
getCredentials();
} catch (e) {
alert(e);
}
};
register.addEventListener('click', e => {
registerCredential({
attestation: 'none',
authenticatorSelection: {
authenticatorAttachment: 'platform',
userVerification: 'required',
requireResidentKey: false
}
})
.then(user => {
getCredentials();
})
.catch(e => alert(e));
});
</script>
...
public/client.js
...
export const unregisterCredential = async (credId) => {
localStorage.removeItem('credId');
return _fetch(`/auth/removeKey?credId=${encodeURIComponent(credId)}`);
};
...
5. احراز هویت کاربر با اثر انگشت
اکنون یک اعتبار ثبت شده و آماده استفاده به عنوان راهی برای احراز هویت کاربر دارید. اکنون قابلیت احراز هویت مجدد را به وب سایت اضافه می کنید. در اینجا تجربه کاربر است:
هنگامی که کاربر در صفحه /reauth
قرار می گیرد، اگر احراز هویت بیومتریک امکان پذیر باشد، دکمه Authenticate را می بیند. احراز هویت با اثر انگشت (UVPA) زمانی شروع میشود که روی Authenticate ضربه میزنند، با موفقیت تأیید میکنند و سپس در صفحه /home
فرود میآیند. اگر احراز هویت بیومتریک در دسترس نباشد یا احراز هویت با بیومتریک ناموفق باشد، رابط کاربری مجدداً از فرم رمز عبور موجود استفاده میکند.
ایجاد تابع authenticate()
یک تابع به نام authenticate()
ایجاد کنید که هویت کاربر را با اثر انگشت تأیید می کند. شما کد جاوا اسکریپت را در اینجا اضافه می کنید:
public/client.js
export const authenticate = async () => {
};
چالش و گزینه های دیگر را از نقطه پایانی سرور دریافت کنید
- قبل از احراز هویت، بررسی کنید که آیا کاربر یک شناسه اعتبار ذخیره شده دارد یا خیر و در صورت داشتن آن، آن را به عنوان پارامتر جستجو تنظیم کنید.
هنگامی که یک شناسه اعتبار را همراه با گزینه های دیگر ارائه می کنید، سرور می تواند allowCredentials
مربوطه را ارائه دهد و این امر تأیید کاربر را قابل اعتماد می کند.
public/client.js
const opts = {};
let url = '/auth/signinRequest';
const credId = localStorage.getItem(`credId`);
if (credId) {
url += `?credId=${encodeURIComponent(credId)}`;
}
- قبل از اینکه از کاربر بخواهید احراز هویت کند، از سرور بخواهید یک چالش و سایر پارامترها را پس بگیرد.
_fetch()
را باopts
به عنوان آرگومان برای ارسال درخواست POST به سرور فراخوانی کنید.
public/client.js
const options = await _fetch(url, opts);
در اینجا نمونههایی از گزینههایی هستند که باید دریافت کنید (همتراز با PublicKeyCredentialRequestOptions
).
{
"challenge": "...",
"timeout": 1800000,
"rpId": "webauthn-codelab.glitch.me",
"userVerification": "required",
"allowCredentials": [
{
"id": "...",
"type": "public-key",
"transports": [
"internal"
]
}
]
}
مهمترین گزینه در اینجا allowCredentials
است. هنگامی که گزینههایی را از سرور دریافت میکنید، بسته به اینکه اعتباری با شناسه در پارامتر query در سمت سرور پیدا شود، allowCredentials
باید یا یک شی واحد در یک آرایه یا یک آرایه خالی باشد.
- زمانی که
allowCredentials
یک آرایه خالی است، وعده را باnull
حل کنید تا UI دوباره به درخواست رمز عبور بازگردد.
if (options.allowCredentials.length === 0) {
console.info('No registered credentials found.');
return Promise.resolve(null);
}
کاربر را به صورت محلی تأیید کنید و یک اعتبار دریافت کنید
- از آنجایی که این گزینهها بهمنظور عبور از پروتکل HTTP به صورت رمزگذاری شده تحویل داده میشوند، برخی از پارامترها را به باینری، بهویژه
challenge
و نمونههایی ازid
موجود در آرایهallowCredentials
، برگردانید:
public/client.js
options.challenge = base64url.decode(options.challenge);
for (let cred of options.allowCredentials) {
cred.id = base64url.decode(cred.id);
}
- برای تأیید هویت کاربر با یک UVPA، متد
navigator.credentials.get()
را فراخوانی کنید.
public/client.js
const cred = await navigator.credentials.get({
publicKey: options
});
هنگامی که کاربر هویت خود را تأیید کرد، باید یک شی اعتبار دریافت کنید که می توانید آن را به سرور ارسال کنید و کاربر را احراز هویت کنید.
تأیید اعتبار
در اینجا یک نمونه شی PublicKeyCredential
( response
AuthenticatorAssertionResponse
است) که باید دریافت می کردید آمده است:
{
"id": "...",
"type": "public-key",
"rawId": "...",
"response": {
"clientDataJSON": "...",
"authenticatorData": "...",
"signature": "...",
"userHandle": ""
}
}
- پارامترهای باینری اعتبارنامه را رمزگذاری کنید تا بتوان آن را به عنوان یک رشته به سرور تحویل داد:
public/client.js
const credential = {};
credential.id = cred.id;
credential.type = cred.type;
credential.rawId = base64url.encode(cred.rawId);
if (cred.response) {
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,
};
}
- شی را به سرور ارسال کنید و اگر
HTTP code 200
، کاربر را با موفقیت وارد شده در نظر بگیرید:
public/client.js
return await _fetch(`/auth/signinResponse`, credential);
اکنون تابع authentication()
کامل را دارید!
کد نهایی این بخش
public/client.js
...
export const authenticate = async () => {
const opts = {};
let url = '/auth/signinRequest';
const credId = localStorage.getItem(`credId`);
if (credId) {
url += `?credId=${encodeURIComponent(credId)}`;
}
const options = await _fetch(url, opts);
if (options.allowCredentials.length === 0) {
console.info('No registered credentials found.');
return Promise.resolve(null);
}
options.challenge = base64url.decode(options.challenge);
for (let cred of options.allowCredentials) {
cred.id = base64url.decode(cred.id);
}
const cred = await navigator.credentials.get({
publicKey: options
});
const credential = {};
credential.id = cred.id;
credential.type = cred.type;
credential.rawId = base64url.encode(cred.rawId);
if (cred.response) {
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);
};
...
6. تجربه احراز هویت مجدد را فعال کنید
ساخت UI
وقتی کاربر برمی گردد، می خواهید تا حد امکان به راحتی و ایمن احراز هویت مجدد انجام شود. اینجاست که احراز هویت بیومتریک می درخشد. با این حال، مواردی وجود دارد که احراز هویت بیومتریک ممکن است کار نکند:
- UVPA در دسترس نیست.
- کاربر هنوز هیچ اعتباری را در دستگاه خود ثبت نکرده است.
- فضای ذخیره سازی پاک می شود و دستگاه دیگر شناسه اعتبار را به خاطر نمی آورد.
- کاربر به دلایلی نمی تواند هویت خود را تأیید کند، مانند زمانی که انگشتش خیس است یا ماسک زده است.
به همین دلیل است که همیشه مهم است که سایر گزینههای ورود به سیستم را به عنوان جایگزین ارائه کنید. در این کد لبه، شما از راه حل رمز مبتنی بر فرم استفاده می کنید.
- برای نمایش یک دکمه احراز هویت که علاوه بر فرم رمز عبور، احراز هویت بیومتریک را نیز فراخوانی می کند، رابط کاربری را اضافه کنید.
از کلاس hidden
برای نمایش انتخابی و پنهان کردن یکی از آنها بسته به وضعیت کاربر استفاده کنید.
views/reauth.html
<div id="uvpa_available" class="hidden">
<h2>
Verify your identity
</h2>
<div>
<mwc-button id="reauth" raised>Authenticate</mwc-button>
</div>
<div>
<mwc-button id="cancel">Sign-in with password</mwc-button>
</div>
</div>
-
class="hidden"
را به فرم اضافه کنید:
views/reauth.html
<form id="form" method="POST" action="/auth/password" class="hidden">
تشخیص ویژگی و در دسترس بودن UVPA
در صورت رعایت یکی از این شرایط، کاربران باید با رمز ورود وارد شوند:
- WebAuthn در دسترس نیست.
- UVPA در دسترس نیست.
- شناسه اعتبار برای این UVPA قابل کشف نیست.
به طور انتخابی دکمه احراز هویت را نشان دهید یا آن را پنهان کنید:
views/reauth.html
if (window.PublicKeyCredential) {
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
.then(uvpaa => {
if (uvpaa && localStorage.getItem(`credId`)) {
document
.querySelector('#uvpa_available')
.classList.remove('hidden');
} else {
form.classList.remove('hidden');
}
});
} else {
form.classList.remove('hidden');
}
بازگشت به فرم رمز عبور
کاربر همچنین باید بتواند با رمز ورود به سیستم وارد شود.
هنگامی که کاربر روی Sign in with password کلیک می کند، فرم رمز عبور را نشان داده و دکمه احراز هویت را مخفی کنید.
views/reauth.html
const cancel = document.querySelector('#cancel');
cancel.addEventListener('click', e => {
form.classList.remove('hidden');
document
.querySelector('#uvpa_available')
.classList.add('hidden');
});
احراز هویت بیومتریک را فراخوانی کنید
در نهایت، احراز هویت بیومتریک را فعال کنید.
-
authenticate
را به عبارتimport
موجود اضافه کنید:
views/reauth.html
import { _fetch, authenticate } from '/client.js';
- هنگامی که کاربر برای شروع احراز هویت بیومتریک روی Authenticate ضربه میزند
authenticate()
را فراخوانی کنید.
مطمئن شوید که نقص در احراز هویت بیومتریک به فرم رمز عبور باز می گردد.
views/reauth.html
const button = document.querySelector('#reauth');
button.addEventListener('click', e => {
authenticate().then(user => {
if (user) {
location.href = '/home';
} else {
throw 'User not found.';
}
}).catch(e => {
console.error(e.message || e);
alert('Authentication failed. Use password to sign-in.');
form.classList.remove('hidden');
document.querySelector('#uvpa_available').classList.add('hidden');
});
});
کد نهایی این بخش
views/reauth.html
...
<main class="content">
<div id="uvpa_available" class="hidden">
<h2>
Verify your identity
</h2>
<div>
<mwc-button id="reauth" raised>Authenticate</mwc-button>
</div>
<div>
<mwc-button id="cancel">Sign-in with password</mwc-button>
</div>
</div>
<form id="form" method="POST" action="/auth/password" class="hidden">
<h2>
Enter a password
</h2>
<input type="hidden" name="username" value="{{username}}" />
<div class="mdc-text-field mdc-text-field--filled">
<span class="mdc-text-field__ripple"></span>
<label class="mdc-floating-label" id="password-label">password</label>
<input type="password" class="mdc-text-field__input" aria-labelledby="password-label" name="password" />
<span class="mdc-line-ripple"></span>
</div>
<input type="submit" class="mdc-button mdc-button--raised" value="Sign-In" />
<p class="instructions">password will be ignored in this demo.</p>
</form>
</main>
<script src="https://unpkg.com/material-components-web@7.0.0/dist/material-components-web.min.js"></script>
<script type="module">
new mdc.textField.MDCTextField(document.querySelector('.mdc-text-field'));
import { _fetch, authenticate } from '/client.js';
const form = document.querySelector('#form');
form.addEventListener('submit', e => {
e.preventDefault();
const form = new FormData(e.target);
const cred = {};
form.forEach((v, k) => cred[k] = v);
_fetch(e.target.action, cred)
.then(user => {
location.href = '/home';
})
.catch(e => alert(e));
});
if (window.PublicKeyCredential) {
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()
.then(uvpaa => {
if (uvpaa && localStorage.getItem(`credId`)) {
document
.querySelector('#uvpa_available')
.classList.remove('hidden');
} else {
form.classList.remove('hidden');
}
});
} else {
form.classList.remove('hidden');
}
const cancel = document.querySelector('#cancel');
cancel.addEventListener('click', e => {
form.classList.remove('hidden');
document
.querySelector('#uvpa_available')
.classList.add('hidden');
});
const button = document.querySelector('#reauth');
button.addEventListener('click', e => {
authenticate().then(user => {
if (user) {
location.href = '/home';
} else {
throw 'User not found.';
}
}).catch(e => {
console.error(e.message || e);
alert('Authentication failed. Use password to sign-in.');
form.classList.remove('hidden');
document.querySelector('#uvpa_available').classList.add('hidden');
});
});
</script>
...
7. تبریک می گویم!
شما این کد لبه را تمام کردید!
بیشتر بدانید
- احراز هویت وب: یک API برای دسترسی به اعتبار کلید عمومی سطح 1
- مقدمه ای بر WebAuthn API
- کارگاه آموزشی FIDO WebAuthn
- راهنمای WebAuthn: DUOSEC
- اولین API اندروید FIDO2 شما
تشکر ویژه از Yuriy Ackermann از FIDO Alliance برای کمک شما.