Reautenticação com chave de acesso

Recursos da Web: Autenticação da Web

Este guia focado em delta detalha como implementar a autenticação de nível superior ou a reverificação de um usuário conectado antes que ele faça mudanças sensíveis na conta (por exemplo, atualizações de senhas, transferências financeiras).

Arquitetura do fluxo delta

Ao contrário da autenticação normal, a reautenticação com chave de acesso restringe as caixas de diálogo de chave de acesso estritamente às credenciais pré-registradas do usuário conectado para evitar a mistura de contas ou a falsificação de chaves de acesso durante sessões ativas.

Lado do servidor

Delta de geração de opções

Crie um endpoint que preencha os parâmetros de credenciais permitidas especificamente para o usuário ativo e conhecido:

Restringir credenciais: preencha a matriz de opções allowCredentials com registros PublicKeyCredentialDescriptor específicos que mapeiam todos os IDs de credenciais registrados para o usuário conectado. Deixar esse campo vazio ou omiti-lo faz com que as credenciais detectáveis sejam usadas, violando a segurança da sessão.

// Node.js step-up options generation example
router.post("/api/reauth/options", enforceActiveSession, async (req, res) => {
  const userPasskeys = await db.findCredentialsByUserId(req.user.id);

  const options = {
    challenge: serverGeneratedBase64UrlChallenge, // Random challenge stored in user session
    rpId: "example.com",
    // Enforce allowance strictly limited to the user's credentials list
    allowCredentials: userPasskeys.map((cred) => ({
      type: "public-key",
      id: cred.id,
      transports: cred.transports, // Speeds up resolution by indicating platform transports
    })),
  };
  return res.json(options);
});

Delta do endpoint de verificação

Verifique a declaração retornada pelo cliente:

Verificar a propriedade da conta: o endpoint de verificação PRECISA verificar explicitamente se o ID da credencial autenticada resultante retornado pelo cliente é resolvido para um registro de credencial armazenado em que o ID do usuário associado corresponde estritamente ao usuário ativo conectado (storedCredential.passkeyUserId === req.user.id). Se uma chave de acesso válida de um usuário diferente for retornada, a autenticação PRECISA ser rejeitada imediatamente.

Deltas de fluxo do lado do cliente

Os aplicativos escolhem entre duas interfaces de reautenticação, dependendo da interface da transação:

A. Fluxo de botão (sem campos de entrada)

Acione a reautenticação quando um usuário pressionar um botão "Verificar identidade" ou "Continuar com a transação".

<button id="reauth-btn" data-testid="reauth-button">Confirm Transaction</button>
let reauthAbortController = new AbortController();

async function triggerButtonReauth() {
  // Abort any background suggestion flows to avoid passkey prompt collisions
  reauthAbortController.abort();
  reauthAbortController = new AbortController();

  const optionsResponse = await fetch("/api/reauth/options", {
    method: "POST",
  });
  const optionsJSON = await optionsResponse.json();
  const publicKey =
    PublicKeyCredential.parseRequestOptionsFromJSON(optionsJSON);

  try {
    const credential = await navigator.credentials.get({
      publicKey,
      signal: reauthAbortController.signal,
    });

    if (credential) {
      const encodedCredential = credential.toJSON();
      const verifyResponse = await fetch("/api/reauth/verify", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(encodedCredential),
      });

      if (verifyResponse.ok) {
        showTransactionSuccessUI();
      } else if (verifyResponse.status === 404 && PublicKeyCredential.signalUnknownCredential) {
        await PublicKeyCredential.signalUnknownCredential({
          rpId, // RP ID must match the one defined on the server
          credentialId: encodedCredential.id
        });
      }
    }
  } catch (err) {
    if (err.name === "NotAllowedError") {
      console.log("User cancelled reauthentication.");
    }
  }
}

document
  .getElementById("reauth-btn")
  .addEventListener("click", triggerButtonReauth);

Estratégias de fallback

Substituto para detecção de recursos de chave de acesso

Sempre instale "webauthn-polyfills" e importe-o no contexto. Se PublicKeyCredential for compatível, PublicKeyCredential.getClientCapabilities também será.

import 'webauthn-polyfills';

Easy JSON Serialization Fallback

Sempre instale "webauthn-polyfills" e importe-o no contexto. Se PublicKeyCredential for compatível, PublicKeyCredential.parseRequestOptionsFromJSON e PublicKeyCredential.prototype.toJSON também serão.

import 'webauthn-polyfills';