Adding WebAuthn to a web application

Hello! In this article, we will learn how to add WebAuthn to web applications from the frontend developer side. WebAuthn is a new authentication method that provides greater security by replacing legacy passwords and SMS verifications with public key-based authentication. This not only increases security against unauthorized access, but also makes login easier for users. For example VK and other companies are already moving to similar technologies, moving away from conventional passwords.

Public Encryption Basics

To understand WebAuthn, it is important to be familiar with the concept of public encryption. This is a method where two keys are used: public and private. The public key can be freely distributed and is used to encrypt messages. The ciphertext obtained in this way can only be decrypted with the corresponding private key, which must remain secret. This technology is the basis of WebAuthn, where public keys provide strong authentication without transmitting sensitive data.

How WebAuthn works

WebAuthn

WebAuthn

WebAuthn works based on several key steps:

  1. Creating a challenge: The server generates a unique call and sends it to the user’s device.

  2. User confirmation: The user confirms his or her intent to authenticate using biometrics, a PIN, or another method.

  3. Call signature: The device signs the challenge with its private key, confirming the legitimacy of the request.

  4. Sending an assertion: The device sends the signed call back to the site.

  5. Validation verification: The site verifies the signature using a previously stored public key. A successful check confirms the user’s authentication.

Example implementation in a web application

In this part of the article, we will develop a simple web application using React.js. Our application will include one username field and two function buttons: ‘Register’ and ‘Login’.

Registration

To register a new user, we need to implement a function that requests the creation of new credentials via navigator.credentials.create(). The important component here is publicKeyCredentialCreationOptions, which defines the parameters for creating a new key. For a more detailed understanding of these parameters, I recommend that you read WebAuthn documentation.

// Функция для создания новых учетных данных (credentials) с использованием Web Authentication API.
const getCredential = async ({
    challenge, // Хеш (вызов) который получаем от сервера
  }) => {
    // Опции для создания публичного ключа.
    const publicKeyCredentialCreationOptions = {
      rp: {
        name: 'my super app', // имя надежной стороны (Relying Party)
        id: 'webauthn.fancy-app.site', // идентификатор надежной стороны (Домен)
      },
      user: {
        id: Uint8Array.from(userId, (c) => c.charCodeAt(0)), // преобразование userId в Uint8Array
        name: 'User', // имя пользователя 
        displayName: 'Full username', // отображаемое имя пользователя
      },
      challenge: Uint8Array.from(challenge, (c) => c.charCodeAt(0)), // преобразование challenge в Uint8Array
      pubKeyCredParams: [
        // предпочтительные параметры для криптографического алгоритма
        {
          type: 'public-key', // тип ключа
          alg: -7, // алгоритм ECDSA с кривой P-256
        },
        {
          type: 'public-key',
          alg: -257, // алгоритм RSA с ограничением SHA-256
        },
      ],
      timeout: 60000, // таймаут ожидания ответа (в миллисекундах)
      excludeCredentials: [], // список учетных данных, которые следует исключить
      authenticatorSelection: { // критерии выбора аутентификатора
        residentKey: 'preferred',
        requireResidentKey: false,
        userVerification: 'required', // требование верификации пользователя
      },
      attestation: 'none', // тип аттестации, здесь не требуется
      extensions: { // расширения
        credProps: true, // если true, возвращаются свойства учетных данных
      },
    };
    // API вызов для создания новых учетных данных с помощью переданных опций.
    return await navigator.credentials.create({
      publicKey: publicKeyCredentialCreationOptions,
    });
  };

This function returns an object PublicKeyCredential, which in JavaScript represents public credentials. They are created when registering via the WebAuthn API.

After sending this data to the server, it checks the public key and, if verified successfully, registers the user who sent it

PublicKeyCredential

PublicKeyCredential

Authentication

To authenticate the user we will need to create a function that uses navigator.credentials.get(). This function will use the parameters publicKeyCredentialRequestOptionswhich should include challenge (call) and rpId (identifier of a trusted site, usually a domain)

const getAssertion = async ({ challenge }) => {
    // Создание объекта с параметрами запроса учетных данных (public key credential request options)
    const publicKeyCredentialRequestOptions = {
      // Преобразование challenge (предоставленного сервером) в Uint8Array. 
      // Это необходимо, так как WebAuthn требует, чтобы challenge был в бинарном формате.
      challenge: Uint8Array.from(challenge, (c) => c.charCodeAt(0)),

      // Пустой массив, указывающий, что любые зарегистрированные учетные данные могут быть использованы.
      // Можно указать конкретные учетные данные, если требуется ограничить доступ.
      allowCredentials: [],

      // Идентификатор надежной стороны (Relying Party ID), обычно домен, с которым ассоциирован запрос.
      rpId: 'webauthn.fancy-app.site',

      // Время ожидания в миллисекундах для завершения процесса аутентификации.
      timeout: 60000,

      // Уровень проверки пользователя. В данном случае требуется подтверждение ('required').
      // Другие варианты могут включать 'preferred' или 'discouraged'.
      userVerification: 'required',
    };

    // Вызов метода get() объекта navigator.credentials с переданными параметрами.
    // Этот вызов инициирует процесс получения ассерции аутентификации от пользователя.
    return await navigator.credentials.get({
      publicKey: publicKeyCredentialRequestOptions,
    });
};

The function will return an object PublicKeyCredential, which then needs to be sent to the server. This is necessary for the authentication procedure and subsequent receipt of an access token.

PublicKeyCredential

PublicKeyCredential

Demo

If you want to try this functionality on your device, use the link to test bench. In the console you will see PublicKeyCredential, which is used for both registration and authentication. Also, for a detailed study of the code, I offer a link GitHub repository.

Thank you for your attention)

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *