Sunucu tarafı geçiş anahtarı kaydı

Genel Bakış

Geçiş anahtarı kaydıyla ilgili önemli adımlara genel bakışı aşağıda bulabilirsiniz:

Geçiş anahtarı kayıt akışı

  • Geçiş anahtarı oluşturma seçeneklerini tanımlayın. Bu şifreleri istemciye göndererek geçiş anahtarı oluşturma görüşmenize iletebilirsiniz: WebAuthn API çağrısı (web'de navigator.credentials.create ve Android'de credentialManager.createCredential). Kullanıcı, geçiş anahtarı oluşturma işlemini onayladıktan sonra geçiş anahtarı oluşturma çağrısı çözümlenir ve kimlik bilgisi PublicKeyCredential döndürülür.
  • Kimlik bilgisini doğrulayın ve sunucuda depolayın.

Aşağıdaki bölümlerde her bir adımın ayrıntıları açıklanmaktadır.

Kimlik bilgisi oluşturma seçenekleri oluşturma

Sunucuda uygulamanız gereken ilk adım bir PublicKeyCredentialCreationOptions nesnesi oluşturmaktır.

Bunu yapmak için FIDO sunucu tarafı kitaplığınızı kullanın. Genellikle sizin için bu seçenekleri oluşturabilecek bir yardımcı program işlevi sunar. SimpleWebAuthn teklifleri, örneğin, generateRegistrationOptions.

PublicKeyCredentialCreationOptions, geçiş anahtarı oluşturmak için gereken her şeyi içermelidir: kullanıcı, kısıtlanmış taraf hakkında bilgi ve oluşturduğunuz kimlik bilgisinin özelliklerine ilişkin yapılandırma. Bunların tümünü tanımladıktan sonra, FIDO sunucu tarafı kitaplığınızda PublicKeyCredentialCreationOptions nesnesini oluşturmaktan sorumlu olan işleve gerektiği şekilde iletin.

Bazı PublicKeyCredentialCreationOptions. alanları sabit olabilir. Diğerleri sunucuda dinamik olarak tanımlanmalıdır:

  • rpId: Sunucuda RP kimliğini doldurmak için sunucu tarafı işlevlerini veya web uygulamanızın ana makine adını veren example.com gibi değişkenleri kullanın.
  • user.name ve user.displayName: Bu alanları doldurmak için oturum açan kullanıcının oturum bilgilerini (veya kullanıcı, kaydolurken geçiş anahtarı oluşturuyorsa yeni kullanıcı hesabı bilgilerini) kullanın. user.name genellikle bir e-posta adresidir ve kısıtlanmış taraf için benzersizdir. user.displayName, kullanıcı dostu bir addır. Tüm platformların displayName kullanmayacağını unutmayın.
  • user.id: Hesap oluşturulduktan sonra üretilen rastgele, benzersiz bir dize. Düzenlenebilir olan bir kullanıcı adının aksine, kalıcı olmalıdır. User-ID bir hesabı tanımlar, ancak kimliği tanımlayabilecek bilgiler (PII) içermemelidir. Sisteminizde muhtemelen zaten bir User-ID vardır ancak gerekirse kimliği tanımlayabilecek bilgiler içermemesi için geçiş anahtarları için özel olarak bir kullanıcı kimliği oluşturun.
  • excludeCredentials: Mevcut kimlik bilgilerinin listesi Geçiş anahtarı sağlayıcısından geçiş anahtarının yinelenmesini önleyen kimlikler. Bu alanı doldurmak için veritabanınızda bu kullanıcının mevcut kimlik bilgilerini arayın. Mevcutsa yeni geçiş anahtarı oluşturulmasını engelle başlıklı makaleyi inceleyin.
  • challenge: Geçiş anahtarı sağlayıcısının kimliğini ve yayınladığı verileri doğrulamak için daha gelişmiş bir teknik olan onay özelliğini kullanmadığınız sürece kimlik bilgisi kaydında sorun yaşamazsınız. Ancak onay kullanmasanız bile sorgulama yine de zorunlu bir alandır. Bu durumda, kolaylık olması açısından bu zorluğu tek bir 0 olarak ayarlayabilirsiniz. Kimlik doğrulama için güvenli sorgulama oluşturma talimatlarını Sunucu tarafı geçiş anahtarı kimlik doğrulaması bölümünde bulabilirsiniz.

