WhatsApp обслуговує мільярди користувачів по всьому світу і для багатьох регіонів залишається основним способом спілкування, тож будь-яка зміна в застосунку миттєво відчувається у величезних масштабах.

What excites me most is the sheer scale of WhatsApp's impact. Even a small improvement to WhatsApp touches billions of users worldwide.

Mayank Manuja, Android-інженер команди WhatsApp Registration and Access

Мейнк Мануджа очолював розробку та впровадження автентифікації через passkeys у WhatsApp. За його словами, робота з аудиторією такого масштабу означає постійне врахування різних мережевих умов, можливостей пристроїв і рівня цифрової грамотності користувачів. Ще у 2023 році WhatsApp вирішив зробити ставку на passkeys, ставши одним із перших великих споживчих застосунків, що інтегрував цю технологію. Мета була проста: дати користувачам швидкий і стійкий до фішингу спосіб входу, який зменшує тертя при авторизації та захищає від перехоплення акаунтів і крадіжки облікових даних.

Чому вибір впав на passkeys

Для WhatsApp наявність кількох способів входу — це спосіб гарантувати, що користувачі завжди зможуть повернутися до свого акаунту. Passkeys дають змогу входити одним дотиком, без ризику фішингу, і при цьому працюють стабільно навіть у регіонах, де доставка SMS з одноразовими кодами часто дає збої.

В основі технології лежить криптографія з відкритим і закритим ключем, яка замінює ручне введення даних на підтвердження через біометрію чи блокування екрана. Це прискорює вхід до одного дотику у зручному нижньому вікні, яке не виводить користувача з контексту застосунку. Ефект подвійний: зручність для людини і надійний захист від фішингу для системи. Й що важливо — механізм працює навіть там, де традиційні SMS-коди підводять.

Наявність різних та надійних способів входу означає, що користувачі ніколи не втратять доступ до того, що для них важливо.

Інтеграція на стороні клієнта

З точки зору розробників WhatsApp, Credential Manager API дав чіткий уніфікований інтерфейс, який приховав складність роботи з різними провайдерами облікових даних. Коли базові сценарії інтеграції були опрацьовані, робота з API стала прямолінійною: створення і отримання облікових даних відбувається за зрозумілими шаблонами запитів і відповідей. Керівництво з реалізації доступне в документації для розробників Android.

Основний сценарій спрацював одразу, але велика різноманітність виробників пристроїв, версій Android і конфігурацій (наприклад, лише PIN-код проти біометрії, чи Android 13 проти 14+) виявила безліч нетипових ситуацій. Серед них — користувачі без блокування екрана, несподівані типи винятків, застарілі Play Services і непослідовна поведінка провайдерів облікових даних.

Для подолання цих труднощів команди WhatsApp і Google працювали разом над кількома напрямками:

  • Оптимізація пошуку облікових даних: спочатку процес пошуку показував погану швидкість, особливо для користувачів, які ще не створили passkey — а це переважна більшість на початкових етапах. Через це затримка відчувалася майже під час кожного входу. Проаналізувавши весь ланцюжок викликів і знайшовши вузькі місця, команди суттєво прискорили процес, і ці покращення в результаті пішли на користь усій екосистемі Android.
  • Обробка тимчасових станів: WhatsApp побудував повноцінний рівень обробки помилок для типових проблем — відсутність менеджера паролів, невлаштоване блокування екрана, нестабільне з'єднання, несумісне обладнання, застарілі Play Services — розділивши винятки на відновлювані й остаточні. Це дозволило системі коректно відступати назад: якщо процес passkey не вдавався завершити, застосунок безпечно переходив до традиційної автентифікації, не залишаючи користувача в непрацездатному стані.
  • Робота з винятками, специфічними для ОС: телеметрія показала проблеми на окремих пристроях — наприклад, GetPublicKeyCredentialDomException (помилка розшифрування облікових даних) на певних пристроях з Android 13 та CreatePublicKeyCredentialDomException (неможливість отримати синхронізований акаунт) під час створення passkey на Android 14. Google і команда WhatsApp дослідили причини і внесли покращення на рівні платформи для стабільнішого створення ключів. Повний список типових кодів помилок Credential Manager та їхніх причин можна знайти у відповідному довіднику.

Примітка: додаткові рекомендації щодо оптимізації користувацького досвіду при впровадженні passkeys можна знайти в блозі про найкращі практики.

Доопрацювання користувацького досвіду

На початку 2023 року passkeys були абсолютно новою концепцією, тому готових шаблонів для того, як пропонувати їх створення, просто не існувало. Завдяки численним A/B-тестам WhatsApp розробив контекстний підхід, орієнтований на користувачів, яким технологія принесе найбільшу користь. Ця стратегія постійно змінювалася: коли сценарії на рівні Android OS перетворилися на спрощений однокроковий процес, WhatsApp відповідно спростив і власні підказки, щоб не дублювати чи заплутувати інтерфейс.

Архітектура на стороні сервера і міжплатформні складнощі

