Web uygulamasında form otomatik doldurma özelliğiyle geçiş anahtarlarını uygulama

1. Başlamadan önce

Şifre yerine geçiş anahtarı kullanmak, web sitelerinin kullanıcı hesaplarını daha güvenli, basit ve kolay kullanılabilir hale getirmesinin harika bir yoludur. Geçiş anahtarı sayesinde kullanıcılar, cihazın ekran kilidi özelliğini (ör. parmak izi, yüz veya cihaz PIN'i) kullanarak web sitesinde ya da uygulamada oturum açabilir. Kullanıcının geçiş anahtarıyla oturum açabilmesi için geçiş anahtarının oluşturulması, bir kullanıcı hesabıyla ilişkilendirilmesi ve herkese açık anahtarının bir sunucuda depolanması gerekir.

Bu codelab'de, temel form tabanlı kullanıcı adı ve şifreyle oturum açma özelliğini, geçiş anahtarlarını destekleyen ve aşağıdakileri içeren bir oturum açma özelliğine dönüştüreceksiniz:

  • Kullanıcı giriş yaptıktan sonra geçiş anahtarı oluşturan bir düğme.
  • Kayıtlı geçiş anahtarlarının listesini gösteren bir kullanıcı arayüzü.
  • Kullanıcıların, form otomatik doldurma özelliğiyle kayıtlı bir geçiş anahtarıyla oturum açmasına olanak tanıyan mevcut oturum açma formu.

Ön koşullar

Neler öğreneceksiniz?

  • Geçiş anahtarı oluşturma
  • Kullanıcıların kimliğini geçiş anahtarıyla doğrulama
  • Formların oturum açma seçeneği olarak geçiş anahtarı önermesine izin verme

İhtiyacınız olanlar

Aşağıdaki cihaz kombinasyonlarından biri:

  • Android 9 veya sonraki sürümlerin yüklü olduğu bir Android cihazda Google Chrome (tercihen biyometrik sensörlü)
  • Windows 10 veya sonraki bir sürümü çalıştıran bir Windows cihazda Chrome
  • iOS 16 veya sonraki sürümlerin yüklü olduğu bir iPhone ya da iPadOS 16 veya sonraki sürümlerin yüklü olduğu bir iPad ile Safari 16 veya sonraki sürümler
  • macOS Ventura veya sonraki bir sürümü çalıştıran bir Apple masaüstü cihazda Safari 16 ya da sonraki bir sürüm veya Chrome.

2. Hazırlanın

Bu codelab'de, istemci ve sunucu tarafı kodunu JavaScript ile düzenlemenize ve yalnızca tarayıcıdan dağıtmanıza olanak tanıyan Glitch adlı bir hizmeti kullanacaksınız.

Projeyi açın.

  1. Projeyi Glitch'te açın.
  2. Glitch projesini çatallamak için Remix'i (Remiks) tıklayın.
  3. Glitch'in alt kısmındaki gezinme menüsünde Önizleme > Yeni pencerede önizleme'yi tıklayın. Tarayıcınızda başka bir sekme açılır.

Glitch'in alt kısmındaki gezinme menüsünde yer alan "Yeni pencerede önizle" düğmesi

Web sitesinin ilk durumunu inceleyin

  1. Önizleme sekmesinde rastgele bir kullanıcı adı girip Sonraki'yi tıklayın.
  2. Rastgele bir şifre girip Oturum aç'ı tıklayın. Şifre yoksayılır ancak kimliğiniz doğrulanır ve ana sayfaya yönlendirilirsiniz.
  3. Görünen adınızı değiştirmek istiyorsanız bunu yapın. İlk durumda yapabileceğiniz tek şey budur.
  4. Oturumu kapat'ı tıklayın.

Bu durumda kullanıcıların her oturum açtıklarında şifre girmesi gerekir. Kullanıcıların cihazın ekran kilidi işlevini kullanarak oturum açabilmesi için bu forma geçiş anahtarı desteği ekleyin. Son durumu https://passkeys-codelab.glitch.me/ adresinde deneyebilirsiniz.

Geçiş anahtarlarının işleyiş şekli hakkında daha fazla bilgi için Geçiş anahtarları nasıl çalışır? başlıklı makaleyi inceleyin.

3. Geçiş anahtarı oluşturma özelliği ekleme

Kullanıcıların geçiş anahtarıyla kimlik doğrulaması yapmasına izin vermek için onlara geçiş anahtarı oluşturma ve kaydetme, ayrıca ortak anahtarını sunucuda saklama olanağı vermeniz gerekir.

Geçiş anahtarı oluşturulduğunda geçiş anahtarı kullanıcı doğrulama iletişim kutusu görünür.

Kullanıcının şifreyle giriş yapmasından sonra geçiş anahtarı oluşturmasına izin vermek ve /home sayfasında kullanıcıların geçiş anahtarı oluşturmasına ve kayıtlı tüm geçiş anahtarlarının listesini görmesine olanak tanıyan bir kullanıcı arayüzü eklemek istiyorsunuz. Bir sonraki bölümde, geçiş anahtarı oluşturup kaydeden bir işlev oluşturacaksınız.

registerCredential() işlevini oluşturma

  1. Glitch'te public/client.js dosyasına gidin ve sonuna kaydırın.
  2. İlgili yorumun ardından şu registerCredential() işlevini ekleyin:

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.  }; 

Bu işlev, sunucuda bir geçiş anahtarı oluşturup kaydeder.

Sunucu uç noktasından sorguyu ve diğer seçenekleri alma

Geçiş anahtarı oluşturulmadan önce, sunucudan WebAuthn'e iletilecek parametreleri (ör. sorgu) istemeniz gerekir. WebAuthn, kullanıcının geçiş anahtarı oluşturmasına ve geçiş anahtarıyla kimliğini doğrulamasına olanak tanıyan bir tarayıcı API'sidir. Neyse ki bu codelab'de bu tür parametrelerle yanıt veren bir sunucu uç noktanız zaten var.

  • Sunucu uç noktasından sorguyu ve diğer seçenekleri almak için ilgili yorumdan sonra registerCredential() işlevinin gövdesine aşağıdaki kodu ekleyin:

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'); 

Aşağıdaki kod snippet'inde, sunucudan aldığınız örnek seçenekler yer almaktadır:

{   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,   } } 

Sunucu ile istemci arasındaki protokol, WebAuthn spesifikasyonunun bir parçası değildir. Ancak bu codelab'in sunucusu, WebAuthn navigator.credentials.create() API'sine iletilen PublicKeyCredentialCreationOptions sözlüğüne mümkün olduğunca benzer bir JSON döndürecek şekilde tasarlanmıştır.

Aşağıdaki tablo kapsamlı olmasa da PublicKeyCredentialCreationOptions sözlüğündeki önemli parametreleri içerir:

Parametreler

Açıklamalar

challenge

Bu kayıt için ArrayBuffer nesnesinde sunucu tarafından oluşturulan bir sorgu. Bu parametre gereklidir ancak onay işlemi yapılmadığı sürece kayıt sırasında kullanılmaz. Onay, bu codelab'de ele alınmayan ileri düzey bir konudur.

user.id

Kullanıcının benzersiz kimliği. Bu değer, e-posta adresleri veya kullanıcı adları gibi kişisel kimlik bilgilerini içermeyen bir ArrayBuffer nesnesi olmalıdır. Hesap başına oluşturulan rastgele bir 16 baytlık değer işe yarar.

user.name

Bu alan, kullanıcının tanıdığı bir hesap için benzersiz bir tanımlayıcı (ör. e-posta adresi veya kullanıcı adı) içermelidir. Hesap seçicide gösterilir. (Kullanıcı adı kullanıyorsanız şifre kimlik doğrulamadaki değerle aynı değeri kullanın.)

user.displayName

Bu alan, hesap için isteğe bağlı olarak girilebilen, kullanıcı dostu bir addır. Benzersiz olması gerekmez ve kullanıcının seçtiği ad olabilir. Web sitenizde buraya eklenecek uygun bir değer yoksa boş bir dize iletin. Bu bilgi, tarayıcıya bağlı olarak hesap seçicide gösterilebilir.

rp.id

Güvenen taraf (RP) kimliği bir alandır. Bir web sitesi, alanını veya kaydedilebilir bir sonek belirtebilir. Örneğin, bir RP'nin kaynağı https://login.example.com:1337 ise RP kimliği login.example.com veya example.com olabilir. RP kimliği example.com olarak belirtilmişse kullanıcı, login.example.com'da veya example.com'un diğer alt alan adlarında kimlik doğrulayabilir.

pubKeyCredParams

Bu alan, RP'nin desteklediği ortak anahtar algoritmalarını belirtir. Bu değeri [{alg: -7, type: "public-key"},{alg: -257, type: "public-key"}] olarak ayarlamanızı öneririz. Bu, P-256 ile ECDSA ve RSA PKCS#1 desteğini belirtir. Bunları desteklemek tam kapsam sağlar.

excludeCredentials

Aynı cihazın iki kez kaydedilmesini önlemek için, daha önce kaydedilmiş kimlik bilgilerinin bir listesini sağlar. Sağlanırsa transports üyesi, her kimliğin kaydı sırasında getTransports() işlevinin çağrılmasının sonucunu içermelidir.

authenticatorSelection.authenticatorAttachment