Kodlama ve kodu çözme

Sunucu tarafından gönderilen PublicKeyCredentialCreationOptions
Sunucu tarafından PublicKeyCredentialCreationOptions gönderildi. PublicKeyCredentialCreationOptions adresinin HTTPS üzerinden teslim edilebilmesi için challenge, user.id ve excludeCredentials.credentials sunucu tarafında base64URL olarak kodlanmalıdır.

PublicKeyCredentialCreationOptions, ArrayBuffer olan alanları içerdiğinden JSON.stringify() tarafından desteklenmez. Bu, şu anda PublicKeyCredentialCreationOptions öğesini HTTPS üzerinden göndermek için bazı alanların sunucuda base64URL kullanılarak manuel olarak kodlanması ve ardından istemcide çözülmesi gerektiği anlamına gelir.

  • Sunucuda, kodlama ve kod çözme işlemi genellikle FIDO sunucu tarafı kitaplığınız tarafından gerçekleştirilir.
  • İstemcide, kodlama ve kod çözme işleminin şu anda manuel olarak yapılması gerekir. Gelecekte daha kolay hale gelecek: Seçenekleri JSON biçiminde PublicKeyCredentialCreationOptions biçimine dönüştürmek için bir yöntem kullanıma sunulacaktır. Chrome'daki uygulamanın durumuna göz atın.

Örnek kod: kimlik bilgisi oluşturma seçenekleri oluşturma

Örneklerimizde SimpleWebAuthn kitaplığını kullanıyoruz. Burada, ortak anahtar kimlik bilgisi seçeneklerinin oluşturulması işlemini generateRegistrationOptions işlevine aktarıyoruz.

import {
  generateRegistrationOptions,
  verifyRegistrationResponse,
  generateAuthenticationOptions,
  verifyAuthenticationResponse
} from '@simplewebauthn/server';
import { isoBase64URL } from '@simplewebauthn/server/helpers';

router.post('/registerRequest', csrfCheck, sessionCheck, async (req, res) => {
  const { user } = res.locals;
  // Ensure you nest verification function calls in try/catch blocks.
  // If something fails, throw an error with a descriptive error message.
  // Return that message with an appropriate error code to the client.
  try {
    // `excludeCredentials` prevents users from re-registering existing
    // credentials for a given passkey provider
    const excludeCredentials = [];
    const credentials = Credentials.findByUserId(user.id);
    if (credentials.length > 0) {
      for (const cred of credentials) {
        excludeCredentials.push({
          id: isoBase64URL.toBuffer(cred.id),
          type: 'public-key',
          transports: cred.transports,
        });
      }
    }

    // Generate registration options for WebAuthn create
    const options = generateRegistrationOptions({
      rpName: process.env.RP_NAME,
      rpID: process.env.HOSTNAME,
      userID: user.id,
      userName: user.username,
      userDisplayName: user.displayName || '',
      attestationType: 'none',
      excludeCredentials,
      authenticatorSelection: {
        authenticatorAttachment: 'platform',
        requireResidentKey: true
      },
    });

    // Keep the challenge in the session
    req.session.challenge = options.challenge;

    return res.json(options);
  } catch (e) {
    console.error(e);
    return res.status(400).send({ error: e.message });
  }
});

Ortak anahtarı saklama

Sunucu tarafından gönderilen PublicKeyCredentialCreationOptions
navigator.credentials.create, bir PublicKeyCredential nesnesi döndürür.

navigator.credentials.create kodu istemcide başarıyla çözüldüğünde geçiş anahtarı başarıyla oluşturulmuş demektir. Bir PublicKeyCredential nesnesi döndürülür.

