1. Derlemeniz istenen nedir?
Şifre tabanlı girişi destekleyen temel bir web uygulamasıyla başlarsınız.
Ardından, WebAuthn'a bağlı olarak güvenlik anahtarı aracılığıyla iki faktörlü kimlik doğrulama için destek ekleyebilirsiniz. Bunun için aşağıdakileri uygulamanız gerekir:
- Kullanıcının WebAuthn kimlik bilgisini kaydetmenin bir yoludur.
- Kullanıcı, iki faktörlü kimlik doğrulama akışı olduğunda, kullanıcının ikinci faktörden (WebAuthn kimlik bilgisi) sorulması istenir.
- Kimlik bilgisi yönetim arayüzü: Kullanıcıların kimlik bilgilerini yeniden adlandırmasına ve silmesine olanak tanıyan kimlik bilgilerinin listesi.
Tamamlanan web uygulamasına göz atın ve deneyin.
2. WebAuthn hakkında
WebAuthn ile ilgili temel bilgiler
Neden WebAuthn?
Kimlik avı web'de büyük bir güvenlik sorunudur. Hesap ihlallerinin çoğunda, siteler arasında yeniden kullanılan zayıf veya çalınan şifreler kullanılır. Sektörün bu soruna toplu olarak verdiği yanıtlar çok öğeli kimlik doğrulamasıdır, ancak uygulamalar çok parçalıdır ve bunların çoğu hâlâ kimlik avını yeterli düzeyde ele almaz.
Web Authentication API veya WebAuthn, herhangi bir web uygulaması tarafından kullanılabilen, kimlik avına dayanıklı, standartlaştırılmış bir protokoldür.
İşleyiş şekli
Kaynak: webauthn.guide
WebAuthn, sunucuların şifre yerine ortak anahtar kriptografisini kullanarak kullanıcıları kaydettirmesine ve kimlik doğrulaması yapmasına olanak tanır. Web siteleri, özel-ortak anahtar çiftinden oluşan bir kimlik bilgisi oluşturabilir.
- Özel anahtar, kullanıcının cihazında güvenli bir şekilde saklanır.
- Ortak anahtar ve rastgele oluşturulan kimlik bilgisi, depolama için sunucuya gönderilir.
Ortak anahtar, sunucu tarafından kullanıcının kimliğini kanıtlamak için kullanılır. Karşılık gelen özel anahtar olmadan yarar sağlamadığından gizli değildir.
Avantajlar
WebAuthn'un iki temel avantajı vardır:
- Paylaşılan gizli anahtar yok: Sunucu gizli anahtarı depolamaz. Bu, ortak anahtarların kullanıcılar için yararlı olmamasından dolayı veritabanlarının bilgisayar korsanları için daha az çekici olmasını sağlar.
- Kapsamlı kimlik bilgileri:
site.example
için kaydedilen kimlik bilgilerievil-site.example
tarihinde kullanılamaz. Bu sayede WebAuthn kimlik avına dayanıklı hale gelir.
Kullanım alanları
WebAuthn için bir kullanım alanı, güvenlik anahtarıyla iki faktörlü kimlik doğrulamadır. Bu, özellikle kurumsal web uygulamaları için geçerli olabilir.
Tarayıcı desteği
Google, Mozilla, Microsoft, Yubico ve diğer şirketlerin katılımıyla W3C ve FIDO tarafından yazılmıştır.
Sözlük
- Kimlik doğrulayıcı: Bir kullanıcıyı kaydettirip daha sonra kayıtlı kimlik bilgilerine sahip olduğunu iddia eden bir yazılım veya donanım varlığı. İki tür kimlik doğrulayıcı vardır:
- Dolaşım kimlik doğrulayıcı: Kullanıcının oturum açmaya çalıştığı cihazda kullanılabilen kimlik doğrulayıcı. Örnek: USB güvenlik anahtarı, akıllı telefon.
- Platform kimlik doğrulayıcı: Kullanıcı cihazında yerleşik bir kimlik doğrulayıcı. Örnek: Apple' Touch ID.
- Kimlik bilgisi: Özel-ortak anahtar çifti
- Bağlı taraf: Kullanıcının kimliğini doğrulamaya çalışan web sitesi (sunucusu)
- FIDO sunucusu: Kimlik doğrulama için kullanılan sunucu. FIDO, FIDO ittifakının geliştirdiği protokollerden oluşan bir ailedir. Bu protokollerden biri WebAuthn'dur.
Bu atölyede dolaşım kimlik doğrulayıcısını kullanacağız.
3. Başlamadan önce
Gerekenler
Bu codelab'i tamamlamak için gerekenler:
- WebAuthn'la ilgili temel bilgiler.
- JavaScript ve HTML hakkında temel bilgi.
- WebAuthn'u destekleyen güncel bir tarayıcı.
- U2F uyumlu bir güvenlik anahtarı.
Aşağıdakilerden birini güvenlik anahtarı olarak kullanabilirsiniz:
- Chrome çalıştıran Android>=7 (Nougat) yüklü bir Android telefon. Bu durumda, çalışan bir Bluetooth özellikli Windows, macOS veya Chrome OS makineye de ihtiyacınız olur.
- YubiKey gibi bir USB anahtarı.
Kaynak: https://www.yubico.com/products/security-key/
Neler öğreneceksiniz?
Öğrenecekleriniz ✨
- WebAuthn kimlik doğrulaması için ikinci faktör olarak güvenlik anahtarının nasıl kaydedileceği ve kullanılacağı.
- Bu işlemi nasıl kullanıcı dostu hale getirebilirsiniz?
Öğrenemeyeceksiniz ❌
- FIDO sunucusu (kimlik doğrulama için kullanılan sunucu) nasıl oluşturulur? Bir web uygulaması veya site geliştiricisi olarak genellikle mevcut FIDO sunucu uygulamalarını kullanacağınız için bu bir sorun teşkil etmez. Güvendiğiniz sunucu uygulamalarının işlevselliğini ve kalitesini her zaman doğruladığınızdan emin olun. Bu codelab'de, FIDO sunucusu SimpleWebAuthn kullanır. Diğer seçenekler için FIDO Alliance resmi sayfasını ziyaret edin. Açık kaynak kitaplıklar için webauthn.io veya AwesomeWebAuthn'a bakın.
Sorumluluk Reddi Beyanı
Kullanıcının oturum açmak için şifre girmesi gerekir. Ancak bu codelab'de kolaylık sağlamak için şifre saklanmaz veya kontrol edilmez. Gerçek bir uygulamada, uygulamanın sunucu tarafında doğru olup olmadığını kontrol edersiniz.
CSRF kontrolleri, oturum doğrulaması ve temizlik gibi temel güvenlik kontrolleri bu codelab'de belirtilmiştir. Ancak birçok güvenlik önlemi yoktur. Örneğin, kaba kuvvet saldırılarının engellenmesi için şifrelerde giriş sınırı yoktur. Şifreler depolanmadığı için burada önemli değildir, ancak bu kodu üretimde olduğu gibi kullanmadığınızdan emin olun.
4. Kimlik doğrulayıcınızı ayarlama
Bir Android telefonu kimlik doğrulayıcı olarak kullanıyorsanız
- Hem masaüstü hem de telefonunuzda Chrome'un güncel olduğundan emin olun.
- Hem masaüstü bilgisayarınızda hem de telefonunuzda, Chrome'u açın ve bu atölyede kullanmak istediğiniz profille oturum açın.
- Masaüstü ve telefonunuzda bu profil için Senkronizasyonu etkinleştirin. Bunun için chrome://settings/syncSetup'ı kullanın.
- Hem masaüstü hem de telefonunuzda Bluetooth'u açın.
- Aynı profille giriş yapılan Chrome masaüstünde webauthn.io sayfasını açın.
- Basit bir kullanıcı adı girin. Onay türü ve Kimlik doğrulayıcı türü değerlerini Yok ve Belirtilmedi (varsayılan) değerlerine bırakın. Kaydol'u tıklayın.
- Kimliğinizi doğrulamanızı isteyen bir tarayıcı penceresi açılır. Listeden telefonunuzu seçin.
- Telefonunuzda Kimliğinizi doğrulayın başlıklı bir bildirim alırsınız. Uygulamaya dokunun.
- Telefonunuzda telefonunuzun PIN kodunu girmeniz (veya parmak izi sensörüne dokunmanız) istenir. Bunu girin.
- Masaüstünüzdeki webauthn.io'da "Başarılı" göstergesi görünür.
- Masaüstünüzde webauthn.io uygulamasında Giriş düğmesini tıklayın.
- Yine, bir tarayıcı penceresi açılmalıdır; listeden telefonunuzu seçin.
- Telefonunuzda açılan bildirime dokunun ve PIN'inizi girin (veya parmak izi sensörüne dokunun).
- webauthn.io, giriş yaptığınızı size bildirmelidir. Telefonunuz güvenlik anahtarı olarak düzgün çalışıyor. Artık atölyeye hazırsınız!
Kimlik doğrulayıcı olarak bir USB güvenlik anahtarı kullanıyorsanız
- Chrome masaüstünde webauthn.io sayfasını açın.
- Basit bir kullanıcı adı girin. Onay türü ve Kimlik doğrulayıcı türü değerlerini Yok ve Belirtilmedi (varsayılan) değerlerine bırakın. Kaydol'u tıklayın.
- Kimliğinizi doğrulamanızı isteyen bir tarayıcı penceresi açılır. Listeden USB güvenlik anahtarı'nı seçin.
- Güvenlik anahtarınızı masaüstünüze yerleştirip anahtara dokunun.
- Masaüstünüzdeki webauthn.io'da "Başarılı" göstergesi görünür.
- Masaüstünüzde webauthn.io uygulamasında Giriş yap düğmesini tıklayın.
- Yine, bir tarayıcı penceresi açılır. Listede USB güvenlik anahtarını seçin.
- Tuşa dokunun.
- Webauthn.io, giriş yaptığınızı size bildirmelidir. USB güvenlik anahtarınız düzgün çalışıyor. Atölyeye hazırsınız!
5. Hazırlanın
Bu codelab'de, kodunuzu otomatik ve anında dağıtacak online kod düzenleyici olan Glitch'i kullanacaksınız.
Başlangıç kodunu çatallayın
Başlangıç projesini açın.
Remiks düğmesini tıklayın.
Bu işlem, başlangıç kodunun bir kopyasını oluşturur. Artık düzenlemek için kendi kodunuz var. Bu lab'la ilgili tüm işleri çatalınız ("Glitch'te remiks" olarak adlandırılır) ile yaparsınız.
Başlangıç kodunu keşfedin
Kısa bir süre önce aldığınız başlangıç kodunu keşfedin.
libs
altında auth.js
adlı bir kitaplığın zaten mevcut olduğunu unutmayın. Sunucu tarafı kimlik doğrulama mantığını işleyen özel bir kitaplıktır. Bağımlılık olarak fido
kitaplığını kullanır.
6. Kimlik bilgisi kaydını uygulama
Kimlik bilgisi kaydını uygulama
Güvenlik anahtarıyla iki faktörlü kimlik doğrulamayı ayarlamak için gereken ilk şey, kullanıcının kimlik bilgisi oluşturmasını sağlamaktır.
Önce istemci tarafı kodumuzda bunu yapan bir işlev ekleyelim.
public/auth.client.js
işlevinde, henüz registerCredential()
hiçbir şey yapmayan bir işlev olduğunu unutmayın. Aşağıdaki kodu ekleyin:
async function registerCredential() {
// Fetch the credential creation options from the backend
const credentialCreationOptionsFromServer = await _fetch(
"/auth/credential-options",
"POST"
);
// Decode the credential creation options
const credentialCreationOptions = decodeServerOptions(
credentialCreationOptionsFromServer
);
// Create a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
const credential = await navigator.credentials.create({
publicKey: {
...credentialCreationOptions,
}
});
// Encode the newly created credential to send it to the backend
const encodedCredential = encodeCredential(credential);
// Send the encoded credential to the backend for storage
return await _fetch("/auth/credential", "POST", encodedCredential);
}
Bu işlevin zaten sizin için dışa aktarıldığını unutmayın.
registerCredential
şunları yapar:
- Sunucudan kimlik bilgisi oluşturma seçeneklerini getirir (
/auth/credential-options
) - Sunucu seçenekleri kodlanmış olduğundan, bunların kodunu çözmek için
decodeServerOptions
yardımcı işlevini kullanır. - Web API'sini (
navigator.credential.create
) çağırarak kimlik bilgisi oluşturur.navigator.credential.create
çağrıldığında, tarayıcı devreye girer ve kullanıcıdan bir güvenlik anahtarı seçmesini ister. - Yeni oluşturulan kimlik bilgilerinin kodunu çözer
- Kodlanmış kimlik bilgisini içeren
/auth/credential
için bir istekte bulunarak yeni kimlik bilgisini sunucu tarafında kaydeder.
Ayrıca, sunucu koduna göz atabilirsiniz.
registerCredential()
, sunucuya iki çağrı yapıyor. Bu nedenle, arka uçta neler olduğunu kontrol edelim.
Kimlik bilgisi oluşturma seçenekleri
İstemci (/auth/credential-options
) için bir istek yaptığında, sunucu bir seçenekler nesnesi oluşturur ve bunu istemciye geri gönderir.
Bu nesne daha sonra istemci tarafından gerçek kimlik bilgisi oluşturma çağrısında kullanılır:
navigator.credentials.create({
publicKey: {
// Options generated server-side
...credentialCreationOptions
// ...
}
Peki ya önceki adımda registerCredential
istemci tarafında kullandığınız credentialCreationOptions
credentialCreationOptions
ne anlama geliyor?
router.post("/credential-options", ...) altındaki sunucu koduna göz atın.
Her bir mülke bakmayalım, ancak fido2
kitaplığı kullanılarak oluşturulan ve sonunda istemciye döndürülen sunucu kodu seçenekleri nesnesinde görebileceğiniz birkaç ilginç özelliği burada görebilirsiniz:
rpName
verpId
, kullanıcıyı kaydeden ve kimlik doğrulaması yapan kuruluşu tanımlar. WebAuthn'ta kimlik bilgilerinin belirli bir alan adı kapsamında olduğunu unutmayın. Bu durum güvenlik avantajıdır; buradakirpName
verpId
, kimlik bilgilerini kapsamak için kullanılır. Geçerli birrpId
, örneğin sitenizin ana makine adıdır. Başlangıç projesini oluştururken ne kadar otomatik olarak güncelleneceğini unutmayın 🧘🏻 ♀️excludeCredentials
bir kimlik bilgileri listesidir; yeni kimlik bilgisi,excludeCredentials
içinde listelenen kimlik bilgilerinden birini de içeren kimlik doğrulayıcı üzerinde oluşturulamaz. Codelab'imizdeexcludeCredentials
, bu kullanıcının mevcut kimlik bilgilerinin bir listesidir. Buuser.id
ve kullanıcı tarafından oluşturulan her kimlik bilgisinin farklı bir kimlik doğrulayıcı (güvenlik anahtarı) üzerinde kullanılmasını sağlıyoruz. Bu, iyi bir uygulamadır. Bir kullanıcı birden fazla kimlik bilgisi kaydettirdiyse farklı kimlik doğrulayıcılar (güvenlik anahtarları) kullanacağı için bir güvenlik anahtarının kaybedilmesi kullanıcının hesabına kilitlenmesine neden olmaz.authenticatorSelection
, web uygulamanızda izin vermek istediğiniz kimlik doğrulayıcıların türünü tanımlar.authenticatorSelection
adlı anlaşmayı daha yakından inceleyelim:residentKey: preferred
bu uygulamanın, istemci tarafında bulunabilir kimlik bilgilerini zorunlu kılmadığı anlamına gelir. İstemci tarafı bulunabilir kimlik bilgisi, kullanıcının tanımlanmasına gerek kalmadan kimlik doğrulamasının yapılmasını sağlayan özel bir kimlik bilgisi türüdür. Bu codelab'in temel uygulamaya odaklandığı içinpreferred
işlevini ayarladık. Bulunabilir kimlik bilgileri daha gelişmiş akışlar içindir.requireResidentKey
yalnızca WebAuthn v1 ile backwards-uyumluluk için kullanılabilir.userVerification: preferred
, kimlik doğrulayıcının kullanıcı doğrulamasını desteklemesi (ör. bir biyometrik güvenlik anahtarı veya yerleşik PIN özelliği olan bir anahtar olması durumunda) bağımlı tarafın, kimlik bilgilerini oluştururken bu anahtarı isteyeceği anlamına gelir. Kimlik doğrulayıcı (temel güvenlik anahtarı) değilse sunucu, kullanıcı doğrulaması istemez.
pubKeyCredParam
, tercih sırasına göre kimlik bilgisinin istenen şifreleme özelliklerini açıklar.
Tüm bu seçenekler web uygulamasının güvenlik modeli için alması gereken kararlardır. Sunucuda bu seçeneklerin tek bir authSettings
nesnesinde tanımlandığını unutmayın.
Meydan okuma
Daha ilgi çekici bir nokta da req.session.challenge = options.challenge;
.
WebAuthn bir kriptografik protokol olduğundan tekrar oynama saldırılarını önlemek için rastgele hale getirilmiş görevlere bağlıdır. Bir saldırgan, kimlik doğrulamayı tekrar etkinleştirecek özel anahtarın sahibi olmadığında, kimlik doğrulama işlemini tekrar yapmak için bir yükü oynattığında.
Bu sorunu en aza indirmek için sunucuda bir sorgulama oluşturulur ve işlem anında imzalanır. Ardından imza, beklenen bilgilerle karşılaştırılır. Bu kullanıcının kimlik bilgisi oluşturulurken özel anahtarı tuttuğunu doğrular.
Kimlik bilgisi kayıt kodu
router.post("/credential" ...) altındaki sunucu koduna göz atın.
Kimlik bilgisi burada sunucu tarafında kaydedilir.
Peki, neler oluyor?
Bu koddaki en önemli bitlerden biri, fido2.verifyAttestationResponse
aracılığıyla yapılan doğrulama çağrısıdır:
- İmzalanmış meydan okuma kontrol ediliyor. Bu kimlik bilgisi, kimlik bilgilerinin oluşturma zamanında özel anahtarı tutuklayan biri tarafından oluşturulmasını sağlar.
- Kaynağı olan bağlı tarafın kimliği de doğrulanır. Bu, kimlik bilgilerinin bu web uygulamasına (ve yalnızca bu web uygulamasına) bağlı olmasını sağlar.
Bu işlevi kullanıcı arayüzüne ekleyin
Kimlik bilgisi oluşturma işleviniz artık hazır olduğuna göre, "`registerCredential(),
kullanıcının kullanımına hazır hale getirelim.
Kimlik doğrulama yönetimi için normal bir konum olduğundan bu işlemi Hesap sayfasından yapabilirsiniz.
account.html
' işaretlemesinde, kullanıcı adının altında class="flex-h-between"
düzen sınıfına sahip çok boş bir div
var. Bu div
, 2FA işleviyle ilgili kullanıcı arayüzü öğeleri için kullanılacaktır.
Bu div'den iOS'e ekleyin:
- "İki faktörlü kimlik doğrulama" yazılı bir başlık
- Kimlik bilgisi oluşturma düğmesi
<div class="flex-h-between">
<h3>
Two-factor authentication
</h3>
<button class="create" id="registerButton" raised>
➕ Add a credential
</button>
</div>
Bu div'in altına, daha sonra ihtiyaç duyacağımız bir kimlik bilgisi div öğesi ekleyin:
<div class="flex-h-between">
(HTML you've just added)
</div>
<div id="credentials"></div>
account.html
satır içi komut dosyasında, yeni oluşturduğunuz işlevi ve register
ifadesini çağıran bir işlevi içe aktarıp, oluşturduğunuz düğmeye eklenmiş bir etkinlik işleyici ekleyin.
// Set up the handler for the button that registers credentials
const registerButton = document.querySelector('#registerButton');
registerButton.addEventListener('click', register);
// Register a credential
async function register() {
let user = {};
try {
const user = await registerCredential();
} catch (e) {
// Alert the user that something went wrong
if (Array.isArray(e)) {
alert(
// `msg` not `message`, this is the key's name as per the express validator API
`Registration failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
);
} else {
alert(`Registration failed. ${e}`);
}
}
}
Kullanıcının göreceği kimlik bilgilerini göster
Kimlik bilgisi oluşturma işlevini eklediğinize göre, kullanıcıların ekledikleri kimlik bilgilerini görmenin bir yolunu bulmaları gerekir.
Hesap sayfası bunun için iyi bir yerdir.
account.html
içinde updateCredentialList()
adlı işlevi bulun.
Giriş yapmış olan kullanıcının tüm kayıtlı kimlik bilgilerini almak için arka uç çağrısı yapan ve döndürülen kimlik bilgilerini gösteren aşağıdaki kodu ekleyin:
// Update the list that displays credentials
async function updateCredentialList() {
// Fetch the latest credential list from the backend
const response = await _fetch('/auth/credentials', 'GET');
const credentials = response.credentials || [];
// Generate the credential list as HTML and pass remove/rename functions as args
const credentialListHtml = getCredentialListHtml(
credentials,
removeEl,
renameEl
);
// Display the list of credentials in the DOM
const list = document.querySelector('#credentials');
render(credentialListHtml, list);
}
Şu an için removeEl
ve renameEl
politikalarını unutmayın. Bu kod laboratuvarının ilerleyen bölümlerinde bunlar hakkında bilgi edineceksiniz.
account.html
içinde satır içi komut dosyanızın başına updateCredentialList
çağrısı ekleyin. Bu çağrıda, kullanıcı hesap sayfasına ulaştığında kullanılabilir kimlik bilgileri getirilir.
<script type="module">
// ... (imports)
// Initialize the credential list by updating it once on page load
updateCredentialList();
Şimdi, updateCredentialList
kodunu çağırarak registerCredential
işlemini başarıyla tamamlayın. Böylece listeler, yeni oluşturulan kimlik bilgisini görüntüler:
async function register() {
let user = {};
try {
// ...
} catch (e) {
// ...
}
// Refresh the credential list to display the new credential
await updateCredentialList();
}
Bu özelliği kullanmayı deneyin. 👩🏻 💻
Kimlik bilgisi kaydını tamamladınız. Kullanıcılar artık güvenlik anahtarı tabanlı kimlik bilgileri oluşturabilir ve bunları Hesap sayfalarında görselleştirebilir.
Şunları deneyin:
- Oturumu kapat'ı tıklayın.
- Herhangi bir kullanıcı ve şifre ile giriş yapın. Daha önce de belirtildiği gibi, bu kod laboratuvarında işlemleri basit hale getirmek için şifrenin doğru olup olmadığı kontrol edilmez. Boş olmayan şifreleri girin.
- Account (Hesap) sayfasına geldiğinizde Kimlik bilgisi ekle'yi tıklayın.
- Bir güvenlik anahtarı takmanız ve bu anahtara dokunmanız istenir. Bu işlemi mutlaka yapın.
- Kimlik bilgisi başarıyla oluşturulduktan sonra, kimlik bilgisi hesap sayfasında gösterilmelidir.
- Hesap sayfasını yeniden yükleyin. Kimlik bilgileri görüntülenir.
- İki anahtarınız varsa kimlik bilgisi olarak iki farklı güvenlik anahtarı eklemeyi deneyin. Her ikisi de gösterilmelidir.
- Aynı kimlik doğrulayıcı (anahtar) ile iki kimlik bilgisi oluşturmayı deneyin. Desteklenmediğini fark edeceksiniz. Bu işlem bilerek yapılmıştır. Bunun nedeni, arka uçta
excludeCredentials
kullanmamızdır.
7. İkinci faktörlü kimlik doğrulamayı etkinleştir
Kullanıcılarınız kimlik bilgilerini kaydedip iptal edebilir, ancak kimlik bilgileri yalnızca gösterilir ve henüz kullanılmaz.
Şimdi bunları kullanma ve iki faktörlü kimlik doğrulama ayarlarını yapma zamanı.
Bu bölümde, web uygulamanızdaki kimlik doğrulama akışını şu temel akıştan değiştireceksiniz:
Bu iki faktörlü akışa:
İkinci etmen kimlik doğrulamasını uygulama
Öncelikle ihtiyacımız olan işlevselliği ekleyelim ve arka uçla iletişim uygulayalım. Sonraki adımda bunu ön uçta ekleyeceğiz.
Burada uygulamanız gereken, kimlik bilgisi ile kullanıcının kimliğini doğrulayan bir işlevdir.
public/auth.client.js
içinde boş işlevi (authenticateTwoFactor
) bulup ona aşağıdaki kodu ekleyin:
async function authenticateTwoFactor() {
// Fetch the 2F options from the backend
const optionsFromServer = await _fetch("/auth/two-factor-options", "POST");
// Decode them
const decodedOptions = decodeServerOptions(optionsFromServer);
// Get a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
const credential = await navigator.credentials.get({
publicKey: decodedOptions
});
// Encode the credential
const encodedCredential = encodeCredential(credential);
// Send it to the backend for verification
return await _fetch("/auth/authenticate-two-factor", "POST", {
credential: encodedCredential
});
}
Bu işlevin zaten sizin için dışa aktarıldığını unutmayın. Bu işleve sonraki adımda ihtiyaç duyacağız.
authenticateTwoFactor
şunları yapar:
- Sunucudan iki faktörlü kimlik doğrulama seçenekleri ister. Daha önce gördüğünüz kimlik bilgisi oluşturma seçenekleri gibi bunlar da sunucuda tanımlanır ve web uygulamasının güvenlik modeline bağlıdır. Ayrıntılar için
router.post("/two-factors-options", ...
altındaki sunucu kodunu inceleyin. navigator.credentials.get
ifadesini çağırırken tarayıcının devralmasına izin verir ve kullanıcıdan daha önce kaydedilmiş bir anahtarı ekleyip dokunmasını ister. Bu, söz konusu ikinci faktör kimlik doğrulaması işlemi için kimlik bilgilerinin seçilmesine neden olur.- Daha sonra, seçilen kimlik bilgisi getirmeleri için arka uç isteğinde iletilir(&; kimlik doğrulama/kimlik doğrulama-iki faktörlü&#).; Kimlik bilgisi bu kullanıcı için geçerliyse kullanıcının kimliği doğrulanır.
Ayrıca, sunucu koduna göz atabilirsiniz.
server.js
uygulaması zaten gezinme ve erişimle ilgilenir: Hesap sayfasına yalnızca kimliği doğrulanmış kullanıcılar tarafından erişilebilmesini sağlar ve bazı gerekli yönlendirmeleri gerçekleştirir.
Şimdi router.post("/initialize-authentication", ...
altındaki sunucu koduna göz atın.
Dikkat edilmesi gereken iki ilginç nokta vardır:
- Bu aşamada hem şifre hem kimlik bilgisi aynı anda kontrol edilir. Bu bir güvenlik önlemidir: İki faktörlü kimlik doğrulama özelliğini ayarlanan kullanıcılar için, şifrenin doğru olup olmamasına bağlı olarak kullanıcı arayüzü akışlarının farklı görünmesini istemeyiz. Bu nedenle, bu adımda hem şifreyi hem de kimlik bilgilerini kontrol ederiz.
- Hem şifre hem de kimlik bilgisi geçerliyse
completeAuthentication(req, res);
hizmetini çağırarak kimlik doğrulama işlemini tamamlarız. Pratikte bu, oturumları henüz kullanıcının kimliği doğrulanmamış geçici birauth
oturumundan kullanıcının kimliğinin doğrulandığı ana oturumamain
geçirdiğimiz anlamına gelir.
İkinci faktör kimlik doğrulama sayfasını kullanıcı akışına dahil et
views
klasöründe, second-factor.html
adlı yeni sayfaya dikkat edin.
Güvenlik anahtarı kullan yazılı bir düğme vardır, ancak şu an için hiçbir şey yapmamaktadır.
Bu düğmeyi tıkladığınızda authenticateTwoFactor()
düğmesini çağırın.
authenticateTwoFactor()
başarılı olursa kullanıcıyı Hesap sayfasına yönlendirin.- İşlem başarısız olursa kullanıcıyı bir hata oluştuğu konusunda uyarın. Gerçek bir uygulamada, daha faydalı hata mesajları uygularsınız. Bu demoda kolaylık sağlamak amacıyla yalnızca pencere uyarısı kullanacağız.
<main>
...
</main>
<script type="module">
import { authenticateTwoFactor, authStatuses } from "/auth.client.js";
const button = document.querySelector("#authenticateButton");
button.addEventListener("click", async e => {
try {
// Ask the user to authenticate with the second factor; this will trigger a browser prompt
const response = await authenticateTwoFactor();
const { authStatus } = response;
if (authStatus === authStatuses.COMPLETE) {
// The user is properly authenticated => Navigate to the Account page
location.href = "/account";
} else {
throw new Error("Two-factor authentication failed");
}
} catch (e) {
// Alert the user that something went wrong
alert(`Two-factor authentication failed. ${e}`);
}
});
</script>
</body>
</html>
İkinci faktörlü kimlik doğrulamayı kullanın
Artık iki faktörlü kimlik doğrulama adımı eklemeye hazırsınız.
Şimdi, iki faktörlü kimlik doğrulamayı yapılandırmış olan kullanıcılar için bu adımı index.html
adlı iş ortağından eklemeniz gerekir.
index.html
içinde, location.href = "/account";
altında, 2FA'yı ayarlamışsa kullanıcıyı koşullu olarak ikinci faktör kimlik doğrulama sayfasına yönlendiren bir kod ekleyin.
Bu codelab'de kimlik bilgisi oluşturulduğunda kullanıcı otomatik olarak iki faktörlü kimlik doğrulamayı etkinleştirir.
server.js
ürününün sunucu tarafı oturum denetimi de uyguladığını unutmayın. Bu sayede, yalnızca kimliği doğrulanmış kullanıcılar account.html
ürününe erişebilir.
const { authStatus } = response;
if (authStatus === authStatuses.COMPLETE) {
// The user is properly authenticated => navigate to account
location.href = '/account';
} else if (authStatus === authStatuses.NEED_SECOND_FACTOR) {
// Navigate to the two-factor-auth page because two-factor-auth is set up for this user
location.href = '/second-factor';
}
Bu özelliği kullanmayı deneyin. 👩🏻 💻
- Yeni bir kullanıcı olan burakbilgili ile giriş yapın.
- Çıkış yap.
- Hesabınıza johndoe olarak giriş yapın ve yalnızca şifre girmeniz gerektiğini doğrulayın.
- Kimlik bilgisi oluşturun. Bu, johndoe olarak iki faktörlü kimlik doğrulamayı etkinleştirdiğiniz anlamına gelir.
- Çıkış yap.
- johndoe kullanıcı adınızı ve şifrenizi girin.
- İkinci faktör kimlik doğrulaması sayfasına otomatik olarak nasıl gittiğinizi öğrenin.
- (
/account
adresinde Hesap sayfasına erişmeyi deneyin. Tam olarak kimlik doğrulaması yapmadığınız için dizin sayfasına nasıl yönlendirildiğinizi not edin: İkinci bir faktör eksik) - İkinci faktörlü kimlik doğrulama sayfasına dönün ve ikinci faktör kimlik doğrulaması için Güvenlik anahtarını kullan'ı tıklayın.
- Artık giriş yapmış durumdasınız ve Hesap sayfanızı görmeniz gerekir.
8. Kimlik bilgilerinin kullanımını kolaylaştırma
Güvenlik anahtarıyla iki faktörlü kimlik doğrulamayla temel işlevleri tamamladınız 📲
Ama... Fark ettiniz mi?
Kimlik bilgisi listemiz şu anda çok kullanışlı değil: Kimlik bilgisi kimliği ve ortak anahtar, kimlik bilgilerini yönetirken yararlı olmayan uzun dizelerdir! İnsanlar uzun dizeler ve rakamlarla çok iyi değil 🤖
Bu nedenle, bunu geliştirelim ve kimlik bilgilerini, insanların okuyabileceği dizelerle adlandırıp yeniden adlandıralım.
Yeniden adlandırma kimlik bilgilerine göz atın
Çok fazla çığır açan bir işlem yapmayarak bu işlevi uygularken size zaman kazandırmak amacıyla başlangıç kodundaki auth.client.js
adresinde, kimlik bilgilerini yeniden adlandırmak için bir işlev eklendi:
async function renameCredential(credId, newName) {
const params = new URLSearchParams({
credId,
name: newName
});
return _fetch(
`/auth/credential?${params}`,
"PUT"
);
}
Bu, normal bir veritabanı güncelleme çağrısıdır: İstemci, arka uça bir kimlik bilgisi ve bu kimlik bilgisi için yeni bir adla bir PUT
isteği gönderir.
Özel kimlik bilgisi adlarını uygulayın
account.html
içinde boş rename
işlevine dikkat edin.
Aşağıdaki kodu ekleyin:
// Rename a credential
async function rename(credentialId) {
// Let the user input a new name
const newName = window.prompt(`Name this credential:`);
// Rename only if the user didn't cancel AND didn't enter an empty name
if (newName && newName.trim()) {
try {
// Make the backend call to rename the credential (the name is sanitized) server-side
await renameCredential(credentialId, newName);
} catch (e) {
// Alert the user that something went wrong
if (Array.isArray(e)) {
alert(
// `msg` not `message`, this is the key's name as per the express validator API
`Renaming failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
);
} else {
alert(`Renaming failed. ${e}`);
}
}
// Refresh the credential list to display the new name
await updateCredentialList();
}
}
Kimlik bilgilerini yalnızca kimlik bilgisi başarıyla oluşturulduktan sonra adlandırmanız daha mantıklı olabilir. Bu yüzden, ad olmadan bir kimlik bilgisi oluşturalım ve başarıyla oluşturduktan sonra kimlik bilgisini yeniden adlandıralım. Ancak bu durumda iki arka uç araması elde edilir.
Kullanıcıların kaydolduktan sonra kimlik bilgilerini adlandırmasını sağlamak için register()
içinde rename
işlevini kullanın:
async function register() {
let user = {};
try {
const user = await registerCredential();
// Get the latest credential's ID (newly created credential)
const allUserCredentials = user.credentials;
const newCredential = allUserCredentials[allUserCredentials.length - 1];
// Rename it
await rename(newCredential.credId);
} catch (e) {
// ...
}
// Refresh the credential list to display the new credential
await updateCredentialList();
}
Kullanıcı girişinin arka uçta doğrulanıp temizleneceğini unutmayın:
check("name")
.trim()
.escape()
Görünen kimlik bilgisi adları
templates.js
içinde getCredentialHtml
adlı işletmeye gidin.
Kimlik bilgisi kartının üst kısmında kimlik bilgisi adını görüntülemek için zaten kod bulunduğunu unutmayın:
// Register credential
const getCredentialHtml = (credential, removeEl, renameEl) => {
const { name, credId, publicKey } = credential;
return html`
<div class="credential-card">
<div class="credential-name">
${name
? html`
${name}
`
: html`
<span class="unnamed">(Unnamed)</span>
`}
</div>
// ...
</div>
`;
};
Bu özelliği kullanmayı deneyin. 👩🏻 💻
- Kimlik bilgisi oluşturun.
- Adlandırmanız istenir.
- Yeni bir ad girip Tamam'ı tıklayın.
- Kimlik bilgisi artık yeniden adlandırıldı.
- İşlemi tekrarlayın ve ad alanını boş bırakıp her şeyin düzgün çalışıp çalışmadığını kontrol edin.
Kimlik bilgisi yeniden adlandırmayı etkinleştir
Kullanıcıların kimlik bilgilerini yeniden adlandırması gerekebilir. Örneğin, ikinci bir anahtar ekliyorlar ve ayırt edilebilmeleri için ilk anahtarlarını yeniden adlandırmak istiyorlar.
account.html
içinde, boş bir renameEl
işlevini arayın ve ona aşağıdaki kodu ekleyin:
// Rename a credential via HTML element
async function renameEl(el) {
// Define the ID of the credential to update
const credentialId = el.srcElement.dataset.credentialId;
// Rename the credential
await rename(credentialId);
// Refresh the credential list to display the new name
await updateCredentialList();
}
Şimdi templates.js
getCredentialHtml
içinde class="flex-end"
div bölümüne şu kodu ekleyin: Bu kod, kimlik bilgisi kartı şablonuna bir Yeniden adlandır düğmesi ekler. Bu düğme tıklandığında, az önce oluşturduğumuz renameEl
işlevi çağrılır:
const getCredentialHtml = (credential, removeEl, renameEl) => {
// ...
<div class="flex-end">
<button
data-credential-id="${credId}"
@click="${renameEl}"
class="secondary right"
>
Rename
</button>
</div>
// ...
`;
};
Bu özelliği kullanmayı deneyin. 👩🏻 💻
- Yeniden adlandır'ı tıklayın.
- İstendiğinde yeni bir ad girin.
- Tamam'ı tıklayın.
- Kimlik bilgisi başarıyla yeniden adlandırılmalıdır ve liste otomatik olarak güncellenmelidir.
- Sayfa yeniden yüklendiğinde yeni ad yine gösterilir (yeni adın sunucu tarafında kalıcı olduğu gösterilir).
Kimlik bilgisi oluşturma tarihini gösterme
navigator.credential.create()
ile oluşturulan kimlik bilgilerinde oluşturma tarihi yer almıyor.
Ancak bu bilgi, kimlik bilgilerinin ayırt edilmesinde kullanıcı açısından faydalı olacağından, başlangıç kodundaki sunucu tarafı kitaplığında küçük değişiklikler yaptık ve yeni kimlik bilgilerini depoladıktan sonra Date.now()
değerine eşit bir creationDate
alanı ekledik.
Kullanıcıya templates.js
class="creation-date"
div
içinde, oluşturma tarihi bilgilerini göstermek için aşağıdakileri ekleyin:
<div class="creation-date">
<label>Created:</label>
<div class="info">
${new Date(creationDate).toLocaleDateString()}
${new Date(creationDate).toLocaleTimeString()}
</div>
</div>
9. Kodunuzu geleceğe uygun hale getirme
Şimdiye kadar kullanıcıdan yalnızca basit bir dolaşım kimlik doğrulayıcısını kaydettirmesini istedik. Daha sonra oturum açma sırasında ikinci bir faktör olarak kullanılır.
Daha gelişmiş bir yaklaşım, daha güçlü bir kimlik doğrulayıcı türüne güvenmektir: Kullanıcı doğrulaması dolaşım kimlik doğrulayıcısı (UVRA). UVRA, tek adımlı oturum açma akışlarında iki kimlik doğrulama faktörü ve kimlik avına karşı koruma sağlayabilir.
İdeal olarak, her iki yaklaşımı da desteklersiniz. Bunu yapmak için kullanıcı deneyimini özelleştirmeniz gerekir:
- Bir kullanıcının yalnızca basit (kullanıcı doğrulaması olmayan) dolaşım kimlik doğrulayıcısı varsa, kimlik avına dayanıklı hesap önyüklemesi için bu tanımlayıcıyı kullanmasına izin verin ancak bir kullanıcı adı ve şifre de yazması gerekir. Codelab'imiz zaten bunu yapıyor.
- Başka bir kullanıcı, kullanıcı doğrulaması dolaşımı için daha gelişmiş bir kimlik doğrulayıcı sistemine sahipse hesap başlatma sırasında şifre adımını, hatta kullanıcı adı adımını atlayabilir.
İsteğe Bağlı Şifresiz Oturum Açma ile Kimlik Avına Dayanıklı Hesap Önyüklemesi hakkında daha fazla bilgi edinin.
Bu codelab'de, kullanıcı deneyimini gerçekleştirmeyeceğiz, ancak kod tabanınızı, kullanıcı deneyimini özelleştirmek için ihtiyacınız olan verilere sahip olacak şekilde ayarlayacağız.
Bunun için iki şey gereklidir:
- Arka uç ayarlarınızda
residentKey: preferred
belirleyin. Bu işlem sizin için zaten yapılmıştır. - Bulunabilir bir kimlik bilgisinin (yerleşik anahtarı olarak da adlandırılır) oluşturulup oluşturulmadığını öğrenmek için bir yöntem oluşturun.
Bulunabilir bir kimlik bilgilerinin oluşturulup oluşturulmadığını öğrenmek için:
- Kimlik bilgisi oluşturulduktan sonra
credProps
değerini sorgulayın (credProps: true
). - Kimlik bilgisi oluşturulduktan sonra
transports
değerini sorgulayın. Bu sayede, temel platformun UVRA işlevini destekleyip desteklemediğini, örneğin cep telefonu olup olmadığını belirleyebilirsiniz. credProps
vetransports
değerini arka uçta depolayın. Bu işlem, başlangıç kodu için zaten yapılmıştır. Merak ediyorsanızauth.js
ürününe göz atın.
credProps
ve transports
değerini alıp arka uça gönderelim. auth.client.js
bölümünde, registerCredential
ayarlarını şu şekilde değiştirin:
navigator.credentials.create
numaralı telefonu aradığınızda birextensions
alanı ekleyin- Kimlik bilgilerini depolama için arka uça göndermeden önce
encodedCredential.transports
veencodedCredential.credProps
değerlerini ayarlayın.
registerCredential
şöyle görünmelidir:
async function registerCredential() {
// Fetch the credential creation options from the backend
const credentialCreationOptionsFromServer = await _fetch(
'/auth/credential-options',
'POST'
);
// Decode the credential creation options
const credentialCreationOptions = decodeServerOptions(
credentialCreationOptionsFromServer
);
// Create a credential via the browser API; this will prompt the user
const credential = await navigator.credentials.create({
publicKey: {
...credentialCreationOptions,
extensions: {
credProps: true,
},
},
});
// Encode the newly created credential to send it to the backend
const encodedCredential = encodeCredential(credential);
// Set transports and credProps for more advanced user flows
encodedCredential.transports = credential.response.getTransports();
encodedCredential.credProps =
credential.getClientExtensionResults().credProps;
// Send the encoded credential to the backend for storage
return await _fetch('/auth/credential', 'POST', encodedCredential);
}
10. Tarayıcılar arası destek sağlama
Chromium dışı tarayıcıları destekleme
public/auth.client.js
' registerCredential
işlevinde, yeni oluşturulan kimlik bilgisi hakkında credential.response.getTransports()
kodunu çağırarak bu bilgiyi arka uçta sunucuya ipucu olarak kaydederiz.
Ancak, getTransports()
şu anda tüm tarayıcılarda uygulanmamaktadır (tarayıcılar arasında desteklenen getClientExtensionResults
politikasından farklı olarak): getTransports()
çağrısı, Firefox ve Safari'de hataya neden olur. Bu, kimlik bilgilerinin bu tarayıcılarda oluşturulmasını engeller.
Kodunuzun tüm önemli tarayıcılarda çalışması için encodedCredential.transports
çağrısını bir koşul içinde sarmalayın:
if (credential.response.getTransports) {
encodedCredential.transports = credential.response.getTransports();
}
Sunucuda transports
değerinin transports || []
olarak ayarlandığını unutmayın. Firefox ve Safari'de transports
listesi undefined
olmaz ancak boş bir liste []
olur. Bu da hataları engeller.
WebAuthn'u desteklemeyen tarayıcılar kullanan kullanıcıları uyar
WebAuthn tüm önemli tarayıcılarda desteklense de, WebAuthn'u desteklemeyen tarayıcılarda bir uyarı görüntülemek iyi bir fikirdir.
index.html
öğesinde bu div'in varlığını inceleyin:
<div id="warningbanner" class="invisible">
⚠️ Your browser doesn't support WebAuthn. Open this demo in Chrome, Edge, Firefox or Safari.
</div>
index.html
's satır içi komut dosyasında, banner'ı WebAuthn'u desteklemeyen tarayıcılarda görüntülemek için aşağıdaki kodu ekleyin:
// Display a banner in browsers that don't support WebAuthn
if (!window.PublicKeyCredential) {
document.querySelector('#warningbanner').classList.remove('invisible');
}
Gerçek bir web uygulamasında daha ayrıntılı bir işlem yapar ve bu tarayıcılar için uygun bir yedek mekanizmaya sahip olursunuz. Ancak bu, WebAuthn desteğini nasıl kontrol edeceğinizi gösterir.
11. Tebrikler!
✨İşlemi tamamladınız!
Güvenlik anahtarıyla iki faktörlü kimlik doğrulamayı uyguladınız.
Bu codelab'de temel bilgileri ele aldık. 2FA için WebAuthn'u daha fazla keşfetmek istiyorsanız aşağıda bazı denemeler yapabilirsiniz:
- Kimlik bilgisi kartına "Son kullanılan" bilgi ekleyin. Bu, kullanıcıların belirli bir güvenlik anahtarının etkin bir şekilde kullanılıp kullanılmadığını (özellikle de birden fazla anahtar kaydettilerse) belirlemeleri için yararlı bir bilgidir.
- Daha sağlam hata giderme ve daha hassas hata mesajları uygulama.
auth.js
bölümünü inceleyin ve özellikle kullanıcı doğrulamasını destekleyen bir anahtar kullandığınızdaauthSettings
ayarlarından bazılarını değiştirdiğinizde neler olacağını keşfedin.