"platform" değerine ayarlayın. Bu, platform cihazına yerleştirilmiş bir kimlik doğrulayıcı istediğinizi gösterir. Böylece kullanıcıdan USB güvenlik anahtarı gibi bir şey takması istenmez.

authenticatorSelection.requireResidentKey

Boole true değerine ayarlayın. Bulunabilir kimlik bilgisi (yerleşik anahtar), sunucunun kimlik bilgisinin kimliğini sağlaması gerekmeden kullanılabilir ve bu nedenle otomatik doldurma ile uyumludur.

authenticatorSelection.userVerification

"preferred" değerine ayarlayın veya varsayılan değer olduğu için atlayın. Bu, cihazın ekran kilidini kullanan bir kullanıcı doğrulamasının "required", "preferred" veya "discouraged" olup olmadığını gösterir. "preferred" değerine ayarlandığında cihaz uygunsa kullanıcı doğrulaması istenir.

Kimlik bilgisi oluşturma

  1. registerCredential() işlevinin gövdesinde, ilgili yorumdan sonra Base64URL ile kodlanmış bazı parametreleri (özellikle user.id ve challenge dizeleri ile excludeCredentials dizisine dahil edilen id dizesinin örnekleri) tekrar ikiliye dönüştürün:

public/client.js

// TODO: Add an ability to create a passkey: Create a credential. // Base64URL decode some values. 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);   } } 
  1. Sonraki satırda authenticatorSelection.authenticatorAttachment değerini "platform", authenticatorSelection.requireResidentKey değerini ise true olarak ayarlayın. Bu, yalnızca keşfedilebilir kimlik bilgisi özelliğine sahip bir platform kimlik doğrulayıcısının (cihazın kendisi) kullanılmasına olanak tanır.

public/client.js

// Use platform authenticator and discoverable credential. options.authenticatorSelection = {   authenticatorAttachment: 'platform',   requireResidentKey: true } 
  1. Bir sonraki satırda, kimlik bilgisi oluşturmak için navigator.credentials.create() yöntemini çağırın.

public/client.js

// Invoke the WebAuthn create() method. const cred = await navigator.credentials.create({   publicKey: options, }); 

Bu çağrıyla tarayıcı, kullanıcının kimliğini cihazın ekran kilidiyle doğrulamaya çalışır.

Kimlik bilgisini sunucu uç noktasına kaydetme

Kullanıcı kimliğini doğruladıktan sonra bir geçiş anahtarı oluşturulup saklanır. Web sitesi, geçiş anahtarını kaydetmek için sunucuya gönderebileceğiniz bir ortak anahtar içeren kimlik bilgisi nesnesi alır.

Aşağıdaki kod snippet'inde örnek bir kimlik bilgisi nesnesi yer almaktadır:

{   "id": *****,   "rawId": *****,   "type": "public-key",   "response": {     "clientDataJSON": *****,     "attestationObject": *****,     "transports": ["internal", "hybrid"]   },   "authenticatorAttachment": "platform" } 

Aşağıdaki tablo kapsamlı olmasa da PublicKeyCredential nesnesindeki önemli parametreleri içerir:

Parametreler

Açıklamalar

id

Oluşturulan geçiş anahtarının Base64URL kodlu kimliği. Bu kimlik, tarayıcının kimlik doğrulama sırasında cihazda eşleşen bir geçiş anahtarı olup olmadığını belirlemesine yardımcı olur. Bu değer, arka uçtaki veritabanında depolanmalıdır.

rawId

Kimlik bilgisinin kimliğinin ArrayBuffer nesne sürümü.

response.clientDataJSON

İstemci verilerini kodlayan bir ArrayBuffer nesnesi.

response.attestationObject

ArrayBuffer kodlu bir onay nesnesi. RP kimliği, işaretler ve genel anahtar gibi önemli bilgiler içerir.

response.transports

Cihazın desteklediği aktarımların listesi: "internal", cihazın geçiş anahtarı desteklediği anlamına gelir. "hybrid", başka bir cihazda kimlik doğrulama özelliğini de desteklediği anlamına gelir.

authenticatorAttachment

Bu kimlik bilgisi, geçiş anahtarı oluşturmaya uygun bir cihazda oluşturulduğunda "platform" değerini döndürür.

Kimlik bilgisi nesnesini sunucuya göndermek için aşağıdaki adımları uygulayın:

  1. Kimlik bilgisinin ikili parametrelerini Base64URL olarak kodlayın. Böylece, sunucuya dize olarak iletilebilir:

public/client.js