PublicKeyCredential nesnesi, geçiş anahtarı sağlayıcısının istemcinin geçiş anahtarı oluşturma talimatına verdiği yanıtı temsil eden bir AuthenticatorAttestationResponse nesnesi içerir. Bu dosya, RP olarak daha sonra kullanıcının kimliğini doğrulamak için ihtiyaç duyacağınız yeni kimlik bilgileriyle ilgili bilgileri içerir. AuthenticatorAttestationResponse hakkında daha fazla bilgiyi Ek: AuthenticatorAttestationResponse bölümünde bulabilirsiniz.

PublicKeyCredential nesnesini sunucuya gönderin. Aldıktan sonra doğrulayın.

Bu doğrulama adımını FIDO sunucu tarafı kitaplığınıza aktarın. Genellikle bu amaca yönelik bir yardımcı program işlevi sunar. SimpleWebAuthn teklifleri, örneğin, verifyRegistrationResponse. Ek: Kayıt yanıtının doğrulanması bölümünden, arka planda gerçekleşen gelişmeler hakkında bilgi edinebilirsiniz.

Doğrulama başarılı olduğunda, kullanıcının daha sonra bu kimlik bilgisiyle ilişkili geçiş anahtarıyla kimlik doğrulaması yapabilmesi için kimlik bilgisi bilgilerini veritabanınızda depolayın.

Geçiş anahtarlarıyla ilişkili ortak anahtar kimlik bilgileri için özel bir tablo kullanın. Bir kullanıcının yalnızca tek bir şifresi olabilir, ancak birden fazla geçiş anahtarı olabilir (ör. Apple iCloud Anahtar Zinciri ile senkronize edilen bir geçiş anahtarı ve Google Şifre Yöneticisi aracılığıyla bir geçiş anahtarı).

Aşağıda, kimlik bilgisi bilgilerini depolamak için kullanabileceğiniz bir örnek şema verilmiştir:

Geçiş anahtarları için veritabanı şeması

  • Kullanıcılar tablosu:
    • user_id: Birincil kullanıcı kimliği. Kullanıcının rastgele, benzersiz, kalıcı bir kimliği. Bunu, Kullanıcılar tablonuz için birincil anahtar olarak kullanın.
    • username. Düzenlenebilir olabilecek, kullanıcı tanımlı kullanıcı adı.
    • passkey_user_id: Kayıt seçeneklerinizde user.id ile temsil edilen, geçiş anahtarına özel, kimliği tanımlayabilecek bilgiler (PII) içermeyen kullanıcı kimliği. Daha sonra kullanıcı kimlik doğrulaması yapmaya çalıştığında, kimlik doğrulayıcı bu passkey_user_id öğesini userHandle içindeki kimlik doğrulama yanıtında kullanılabilir hale getirir. passkey_user_id öğesini birincil anahtar olarak ayarlamamanızı öneririz. Birincil anahtarlar, yaygın olarak kullanıldıkları için sistemlerde fiili kimliği tanımlayabilecek bilgiler (PII) haline gelir.
  • Ortak anahtar kimlik bilgileri tablosu:
    • id: Kimlik bilgisi kimliği. Bunu, Ortak anahtar kimlik bilgileri tablonuz için birincil anahtar olarak kullanın.
    • public_key: Kimlik bilgisinin ortak anahtarı.
    • passkey_user_id: Kullanıcılar tablosuyla bağlantı oluşturmak için bunu yabancı anahtar olarak kullanın.
    • backed_up: Geçiş anahtarı sağlayıcısı tarafından senkronize edilen geçiş anahtarları yedeklenir. İleride backed_up geçiş anahtarına sahip kullanıcıların şifrelerini kaydetmeyi düşünüyorsanız yedekleme durumunu saklamak yararlı olabilir. authenticatorData ürünündeki işaretleri inceleyerek veya bu bilgilere kolayca erişmenizi sağlamak için genellikle mevcut olan FIDO sunucu tarafı kitaplık özelliğini kullanarak geçiş anahtarının yedeklenip yedeklenmediğini kontrol edebilirsiniz. Yedekleme uygunluğunun depolanması, olası kullanıcı sorgularının ele alınması açısından faydalı olabilir.
    • name: İsteğe bağlı olarak, kullanıcıların kimlik bilgilerine özel adlar vermesini sağlamak için kimlik bilgisinin görünen adı.
    • transports: Bir aktarım dizisi. Aktarımların depolanması, kimlik doğrulama kullanıcı deneyimi için kullanışlıdır. Aktarımlar kullanılabilir olduğunda tarayıcı buna göre davranabilir ve geçiş anahtarı sağlayıcısının istemcilerle iletişim kurmak için kullandığı aktarımla eşleşen bir kullanıcı arayüzü gösterebilir. Bu, özellikle allowCredentials alanının boş olmadığı yeniden kimlik doğrulama kullanım durumları için geçerlidir.

