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 özelliğe 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

2. Hazırlanın

Bu codelab'de, GitHub'dan eksik bir demo uygulamasını klonlayacak ve ardından geçiş anahtarı desteğinin uygulanmasını tamamlayacaksınız.

Projeyi klonlama

  1. GitHub'daki projeyi açın.
  2. Projeyi klonlayın veya indirin.

ac587c53b746785a.png

Projeyi yürütme

  1. Bir terminal açın ve cd start komutunu kullanarak dizini değiştirin.
  2. Proje bağımlılıklarını yüklemek için npm install komutunu çalıştırın.
  3. Projeyi npm run build && IS_LOCAL=1 npm run start ile derleyip çalıştırın.
  4. Tarayıcınızda http://localhost:8080/ adresini açın.

Web sitesinin ilk durumunu inceleyin

  1. Sitede rastgele bir kullanıcı adı girin ve 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.

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.

9b84dbaec66afe9c.png

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. Seçtiğiniz kod düzenleyicide start dizinini açın.
  2. public/client.js dosyasına gidin ve sonuna kaydırın.
  3. İlgili yorumdan sonra ş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'ye 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, kayıt sırasında gereklidir ancak onay işlemi yapılmadığı sürece 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ığı, 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ğrulamasındakiyle 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 alan adıdı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 kimliğini doğrulayabilir.

pubKeyCredParams

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

excludeCredentials

Aynı cihazın iki kez kaydedilmesini önlemek için, daha önce kaydedilmiş kimlik bilgisi kimliklerinin listesini sağlar. Sağlanırsa transports üyesi, her kimlik bilgisinin kaydı sırasında getTransports() işlevinin çağrılmasının sonucunu içermelidir. Mevcut bir geçiş anahtarı varsa yeni bir geçiş anahtarı oluşturmayı engelleme hakkındaki dokümanımızda daha fazla bilgi edinin.

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. Daha fazla bilgi için keşfedilebilir kimliklerle ilgili ayrıntılı incelememize göz atın.

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. Daha fazla bilgi için Kullanıcı Doğrulama ile ilgili ayrıntılı inceleme başlıklı makalemizi inceleyin.

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. Bu işlem PublicKeyCredential.parseCreationOptionsFromJSON() işleviyle yapılabilir:

public/client.js

// TODO: Add an ability to create a passkey: Create a credential.

// Deserialize and decode the `PublicKeyCredential.parseCreationOptionsFromJSON()`.
const options = PublicKeyCredential.parseCreationOptionsFromJSON(_options);
  1. Sonraki satırda authenticatorSelection.authenticatorAttachment değerini "platform", authenticatorSelection.requireResidentKey değerini ise true olarak ayarlayın. Bu ayar, yalnızca keşfedilebilir kimlik bilgisi özelliğine sahip bir platform kimlik doğrulayıcısının (cihazın kendisi) kullanılmasına izin verir.

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 bir 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 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. Bunu yapmak için .toJSON() simgesini kullanabilirsiniz:

public/client.js

// TODO: Add an ability to create a passkey: Register the credential to the server endpoint.

// Encode and serialize the `PublicKeyCredential`.
const credential = JSON.stringify(cred);
  1. Bir 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 the server endpoint.

  const _options = await _fetch('/auth/registerRequest');

  // TODO: Add an ability to create a passkey: Create a credential.

  // Deserialize and decode the `PublicKeyCredential.parseCreationOptionsFromJSON()`.
  const options = PublicKeyCredential.parseCreationOptionsFromJSON(_options);

  // Use platform authenticator and discoverable credential.
  options.authenticatorSelection = {
    authenticatorAttachment: 'platform',
    requireResidentKey: true
  }

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

  // TODO: Add an ability to create a passkey: Register the credential to the server endpoint.

  // Encode and serialize the `PublicKeyCredential`.
  const credential = JSON.stringify(cred);

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

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.

bfa4e7cdda47669e.png

Yer tutucu HTML ekleme

  1. Düzenleyicinizde 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>Your registered passkeys:</h3>
  <div id="list"></div>
</section>
<p id="message" class="instructions"></p>
<mdui-button id="create-passkey" class="hidden" icon="fingerprint" type="button">Create a passkey</mdui-button>