// TODO: Add an ability to create a passkey: Register the credential to the server endpoint. const credential = {}; credential.id = cred.id; credential.rawId = cred.id; // Pass a Base64URL encoded ID string. credential.type = cred.type;  // The authenticatorAttachment string in the PublicKeyCredential object is a new addition in WebAuthn L3. if (cred.authenticatorAttachment) {   credential.authenticatorAttachment = cred.authenticatorAttachment; }  // Base64URL encode some values. const clientDataJSON = base64url.encode(cred.response.clientDataJSON); const attestationObject = base64url.encode(cred.response.attestationObject);  // Obtain transports. const transports = cred.response.getTransports ? cred.response.getTransports() : [];  credential.response = {   clientDataJSON,   attestationObject,   transports }; 
  1. Sonraki satırda, nesneyi sunucuya gönderin:

public/client.js

return await _fetch('/auth/registerResponse', credential); 

Programı çalıştırdığınızda sunucu, kimlik bilgisinin kaydedildiğini belirten HTTP code 200 değerini döndürür.

Artık registerCredential() işlevinin tamamına sahipsiniz.

Bu bölümün çözüm kodunu inceleyin.

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 server endpoint.   const options = await _fetch('/auth/registerRequest');      // TODO: Add an ability to create a passkey: Create a credential.   // Base64URL decode some values.    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);     }   }    // 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.   const credential = {};   credential.id = cred.id;   credential.rawId = cred.id; // Pass a Base64URL encoded ID string.   credential.type = cred.type;    // The authenticatorAttachment string in the PublicKeyCredential object is a new addition in WebAuthn L3.   if (cred.authenticatorAttachment) {     credential.authenticatorAttachment = cred.authenticatorAttachment;   }    // Base64URL encode some values.   const clientDataJSON = base64url.encode(cred.response.clientDataJSON);   const attestationObject =     base64url.encode(cred.response.attestationObject);    // Obtain transports.   const transports = cred.response.getTransports ?    cred.response.getTransports() : [];    credential.response = {     clientDataJSON,     attestationObject,     transports   };    return await _fetch('/auth/registerResponse', credential); }; 

4. Geçiş anahtarı kimlik bilgilerini kaydetmek ve yönetmek için bir kullanıcı arayüzü oluşturun

registerCredential() işlevi kullanıma sunulduğundan, bu işlevi çağırmak için bir düğmeye ihtiyacınız var. Ayrıca, kayıtlı geçiş anahtarlarının listesini de göstermeniz gerekir.

Kayıtlı geçiş anahtarları /home sayfasında listelenir.

Yer tutucu HTML ekleme

  1. Glitch'te views/home.html dosyasına gidin.
  2. İlgili yorumun ardından, geçiş anahtarı kaydetme düğmesini ve geçiş anahtarlarının listesini gösteren bir kullanıcı arayüzü yer tutucusu ekleyin:

views/home.html

​​<!-- TODO: Add an ability to create a passkey: Add placeholder HTML. --> <section>   <h3 class="mdc-typography mdc-typography--headline6"> Your registered    passkeys:</h3>   <div id="list"></div> </section> <p id="message" class="instructions"></p> <mwc-button id="create-passkey" class="hidden" icon="fingerprint" raised>Create a passkey</mwc-button> 

div#list öğesi, listenin yer tutucusudur.

Geçiş anahtarı desteğini kontrol etme

Yalnızca geçiş anahtarlarını destekleyen cihazlara sahip kullanıcılara geçiş anahtarı oluşturma seçeneğini göstermek için öncelikle WebAuthn'nin kullanılabilir olup olmadığını kontrol etmeniz gerekir. Bu durumda, Geçiş anahtarı oluştur düğmesini göstermek için hidden sınıfını kaldırmanız gerekir.

Bir ortamın geçiş anahtarlarını destekleyip desteklemediğini kontrol etmek için aşağıdaki adımları uygulayın:

  1. İlgili yorumdan sonra views/home.html dosyasının sonuna window.PublicKeyCredential, PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable ve PublicKeyCredential.isConditionalMediationAvailable true ise yürütülecek bir koşul yazın.