На бекенді сервер WhatsApp реалізує стандартні процедури WebAuthn/FIDO2. Бекенд написаний на Erlang і звертається до бібліотеки Rust webauthn-rs через нативний інтерфейс. Ця бібліотека відповідає за перевірку підписів та розбір облікових даних, тоді як внутрішній код зосереджений на оркестрації, зберіганні та бізнес-правилах — таких як придатність, обмеження частоти запитів і життєвий цикл облікових даних.

Архітектура сервера будує ці ключові процедури навколо чотирьох основних точок входу, парами Begin і Finish для реєстрації та автентифікації.

1. Реєстрація passkey

Ця послідовність відповідає за видачу опцій створення клієнту, перевірку атестації після успішного створення на боці клієнта і надійне збереження облікових даних.

begin_registration(UserId) -> Existing = list_credentials(UserId), %% reuse the existing user handle, or mint a new one {UserHandle, IsNew} = user_handle(Existing), %% returns the client creation options and the server-side challenge state #{client_safe := CreationOptions, server_only := ChallengeState} = webauthn:start_registration(UserId, UserHandle, rp_config()), %% excludeCredentials: the user's existing credential IDs, so the device won't re-enroll one Options = with_exclude_credentials(CreationOptions, credential_ids(Existing)), store_challenge(UserId, ChallengeState), %% short TTL IsNew andalso reserve_user_handle(UserId, UserHandle), Options.
  • Ідентифікація користувача: сервер спочатку перевіряє наявні облікові дані, щоб повторно використати ідентифікатор користувача або створити новий.
  • Генерація опцій і викликів: сервер звертається до бібліотеки WebAuthn для генерації опцій створення для клієнта та безпечного стану-виклику для себе.
  • Запобігання дублюванню: сервер явно виключає наявні ідентифікатори облікових даних, щоб пристрій випадково не зареєстрував уже наявний passkey повторно.
  • Збереження виклику: сервер тимчасово зберігає виклик з коротким терміном дії (TTL) і надсилає опції назад на клієнтський пристрій.
finish_registration(UserId, Attestation) -> ChallengeState = get_challenge(UserId), %% must exist and be unexpired #{credential_id := CredId, public_key := PubKey} = webauthn:finish_registration(Attestation, ChallengeState, rp_config()), ok = index_credential(CredId, UserId), %% map credential_id -> account case multi_passkey_enabled(UserId) of true -> add_credential(UserId, CredId, PubKey); %% append (oldest evicted past the cap) false -> replace_credential(UserId, CredId, PubKey) %% single-passkey mode end, notify_client(UserId, {passkey_created, CredId}), ok.
  • Отримання виклику: сервер отримує збережений виклик, перевіряючи, що він досі існує й не застарів.
  • Перевірка атестації: сервер передає відповідь клієнта (Attestation) і виклик бібліотеці WebAuthn для перевірки запиту та вилучення нового ідентифікатора облікових даних і публічного ключа.
  • Індексація облікових даних: новий ідентифікатор облікових даних напряму зіставляється з акаунтом користувача для швидкого пошуку в майбутньому.
  • Збереження і контроль лімітів: залежно від того, чи увімкнена функція кількох passkeys, сервер або додає новий обліковий запис до списку користувача (витісняючи найстаріший при досягненні ліміту), або замінює наявний у режимі одного passkey.

2. Автентифікація за обліковими даними

Подібно до створення, сервер застосунку керує процесом входу, оркеструючи послідовність логіну. Це включає перевірку твердження після успішної автентифікації на боці клієнта та динамічне оновлення збережених облікових даних щоразу, коли WebAuthn сигналізує про потребу оновлення.

begin_authentication(UserId) -> Credentials = list_valid_credentials(UserId), #{client_safe := RequestOptions, server_only := ChallengeState} = webauthn:start_authentication(Credentials, rp_config()), store_challenge(UserId, ChallengeState), %% short TTL RequestOptions.
  • Отримання дійсних облікових даних: сервер шукає всі дійсні на цей момент облікові дані, пов'язані з користувачем.
  • Генерація виклику: на основі цих даних будуються опції запиту для клієнта і генерується новий виклик на стороні сервера.
  • Збереження і повернення: як і в реєстрації, виклик тимчасово зберігається, а опції запиту передаються клієнтському застосунку.
finish_authentication(UserId, Assertion) -> ChallengeState = get_challenge(UserId), Credentials = list_valid_credentials(UserId), case webauthn:finish_authentication(Credentials, Assertion, ChallengeState) of #{user_verified := true, credential_id := CredId, needs_update := NeedsUpdate} = Result -> %% webauthn tells us when the stored credential should be refreshed NeedsUpdate andalso refresh_credential(UserId, CredId, Result), mark_credential_used(UserId, CredId), {ok, CredId}; _ -> {error, not_allowed} end.
  • Перевірка твердження: сервер отримує збережений виклик і дійсні облікові дані, а потім просить бібліотеку WebAuthn перевірити твердження клієнта (Assertion).
  • Оновлення за потреби: якщо користувача успішно верифіковано, сервер перевіряє прапорець needs_update. Бібліотека WebAuthn використовує цей прапорець, щоб сигналізувати, чи потрібно оновити стан збережених облікових даних на сервері.
  • Завершення: сервер позначає облікові дані як використані і успішно завершує процес входу.