Geçiş anahtarı sağlayıcı, kimlik bilgisinin oluşturulma zamanı ve son kullanılma zamanı gibi diğer bilgileri de kullanıcı deneyimi amacıyla saklayabilirsiniz. Daha fazla bilgi için Geçiş anahtarları kullanıcı arayüzü tasarımı bölümünü inceleyin.

Örnek kod: kimlik bilgisini depolama

Örneklerimizde SimpleWebAuthn kitaplığını kullanıyoruz. Burada, kayıt yanıtı doğrulamasını verifyRegistrationResponse işlevine devrederiz.

import { isoBase64URL } from '@simplewebauthn/server/helpers';


router.post('/registerResponse', csrfCheck, sessionCheck, async (req, res) => {
  const expectedChallenge = req.session.challenge;
  const expectedOrigin = getOrigin(req.get('User-Agent'));
  const expectedRPID = process.env.HOSTNAME;
  const response = req.body;
  // This sample code is for registering a passkey for an existing,
  // signed-in user

  // Ensure you nest verification function calls in try/catch blocks.
  // If something fails, throw an error with a descriptive error message.
  // Return that message with an appropriate error code to the client.
  try {
    // Verify the credential
    const { verified, registrationInfo } = await verifyRegistrationResponse({
      response,
      expectedChallenge,
      expectedOrigin,
      expectedRPID,
      requireUserVerification: false,
    });

    if (!verified) {
      throw new Error('Verification failed.');
    }

    const { credentialPublicKey, credentialID } = registrationInfo;

    // Existing, signed-in user
    const { user } = res.locals;
    
    // Save the credential
    await Credentials.update({
      id: base64CredentialID,
      publicKey: base64PublicKey,
      // Optional: set the platform as a default name for the credential
      // (example: "Pixel 7")
      name: req.useragent.platform, 
      transports: response.response.transports,
      passkey_user_id: user.passkey_user_id,
      backed_up: registrationInfo.credentialBackedUp
    });

    // Kill the challenge for this session
    delete req.session.challenge;

    return res.json(user);
  } catch (e) {
    delete req.session.challenge;

    console.error(e);
    return res.status(400).send({ error: e.message });
  }
});

Ek: AuthenticatorAttestationResponse

AuthenticatorAttestationResponse iki önemli nesne içeriyor:

  • response.clientDataJSON, istemci verilerinin JSON sürümüdür. Web'deki veriler, tarayıcı tarafından görüldüğü şekildedir. RP kaynağını, görevi ve istemci bir Android uygulamasıysa androidPackageName içerir. Kısıtlanmış taraf olarak clientDataJSON okuması, create isteği sırasında tarayıcının gördüğü bilgilere erişmenizi sağlar.
  • response.attestationObjectiki farklı bilgi içeriyor:
    • attestationStatement (onay kullanmazsanız sizinle alakalı değildir).
    • authenticatorData verileri, geçiş anahtarı sağlayıcısı tarafından görülüyor. Kısıtlanmış taraf olarak, authenticatorDatabu belgeyi okumak geçiş anahtarı sağlayıcısı tarafından görülen ve create isteği esnasında döndürülen verilere erişmenize olanak tanır.