views/home.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) { 
  1. Koşullu ifadenin gövdesinde, cihazın geçiş anahtarı oluşturup oluşturamayacağını ve ardından geçiş anahtarının form otomatik doldurma işleminde önerilip önerilemeyeceğini kontrol edin.

views/home.html

try {   const results = await Promise.all([      // Is platform authenticator available in this browser?     PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),      // Is conditional UI available in this browser?     PublicKeyCredential.isConditionalMediationAvailable()   ]); 
  1. Tüm koşullar karşılanıyorsa geçiş anahtarı oluşturma düğmesini gösterin. Aksi takdirde uyarı mesajı gösterilir.

views/home.html

    if (results.every(r => r === 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.'; } 

Kayıtlı geçiş anahtarlarını listede görüntüleme

  1. Kayıtlı geçiş anahtarlarını sunucudan getiren ve bunları listede oluşturulan bir renderCredentials() işlevi tanımlayın. Neyse ki, oturum açmış kullanıcı için kayıtlı geçiş anahtarlarını getirmek üzere /auth/getKeys sunucu uç noktasına zaten sahipsiniz.

views/home.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 = html`${res.length > 0 ? html`     <mwc-list>       ${res.map(cred => html`         <mwc-list-item>           <div class="list-item">             <div class="entity-name">               <span>${cred.name || 'Unnamed' }</span>           </div>           <div class="buttons">             <mwc-icon-button data-cred-id="${cred.id}"               data-name="${cred.name || 'Unnamed' }" @click="${rename}"               icon="edit"></mwc-icon-button>             <mwc-icon-button data-cred-id="${cred.id}" @click="${remove}"              icon="delete"></mwc-icon-button>           </div>          </div>       </mwc-list-item>`)}   </mwc-list>` : html`   <mwc-list>     <mwc-list-item>No credentials found.</mwc-list-item>   </mwc-list>`}`;   render(creds, list); }; 
  1. Bir sonraki satırda, kullanıcı /home sayfasına geldiğinde kayıtlı geçiş anahtarlarını başlatma olarak hemen görüntülemek için renderCredentials() işlevini çağırın.

views/home.html

renderCredentials(); 

Geçiş anahtarı oluşturma ve kaydetme

Geçiş anahtarı oluşturup kaydetmek için daha önce uyguladığınız registerCredential() işlevini çağırmanız gerekir.

Geçiş anahtarı oluştur düğmesini tıkladığınızda registerCredential() işlevini tetiklemek için aşağıdaki adımları uygulayın:

  1. Yer tutucu HTML'den sonraki dosyada aşağıdaki import ifadesini bulun:

views/home.html

import {    $,    _fetch,    loading,    updateCredential,    unregisterCredential,  } from '/client.js'; 
  1. import ifadesinin gövdesinin sonuna registerCredential() işlevini ekleyin.

views/home.html

// TODO: Add an ability to create a passkey: Create and register a passkey. import {   $,   _fetch,   loading,   updateCredential,   unregisterCredential,   registerCredential } from '/client.js'; 
  1. Dosyanın sonuna, ilgili yorumdan sonra register() işlevini çağıran bir registerCredential() işlevi ve yükleme kullanıcı arayüzü tanımlayın ve kayıttan sonra renderCredentials() işlevini çağırın. Bu, tarayıcının geçiş anahtarı oluşturduğunu ve bir sorun olduğunda hata mesajı gösterdiğini netleştirir.

views/home.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(); 
  1. register() işlevinin gövdesinde istisnaları yakalayın. navigator.credentials.create() yöntemi, cihazda zaten bir geçiş anahtarı varsa InvalidStateError hatası verir. Bu, excludeCredentials dizisiyle incelenir. Bu durumda kullanıcıya alakalı bir mesaj gösterirsiniz. Kullanıcı kimlik doğrulama iletişim kutusunu iptal ettiğinde de NotAllowedError hatası oluşturur. Bu durumda sessizce yoksayarsınız.

views/home.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);     }   } }; 
  1. register() işlevinden sonraki satırda, Geçiş anahtarı oluştur düğmesi için register() işlevini bir click etkinliğine ekleyin.

views/home.html

createPasskey.addEventListener('click', register); 

Bu bölümün çözüm kodunu inceleyin.

views/home.html

​​<!-- TODO: Add an ability to create a passkey: Add placeholder HTML. --> <section>   <h3 class="mdc-typography mdc-typography--headline6"> Your registered     passkeys:</h3>   <div id="list"></div> </section> <p id="message" class="instructions"></p> <mwc-button id="create-passkey" class="hidden" icon="fingerprint" raised>Create a passkey</mwc-button> 

views/home.html

// TODO: Add an ability to create a passkey: Create and register a passkey. import {    $,    _fetch,    loading,    updateCredential,    unregisterCredential,    registerCredential  } from '/client.js'; 

views/home.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) {   try {     const results = await Promise.all([        // Is platform authenticator available in this browser?       PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),        // Is conditional UI available in this browser?       PublicKeyCredential.isConditionalMediationAvailable()     ]);     if (results.every(r => r === 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`   <mwc-list>     ${res.map(cred => html`       <mwc-list-item>         <div class="list-item">           <div class="entity-name">             <span>${cred.name || 'Unnamed' }</span>           </div>           <div class="buttons">             <mwc-icon-button data-cred-id="${cred.id}" data-name="${cred.name || 'Unnamed' }" @click="${rename}" icon="edit"></mwc-icon-button>             <mwc-icon-button data-cred-id="${cred.id}" @click="${remove}" icon="delete"></mwc-icon-button>           </div>         </div>       </mwc-list-item>`)}   </mwc-list>` : html`   <mwc-list>     <mwc-list-item>No credentials found.</mwc-list-item>   </mwc-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 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);     }   } };  createPasskey.addEventListener('click', register); 

Dene

Şimdiye kadarki tüm adımları uyguladıysanız web sitesinde geçiş anahtarları oluşturma, kaydetme ve görüntüleme özelliğini etkinleştirdiniz.

Bu özelliği denemek için aşağıdaki adımları uygulayın:

  1. Önizleme sekmesinde rastgele bir kullanıcı adı ve şifreyle oturum açın.
  2. Geçiş anahtarı oluştur'u tıklayın.
  3. Kimliğinizi cihazın ekran kilidiyle doğrulayın.
  4. Bir geçiş anahtarının kaydedildiğini ve web sayfasının Kayıtlı geçiş anahtarlarınız bölümünde gösterildiğini onaylayın.

Kayıtlı geçiş anahtarları /home sayfasında listelenir.

Kayıtlı geçiş anahtarlarını yeniden adlandırma ve kaldırma

Kayıtlı geçiş anahtarlarını listede yeniden adlandırabilir veya silebilirsiniz. Nasıl çalıştığını, codelab'de yer alan koddan kontrol edebilirsiniz.

Chrome'da, kayıtlı geçiş anahtarlarını masaüstünde chrome://settings/passkeys adresinden veya Android'de ayarlardaki şifre yöneticisinden kaldırabilirsiniz.

Diğer platformlarda kayıtlı geçiş anahtarlarını yeniden adlandırma ve kaldırma hakkında bilgi edinmek için ilgili platformların destek sayfalarına bakın.

5. Geçiş anahtarıyla kimlik doğrulama özelliği ekleme

Kullanıcılar artık geçiş anahtarı oluşturup kaydedebilir ve web sitenizde güvenli bir şekilde kimlik doğrulamak için kullanabilir. Şimdi web sitenize geçiş anahtarı kimlik doğrulama özelliği eklemeniz gerekiyor.

authenticate() işlevini oluşturma

  • public/client.js dosyasında, ilgili yorumun ardından authenticate() adlı bir işlev oluşturun. Bu işlev, kullanıcıyı önce yerel olarak, ardından sunucuya karşı doğrular:

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.  }; 

Sunucu uç noktasından sorgulama ve diğer seçenekleri alma

Kullanıcıdan kimlik doğrulaması yapmasını istemeden önce, sunucudan WebAuthn'de iletilecek parametreleri (ör. bir sorgu) istemeniz gerekir.

  • İlgili yorumdan sonra authenticate() işlevinin gövdesinde, sunucuya POST isteği göndermek için _fetch() işlevini çağırın:

public/client.js

// 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'); 

Bu codelab'deki sunucu, WebAuthn navigator.credentials.get() API'sine iletilen PublicKeyCredentialRequestOptions sözlüğüne mümkün olduğunca benzer JSON döndürecek şekilde tasarlanmıştır. Aşağıdaki kod snippet'inde, almanız gereken örnek seçenekler yer almaktadır:

{   "challenge": *****,   "rpId": "passkeys-codelab.glitch.me",   "allowCredentials": [] } 

Aşağıdaki tablo kapsamlı olmasa da PublicKeyCredentialRequestOptions sözlüğündeki önemli parametreleri içerir:

Parametreler

Açıklamalar

challenge

ArrayBuffer nesnesinde sunucu tarafından oluşturulan bir sorgu. Bu, tekrarlama saldırılarını önlemek için gereklidir. Bir yanıtta aynı meydan okumayı asla iki kez kabul etmeyin. Bunu CSRF jetonu olarak değerlendirin.

rpId

RP kimliği bir alandır. Bir web sitesi, alanını veya kaydedilebilir bir sonek belirtebilir. Bu değer, geçiş anahtarı oluşturulurken kullanılan rp.id parametresiyle eşleşmelidir.

allowCredentials

Bu özellik, bu kimlik doğrulama için uygun kimlik doğrulayıcıları bulmak amacıyla kullanılır. Tarayıcının hesap seçici göstermesine izin vermek için boş bir dizi iletin veya belirtmeyin.

userVerification

"preferred" değerine ayarlayın veya varsayılan değer olduğu için atlayın. Bu, cihazın ekran kilidi kullanılarak yapılan kullanıcı doğrulamasının "required", "preferred" veya "discouraged" olup olmadığını gösterir. "preferred" değerine ayarlandığında cihaz uygunsa kullanıcı doğrulaması istenir.

Kullanıcıyı yerel olarak doğrulama ve kimlik bilgisi alma

  1. authenticate() işlevinin gövdesinde, ilgili yorumdan sonra challenge parametresini tekrar ikiliye dönüştürün:

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); 
  1. Kullanıcı kimlik doğrulaması yaptığında hesap seçiciyi açmak için allowCredentials parametresine boş bir dizi iletin:

public/client.js

// An empty allowCredentials array invokes an account selector by discoverable credentials. options.allowCredentials = []; 

Hesap seçici, kullanıcının geçiş anahtarıyla saklanan bilgilerini kullanır.

  1. navigator.credentials.get() yöntemini mediation: 'conditional' seçeneğiyle birlikte çağırın:

public/client.js

// Invoke the WebAuthn get() method. const cred = await navigator.credentials.get({   publicKey: options,    // Request a conditional UI.   mediation: 'conditional' }); 

Bu seçenek, tarayıcıya form otomatik doldurma işleminin bir parçası olarak koşullu geçiş anahtarı önermesi talimatını verir.

Kimlik bilgisini doğrulama

Kullanıcı kimliğini yerel olarak doğruladıktan sonra, sunucuda doğrulayabileceğiniz bir imza içeren bir kimlik bilgisi nesnesi alırsınız.

Aşağıdaki kod snippet'inde bir örnek PublicKeyCredential nesnesi yer almaktadır:

{   "id": *****,   "rawId": *****,   "type": "public-key",   "response": {     "clientDataJSON": *****,     "authenticatorData": *****,     "signature": *****,     "userHandle": *****   },   authenticatorAttachment: "platform" } 

Aşağıdaki tablo kapsamlı olmasa da PublicKeyCredential nesnesindeki önemli parametreleri içerir:

Parametreler

Açıklamalar

id

Kimliği doğrulanmış geçiş anahtarı kimliğinin Base64URL kodlu sürümü.

rawId

Kimlik bilgisinin kimliğinin ArrayBuffer nesne sürümü.

response.clientDataJSON

İstemci verilerinin ArrayBuffer nesnesi. Bu alanda, RP sunucusunun doğrulaması gereken sorgulama ve kaynak gibi bilgiler yer alır.

response.authenticatorData

Kimlik doğrulayıcı verilerinin ArrayBuffer nesnesi. Bu alanda RP kimliği gibi bilgiler yer alır.

response.signature

İmzanın ArrayBuffer nesnesi. Bu değer, kimlik bilgisinin temelini oluşturur ve sunucuda doğrulanmalıdır.

response.userHandle

Oluşturma sırasında ayarlanan kullanıcı kimliğini içeren bir ArrayBuffer nesnesi. Sunucunun kullandığı kimlik değerlerini seçmesi gerekiyorsa veya arka uç, kimlik bilgilerinin kimlikleri üzerinde bir dizin oluşturulmasını istemiyorsa bu değer, kimlik bilgisi kimliği yerine kullanılabilir.

authenticatorAttachment

Bu kimlik bilgisi yerel cihazdan geldiğinde "platform" dizesini döndürür. Aksi takdirde, özellikle kullanıcı oturum açmak için telefon kullandığında "cross-platform" dizesini döndürür. Kullanıcının oturum açmak için telefon kullanması gerekiyorsa yerel cihazda geçiş anahtarı oluşturmasını isteyin.

Kimlik bilgisi nesnesini sunucuya göndermek için aşağıdaki adımları uygulayın:

  1. authenticate() işlevinin gövdesinde, ilgili yorumdan sonra kimlik bilgisinin ikili parametrelerini, sunucuya dize olarak iletilebilmesi için kodlayın:

public/client.js

// 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, }; 
  1. Nesneyi sunucuya gönderin:

public/client.js

return await _fetch(`/auth/signinResponse`, credential); 

Programı çalıştırdığınızda sunucu, kimlik bilgisinin doğrulandığını belirten HTTP code 200 değerini döndürür.

Artık authentication() işlevinin tüm özelliklerini kullanabilirsiniz.

Bu bölümün çözüm kodunu inceleyin.

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); }; 

6. Tarayıcıda otomatik doldurma özelliğine geçiş anahtarları ekleme

Kullanıcı geri döndüğünde mümkün olduğunca kolay ve güvenli bir şekilde oturum açmasını istersiniz. Giriş sayfasına Geçiş anahtarıyla oturum aç düğmesi eklerseniz kullanıcı bu düğmeye basabilir, tarayıcının hesap seçicisinde bir geçiş anahtarı seçebilir ve kimliğini doğrulamak için ekran kilidini kullanabilir.

Ancak şifreden geçiş anahtarına geçiş, tüm kullanıcılar için aynı anda gerçekleşmez. Bu nedenle, tüm kullanıcılar geçiş anahtarlarına geçene kadar şifreleri kaldıramazsınız. Bu süre zarfında şifre tabanlı oturum açma formunu kullanmaya devam etmeniz gerekir. Ancak şifre formu ve geçiş anahtarı düğmesi bırakırsanız kullanıcılar oturum açmak için hangisini kullanacakları konusunda gereksiz bir seçim yapmak zorunda kalır. İdeal olan, basit bir oturum açma sürecidir.

Bu noktada koşullu kullanıcı arayüzü devreye girer. Koşullu kullanıcı arayüzü, şifrelere ek olarak otomatik doldurma öğelerinin bir parçası olarak geçiş anahtarı önermek için bir form giriş alanı oluşturabileceğiniz bir WebAuthn özelliğidir. Kullanıcı, otomatik doldurma önerilerinde bir geçiş anahtarına dokunursa kimliğini yerel olarak doğrulamak için cihazın ekran kilidini kullanması istenir. Kullanıcı işlemi, şifre tabanlı oturum açma işlemine neredeyse tamamen benzediği için bu, sorunsuz bir kullanıcı deneyimidir.