Детальніше про серверну реєстрацію можна прочитати в керівництві з інтеграції.

Складніші архітектурні рішення

Впровадження passkeys на сервері у великому масштабі поставило унікальні викликі, зокрема щодо архітектури акаунтів і синхронізації між пристроями. Ашиш Чоудхарі з команди бекенду WhatsApp виділив основні труднощі:

  • Перехід до кількох passkeys на акаунт: стара серверна логіка WhatsApp була сильно прив'язана до припущення про один обліковий запис на користувача. Щоб підтримати сучасні реалії з кількома пристроями, команда розробила систему з обмеженим списком, яка автоматично витісняє найстаріший обліковий запис при досягненні ліміту. Заради стабільності цю масштабну структурну зміну впроваджували поступово, з ретельним експериментуванням.
  • Баланс життєвого циклу облікових даних: керування дійсністю облікових даних вимагало обережного підходу. Занадто агресивне анулювання змушує людей повторно реєструватися без потреби, а надмірна поблажливість дозволяє застарілим ключам накопичуватися. WhatsApp вирішив цю проблему через збалансовані стани життєвого циклу, що підтримують безпеку без роздратування користувачів, доповнені автоматичним фоновим очищенням неактивних passkeys.

Новий підхід до синхронізації між пристроями

Стійка архітектура з підтримкою кількох passkeys також дозволила WhatsApp повністю переглянути питання зручності на різних платформах. Стандартний міжпристроєвий сценарій WebAuthn передбачає сканування QR-коду на одному пристрої та автентифікацію через Bluetooth на іншому. Однак WhatsApp виявив, що залежність від Bluetooth ненадійна, а користувачі часто плутали нові QR-коди з уже наявним процесом прив'язки WhatsApp Web.

Замість того щоб змушувати користувачів проходити через хиткий міжпристроєвий механізм передачі, WhatsApp дозволяє тримати passkeys нативно в різних екосистемах — наприклад, у Google Password Manager на Android і в iCloud Keychain на iOS. Коли користувач переходить на нову платформу, він просто створює новий passkey під час наступного входу. Цей підхід цілком безболісний для людини і працює безперешкодно поверх нової серверної інфраструктури з підтримкою кількох passkeys.

Що далі

Після запуску passkeys WhatsApp зафіксував впевнене органічне поширення технології серед своєї величезної бази користувачів. Перетворивши традиційний багатоетапний вхід на один безболісний біометричний жест, застосунок помітно покращив досвід користування. Розвиваючи цей успіх, WhatsApp тепер розширює застосування passkeys за межі первинного входу, досліджуючи можливість безшовної повторної автентифікації всередині застосунку для чутливих дій — наприклад, зашифрованих резервних копій із захистом passkey.

Дивлячись у майбутнє, WhatsApp активно співпрацює з партнерами-платформами, щоб створити менш обтяжливі шляхи створення облікових даних, розраховуючи, що бар'єри входу поступово знижуватимуться разом із розвитком біометричних можливостей пристроїв.

Рекомендації для розробників, які масштабуються

Для розробників, які готуються впроваджувати passkeys у великому масштабі, команда WhatsApp поділилася ключовими рекомендаціями:

  • Розробіть класифікацію помилок заздалегідь: розділіть численні винятки Credential Manager на відновлювані та остаточні стани і чітко визначте плавні шляхи відступу для кожного сценарію.
  • Розумійте свою вибірку придатних користувачів: відстежуйте технічні можливості пристрою — наявність блокування екрана, біометричного обладнання, версії Play Services — і будуйте сценарії, що заздалегідь відсіюють непридатних користувачів, а не провалюються посередині процесу.
  • Готуйте застосунок до відступу: використовуйте passkeys як оптимальний основний метод автентифікації для придатних пристроїв, але завжди тримайте традиційні методи як надійний універсальний запасний варіант.
  • Плануйте фрагментацію версій ОС: поведінка passkey може відрізнятися між операційними системами. Ретельно тестуйте на Android 13, 14 і 15+, враховуючи специфічні для виробників варіанти інтерфейсу вибору облікових даних.
  • Пропонуйте контекстно і навчайте: показуйте пропозицію створити passkey природно, під час дій, пов'язаних з безпекою. Чітко пояснюйте цінність (швидкість і безпеку) доступною мовою, щоб стимулювати впровадження серед користувачів.
  • Стежте проактивно: екосистема змінюється з кожним оновленням ОС. Постійно відстежуйте затримки та закономірності помилок, щоб випереджати зміни ландшафту пристроїв.

Як почати роботу з passkeys і Credential Manager

Розробники можуть ознайомитися з passkeys і Credential Manager на Android, скориставшись керівництвом з інтеграції та публічним прикладом коду.

Питання та проблеми можна залишати в трекері проблем Android Credentials.

Джерело: Android Developers Blog