authenticatorDatayeni oluşturulan geçiş anahtarıyla ilişkili ortak anahtar kimlik bilgisi hakkında önemli bilgileri içerir:

  • Ortak anahtar kimlik bilgisinin kendisi ve benzersiz kimlik bilgisi kimliği.
  • Kimlik bilgisiyle ilişkili RP kimliği.
  • Geçiş anahtarı oluşturulduğunda kullanıcı durumunu açıklayan işaretler: Kullanıcının gerçekten burada olup olmadığı ve kullanıcının başarıyla doğrulanıp doğrulanmadığı (userVerification sayfasına göz atın).
  • Geçiş anahtarı sağlayıcıyı tanımlayan AAGUID. Geçiş anahtarı sağlayıcısının gösterilmesi, özellikle de birden fazla geçiş anahtarı sağlayıcıda hizmetiniz için kayıtlı geçiş anahtarı olan kullanıcılarınız için yararlı olabilir.

authenticatorData, attestationObject ile iç içe yerleştirilmiş olsa da onay kullanıp kullanmadığınıza bakılmaksızın geçiş anahtarı uygulamanız için içerdiği bilgiler gereklidir. authenticatorData olarak kodlanmıştır ve ikili biçimde kodlanmış alanları içerir. Sunucu tarafı kitaplığınız genellikle ayrıştırma ve kod çözme işlemlerini gerçekleştirir. Sunucu tarafı kitaplığı kullanmıyorsanız sunucu tarafında yapılan ayrıştırma ve kodu çözme konusunda bir miktar tasarruf etmek için getAuthenticatorData() istemci tarafında yararlanabilirsiniz.

Ek: Kayıt yanıtının doğrulanması

Gelişmiş seçenekte, kayıt yanıtının doğrulanması aşağıdaki kontrollerden oluşur:

  • Kısıtlanmış taraf kimliğinin sitenizle eşleştiğinden emin olun.
  • İsteğin kaynağının siteniz için beklenen bir kaynak (ana site URL'si, Android uygulaması) olduğundan emin olun.
  • Kullanıcı doğrulamasına ihtiyacınız varsa authenticatorData.uv kullanıcı doğrulama işaretinin true olduğundan emin olun. Geçiş anahtarları için kullanıcı varlığı her zaman gerekli olduğundan authenticatorData.up kullanıcı varlığı işaretinin true olduğundan emin olun.
  • İstemcinin, istediğiniz sorgulamayı sağlayıp sağlayamadığını kontrol edin. Onay kullanmazsanız bu kontrol önemli değildir. Ancak, bu kontrolün uygulanması en iyi uygulamadır: Gelecekte onay kullanmaya karar verirseniz kodunuzun hazır olmasını sağlar.
  • Kimlik bilgisi kimliğinin henüz herhangi bir kullanıcı için kaydedilmediğinden emin olun.
  • Kimlik bilgisini oluşturmak için geçiş anahtarı sağlayıcısı tarafından kullanılan algoritmanın, belirttiğiniz bir algoritma olduğunu doğrulayın (publicKeyCredentialCreationOptions.pubKeyCredParams öğesinin her alg alanında, bu genellikle sunucu tarafı kitaplığınızda tanımlanır ve sizden görülemez). Bu, kullanıcıların yalnızca izin vermeyi seçtiğiniz algoritmalara kaydolmasını sağlar.

Daha fazla bilgi edinmek istiyorsanız SimpleWebAuthn'un verifyRegistrationResponse için kaynak koduna bakın veya spesifikasyonda doğrulamaların tam listesini inceleyin.

Sıradaki

Sunucu tarafı geçiş anahtarı kimlik doğrulaması