div#list öğ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 ifadede, 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 capabilities = await PublicKeyCredential.getClientCapabilities();
    // Is conditional UI available in this browser?
    if (capabilities.conditionalGet === true &&
        capabilities.passkeyPlatformAuthenticator === true) {
  1. Tüm koşullar karşılanıyorsa geçiş anahtarı oluşturma düğmesini gösterin. Aksi takdirde uyarı mesajı gösterin.

views/home.html

      createPasskey.classList.remove('hidden');
    } else {

      // If conditional UI isn't available, show a message.
      $('#message').innerText = 'This device does not support passkeys.';
    }
  } catch (e) {
    console.error(e);
  }
} else {

  // If WebAuthn isn't available, show a message.
  $('#message').innerText = 'This device does not support passkeys.';
}

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

  1. Kayıtlı geçiş anahtarlarını sunucudan getiren ve bunları listede oluşturan 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 = res.length > 0 ? html`
    <mdui-list>
      ${res.map(cred => html`
        <mdui-list-item>
          ${cred.name || 'Unnamed'}
          <mdui-button-icon data-cred-id="${cred.id}" data-name="${cred.name || 'Unnamed'}" @click="${rename}" icon="edit" slot="end-icon"></mdui-button-icon>
          <mdui-button-icon data-cred-id="${cred.id}" @click="${remove}" icon="delete" slot="end-icon"></mdui-button-icon>
        </mdui-list-item>`)}
    </mdui-list>` : html`
    <mdui-list>
      <mdui-list-item>No credentials found.</mdui-list-item>
    </mdui-list>`;
  render(creds, list);
};
  1. Bir sonraki satırda, kullanıcının /home sayfasına ulaşır ulaşmaz kayıtlı geçiş anahtarlarını başlatma olarak göstermek 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>Your registered passkeys:</h3>
  <div id="list"></div>
</section>
<p id="message" class="instructions"></p>
<mdui-button id="create-passkey" icon="fingerprint" type="button">Create a passkey</mdui-button>

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

// Is WebAuthn available in this browser?
if (window.PublicKeyCredential &&
  PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&
  PublicKeyCredential.isConditionalMediationAvailable) {
  try {
    const capabilities = await PublicKeyCredential.getClientCapabilities();
    // Is conditional UI available in this browser?
    if (capabilities.conditionalGet === true &&
      capabilities.passkeyPlatformAuthenticator === true) {
      // If conditional UI is available, reveal the Create a passkey button.
      createPasskey.classList.remove('hidden');
    } else {
      // If conditional UI isn't available, show a message.
      $('#message').innerText = 'This device does not support passkeys.';
    }
  } catch (e) {
    console.error(e);
  }
} else {
  // If WebAuthn isn't available, show a message.
  $('#message').innerText = 'This device does not support passkeys.';
}

// TODO: Add an ability to create a passkey: Render registered passkeys in a list.

async function renderCredentials() {
  const res = await _fetch('/auth/getKeys');
  const list = $('#list');
  const creds = html`${res.length > 0 ? html`
    <mdui-list>
      ${res.map(cred => html`
        <mdui-list-item>
          ${cred.name || 'Unnamed'}
          <mdui-button-icon data-cred-id="${cred.id}" data-name="${cred.name || 'Unnamed'}" @click="${rename}" icon="edit" slot="end-icon"></mdui-button-icon>
          <mdui-button-icon data-cred-id="${cred.id}" @click="${remove}" icon="delete" slot="end-icon"></mdui-button-icon>
        </mdui-list-item>`)}
    </mdui-list>` : html`
    <mdui-list>
      <mdui-list-item>No credentials found.</mdui-list-item>
    </mdui-list>`}`;
  render(creds, list);
};

renderCredentials();

// TODO: Add an ability to create a passkey: Create and register a passkey.

async function register() {
  try {
    // Start the loading UI.
    loading.start();
    // Start creating a passkey.
    await registerCredential();
    // Stop the loading UI.
    loading.stop();
    // Render the updated passkey list.
    renderCredentials();
  } catch (e) {
    // Stop the loading UI.
    loading.stop();
    // An InvalidStateError indicates that a passkey already exists on the device.
    if (e.name === 'InvalidStateError') {
      alert('A passkey already exists for this device.');
      // A NotAllowedError indicates the user canceled the operation.
    } else if (e.name === 'NotAllowedError') {
      return;
      // Show other errors in an alert.
    } else {
      alert(e.message);
      console.error(e);
    }
  }
};