Form otomatik doldurma işlemi kapsamında önerilen geçiş anahtarı.

Koşullu kullanıcı arayüzünü etkinleştirme

Koşullu bir kullanıcı arayüzünü etkinleştirmek için tek yapmanız gereken bir giriş alanının autocomplete özelliğine webauthn jetonu eklemektir. Jeton ayarlandığında, ekran kilidi kullanıcı arayüzünü koşullu olarak tetiklemek için navigator.credentials.get() yöntemini mediation: 'conditional' dizesiyle çağırabilirsiniz.

  • Koşullu bir kullanıcı arayüzünü etkinleştirmek için view/index.html dosyasındaki ilgili yorumdan sonra mevcut kullanıcı adı giriş alanlarını aşağıdaki HTML ile değiştirin:

view/index.html

<!-- TODO: Add passkeys to the browser autofill: Enable conditional UI. --> <input   type="text"   id="username"   class="mdc-text-field__input"   aria-labelledby="username-label"   name="username"   autocomplete="username webauthn"   autofocus /> 

Özellikleri algılama, WebAuthn'i çağırma ve koşullu bir kullanıcı arayüzünü etkinleştirme

  1. view/index.html dosyasında, ilgili yorumdan sonraki mevcut import ifadesini aşağıdaki kodla değiştirin:

view/index.html

// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI. import {   $,   _fetch,   loading,   authenticate  } from "/client.js"; 

Bu kod, daha önce uyguladığınız authenticate() işlevini içe aktarır.

  1. window.PulicKeyCredential nesnesinin kullanılabilir olduğunu ve PublicKeyCredential.isConditionalMediationAvailable() yönteminin true değeri döndürdüğünü onaylayın, ardından authenticate() işlevini çağırın:

view/index.html

// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI. if (   window.PublicKeyCredential &&   PublicKeyCredential.isConditionalMediationAvailable ) {   try {      // Is conditional UI available in this browser?     const cma =       await PublicKeyCredential.isConditionalMediationAvailable();     if (cma) {        // 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);     }   } } 

Bu bölümün çözüm kodunu inceleyin.

view/index.html

<!-- TODO: Add passkeys to the browser autofill: Enable conditional UI. --> <input   type="text"   id="username"   class="mdc-text-field__input"   aria-labelledby="username-label"   name="username"   autocomplete="username webauthn"   autofocus  /> 

view/index.html

// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI. import {    $,    _fetch,    loading,    authenticate  } from '/client.js'; 

view/index.html

// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI.         // Is WebAuthn avaiable in this browser? if (window.PublicKeyCredential &&     PublicKeyCredential.isConditionalMediationAvailable) {   try {      // Is a conditional UI available in this browser?     const cma= await PublicKeyCredential.isConditionalMediationAvailable();     if (cma) {        // If a 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);     }   } } 

Dene

Web sitenizde geçiş anahtarlarının oluşturulması, kaydedilmesi, gösterilmesi ve kimlik doğrulaması işlemlerini uyguladınız.

Bu özelliği denemek için aşağıdaki adımları uygulayın:

  1. Önizleme sekmesine gidin.
  2. Gerekirse oturumu kapatın.
  3. Kullanıcı adı metin kutusunu tıklayın. Bir iletişim kutusu açılır.
  4. Oturum açmak istediğiniz hesabı seçin.
  5. Kimliğinizi cihazın ekran kilidiyle doğrulayın. /home sayfasına yönlendirilir ve oturum açarsınız.

Kimliğinizi kayıtlı şifreniz veya geçiş anahtarınızla doğrulamanızı isteyen bir iletişim kutusu.

7. Tebrikler!

Bu codelab'i tamamladınız. Sorularınız varsa bunları FIDO-DEV posta listesinde veya StackOverflow'da passkey etiketiyle sorun.

Daha fazla bilgi