createPasskey.addEventListener('click', register);

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. Sitede 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

Listede kayıtlı geçiş anahtarlarını yeniden adlandırabilir veya silebilirsiniz. Nasıl çalıştığını, codelab'de yer alan kodda 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. 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.

// Base64URL decode the challenge.
const options = PublicKeyCredential.parseRequestOptionsFromJSON(_options);

Bu codelab'in sunucusu, 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": "localhost",
  "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.

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. allowCredentials davranışları hakkında daha fazla bilgi edinin.

userVerification

"preferred" değerine ayarlayın veya varsayılan değer olduğu için atlayın. Bu ayar, cihazın ekran kilidi kullanılarak yapılan kullanıcı doğrulamasının "required", "preferred" veya "discouraged" olduğunu gösterir. "preferred" değerine ayarlandığında cihaz uygunsa kullanıcı doğrulaması istenir. Kullanıcı doğrulama davranışı hakkında daha fazla bilgi edinin.

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 kapsamında koşullu olarak 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 hali.

rawId

Kimlik bilgisinin ArrayBuffer nesne sürümü.

response.clientDataJSON

İstemci verilerinin ArrayBuffer nesnesi. Bu alan, RP sunucusunun doğrulaması gereken sorgulama ve kaynak gibi bilgileri içerir.

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 iletilebilecek şekilde kodlayın. Bunu yapmak için .toJSON() simgesini kullanabilirsiniz:

public/client.js

// TODO: Add an ability to authenticate with a passkey: Verify the credential.
// Encode and serialize the `PublicKeyCredential`.
const credential = JSON.stringify(cred);
  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ı otomatik doldurma özelliğine geçiş anahtarları ekleme

Kullanıcı geri döndüğünde oturum açma işlemini mümkün olduğunca kolay ve güvenli bir şekilde yapması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. Bu, kullanıcı işleminin şifre tabanlı oturum açma işlemine neredeyse tamamen benzemesi nedeniyle sorunsuz bir kullanıcı deneyimidir.

d616744939063451.png

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. -->
<mdui-text-field id="username" label="Username" name="username" autocomplete="username webauthn" autofocus></mdui-text-field>

Özellikleri algılama, WebAuthn'i çağırma ve koşullu 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.getClientCapabilities) {
  try {

    // Is conditional UI available in this browser?
      const capabilities = await PublicKeyCredential.getClientCapabilities();
      if (capabilities.conditionalGet) {

      // If conditional UI is available, invoke the authenticate() function.
      const user = await authenticate();
      if (user) {

        // Proceed only when authentication succeeds.
        $("#username").value = user.username;
        loading.start();
        location.href = "/home";
      } else {
        throw new Error("User not found.");
      }
    }
  } catch (e) {
    loading.stop();

    // A NotAllowedError indicates that the user canceled the operation.
    if (e.name !== "NotAllowedError") {
      console.error(e);
      alert(e.message);
    }
  }
}

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

view/index.html

<!-- TODO: Add passkeys to the browser autofill: Enable conditional UI. -->
<mdui-text-field id="username" label="Username" name="username" autocomplete="username webauthn" autofocus></mdui-text-field>

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 available on this browser?
if (window.PublicKeyCredential &&
    PublicKeyCredential.getClientCapabilities) {
  try {
    // Is conditional UI available in this browser?
    const capabilities = await PublicKeyCredential.getClientCapabilities();
    if (capabilities.conditionalGet) {
      // If conditional UI is available, invoke the authenticate() function.
      const user = await authenticate();
      if (user) {
        // Proceed only when authentication succeeds.
        $('#username').value = user.username;
        loading.start();
        location.href = '/home';
      } else {
        throw new Error('User not found.');
      }
    }
  } catch (e) {
    loading.stop();
    // A NotAllowedError indicates that the user canceled the operation.
    if (e.name !== 'NotAllowedError') {
      console.error(e);
      alert(e.message);
    }
  }
}

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