1. Authentication flow


    and how we can improve it

    Let's talk about authentication flow and how we can improve it
  2. Problem

    While researching our main customer flow, I find out that a quarter of customers drop on the authentication step. I see there great opportunity to improve conversion.
  3. This particular chart was build based on feedback collected from users that decide to leave this flow.
  4. Why not?

    • Not enough trust
    • Not enough information
    • Too complex
    But why not? Why do they not want to share their private data? There could be multiple reasons: not enough trust, not enough information, or it's just plain too complex.
  5. Trust

    User should think, that it's safe to use your service

    Trust: user should think, that it's safe to use your service.
  6. LastPass
    In 2021 report, LastPass shows that each password reused approximately thirteen times. When a user enters his password in your service, he gives you a lot of trusts.
  7. Year later it appears that 65% of people still reuse their passwords
  8. And only one third stop doing this after getting correspondent education.
  9. How to build trust?

    Show rating and review, make sure that you are responsive and available in used media-channel, add microformats

    Trust: show rating and review, make sure that you are responsive and available in used media-channel, add microformats
  10. I was amazed to see it works. After we have used schema to add this kind of widgets, we gain more trust, and this cut share of customers leave this flow.
  11. Transparency

    Explain why do you need information and how you are planing to use it. And how you wouldn't use it.

    Transparency: explain why do you need information and how you are planing to use it. And how you wouldn't use it.
  12. We add copy that explains: we wouldn't share customer contact data with anyone until the customer explicitly willing to share them with service professionals
  13. Simplicity

    Do not request data until the moment, when you need it

    For example, in one of the experiments, we decide to postpone the user phone number request until we should pass it to the service professional.
  14. HTML

    I believe that the web based on HTML, CSS and JavaScript. Also, I believe in progressive enhancement. So let's start from HTML and then see how we can iterate from there.
  15. Forms

    Let's talk about forms.
  16. What forms?

    • Registration (SR Flow)
    • Login (SR Flow and dedicated)
    • Password recovery
    What forms? When we talk about authentication, it would be registration, login and password recovery forms.
  17. Semantics!
    BIP!!!

    What could help us improve forms? Semantics!
  18. Attributes

    Attributes!
  19. autocorrect="off"
    spellcheck="false"
    autocapitalize="none"
    This attributes would allow us to prevent autocorrection and autocapitalize. You wouldn't want email to autocapitalized or spellchecked, right?
  20. Which attributes can be useful?

    • inputmode
    • autocomplete
    Aside from that, remember about inputmode and autocomplete attributes. They would allow you to set how to work input and autocompletion.
  21. inputmode

    • none
    • text
    • tel
    • url
    • email
    • numeric
    • decimal
    • search
    inputmode define how virtual keyboard on mobile phone looks like
  22. autocomplete

    • username
    • new-password
    • current-password
    • one-time-code
    autocomplete hint the brawser which autocomplete options to show
  23. Email

    inputmode="email"
    autocomplete="email"
    For example, for email both attributes should be email
  24. On the iPhone you will get options for autocomplete and keyboard will be optimized to enter email. Android at first propose to fill all the form at once, and then — choose specific email.
  25. In the registration

    or recovery form

    autocomplete="new-password"
    Another useful for registration form attribute is autocomplete with value new-password. It make sense to use it in the registration or password recovery form, when you enter it first time.
  26. At iOS Safari for new-password you will see option Suggest New Password, which generate strong password, ask if you want to use it and then propose save.
  27. Android do the same, except if you agree to use password it will just save the strong password, without any further questions.
  28. And on desktop: the same story old.
  29. Passwords

    in the authentication

    process

    autocomplete="current-password"
    current-password is the autocomplete type, which you should use in the login form when we should enter the existing password.
  30. iOS propose to fill password from login-passwords pairs, that was used on this site. Android will propose to will all the form at once, then just a password. Both will show login with which password was used.
  31. Desktop is well known and standard
  32. One time password

    inputmode="numeric"
    autocomplete="one-time-code"
    one-time-code is … well … one time code, which usually being send as an SMS, email or generated by application.
  33. 
    `${code} — your code
     for authentication.
     @${domain} #${code}`
                
    Let's look at this SMS, because it's exectly the format that would allow us to use autocomplete.
  34. When you will get this SMS, you will be able just click the code
  35. And it even more usefule, because iOS can share SMS with MacOS.
  36. So on the descktop you will have it as well!
  37. I did give you a specific SMS format, bit all this options will work as well.
  38. iOS simply looks for numbers next to words:

    1. code
    2. passcode
    Before we actually got SMS standardised, iOS searched for numbers next to code and passcode words.
  39. Chrome and Android don't support a declarative way of OTP autocompletion.

    Chrome and Android don't support a declarative way of OTP autocompletion.
  40. Origin-bound one-time codes delivered via SMS

    Raad SMS standard for OTP, I mentioned before. This is a draft, but it with nuances supported by both: iOS and Android.
  41. Automatic Strong Passwords and Security Code AutoFill

    Apple WWDC 2018 presentation, where OTP-related functionality was introdused. As well as new-password value.
  42. HTML Living Standard

    Read about autocomplete in the HTML standard
  43. Form Design Patterns Book
    Also take a look at this book: «Form design patterns». If you will read it — you will make your forms even better than this.
  44. Where all this passwords come?!

    Where did the browser get all the passwords for auto-completion?
  45. All your passwords
    belong to us! BOP!

    You give it to him.
  46. Browser

    All modern browsers allow you to save and store the credentials you enter.

    All modern browsers allow you to save and store the credentials you enter.
  47. And, if it's a registration form, in the dropdown, you see different options for fields that can be considered login.
  48. Se-e-e-e-e-e-e-et the na-a-a-a-a-a-ame!

    Не забывайте задавать имена полям. Это критически важно.
  49. <StyledInput
      ref={emailInputRef}
      type="email"
      onChange={setEmail}
      value={email}
      autoComplete="email"
      />
    Yes, we are developing react applications and rarely properly send forms nowadays. We even less often have time for graceful degradation, and our app would only work with JavaScript anyway. There is a temptation not to set a name on the field.
  50. But if we wouldn't set the name, there is a chance to see this: phone number as a login. How come?
  51. Chrome and Firefox

    1. username
    2. field, that located in the DOM before password field
    Chrome and FF search for a field with the name value username or autocomplete username. If they didn't find any — they will use a field that would be found right before password field.
  52. Safari

    1. username
    2. email
    3. field, that located in the DOM before password field
    Safari is a bit more cautious, after username it search for email, only then take the field before the password input.
  53. Help the browser

    understand

    where is the username

    autocomplete="username email"
    Keeping this in mind, I recommend you use double value for the autocomplete field. And set the name attribute.
  54. You can find all passwords, payment credentials and addresses, which the browser did save, in the settings.
  55. You can control addresses saved by the browser, delete and edit.
  56. And, even more, you can add a new one. And this does make perfect sense, keeping in mind that you will be able to save a lot of time by browser autocomplete.
  57. Sync

    • Google Password Manager
    • Firefox
    • iCloud Keychain
    Aspecially keeping in mind that this data will be synced across devides.
  58. Google Password Manager
  59. Google will, without hustle, try to turn on sync as well. In any menu, you can click «Manage your Google Account».
  60. In FF click the «Passwords» in the menu
  61. You will get to the page with your saved credentials
  62. iCloud Keychain
  63. Open Safari settings, Passwords tab.
  64. iOS. Ok, how phone will sync with the desktop?
  65. In iOS you can set the source for autocompletion. By default it's iCloud Keychain. But you can select one more. It can be Google Password Manager Or FireFox.
  66. Changing the password

  67. Aside from autofill browser can detect compromised passwords. And, even more, you can change them directly from browser! Well … almost.
  68. server.use(
    "/.well-known/change-password",
    (req, res) => {
      res.redirect("/reset-password");
    });
    This button by default will just open domain, related to the password. But we can use this URL, to make redirect to the page were customer can actually change it. It can be password recovery page, or specific one. This code will work for any express-like server.
  69. 
    server {
      rewrite
        ^/.well-known/change-password$
        http://domain.com/reset-password
        redirect;
    }
                  
    And this — in the NGINX configuration.
  70. Expected responces

    • 302 Found
    • 303 See Other
    • 307 Temporary Redirect
    URL should return one of this statuses.
  71. In the Safari it work from 2019
  72. But FireFox just only planning to introduce that.
  73. A Well-Known URL for Changing Passwords

    This is a standard, that describe, how it works.
  74. JavaScript

    I think you are pretty-much tired from HTML, so let's look what we can do with JavaScript.
  75. Credential Management

    Almost everything we will talk about is described in the Credential Management specification. It's a very unusual one: it's extendable.
  76. navigator.credentials
    navigator.credentials is the base for all things. This object gives us four primary methods to work with credentials.
  77. .create(CredentialCreationOptions)
    .store(Credential)
    .get(CredentialRequestOptions)
    .preventSilentAccess()
    .create allow us to create credentials object, .store — save it into the browser, .get let as get it back. .preventSilentAccess turn off silent login, wich we will discuss later in details.
  78. Credential types:

    • PasswordCredential
    • FederatedCredential
    • OTPCredential (WebOTP)
    • PublicKeyCredential (WebAuthn)
    • PaymentCredential
    Today we have five types of credential, and we wouldn't touch PaymentCredential, because it would be too much for this talk.
  79. PasswordCredential

    Authentication by login and password.

    The basic case: login and password.
  80. CredentialData:

    1. id
    When we create credential objects, CredentialData type will be the base for all of them. And it have only one single attribute — "id".
  81. PasswordCredential:

    1. name
    2. iconURL
    3. password
    4. type: "password"
    PasswordCredential extend CredentialData and add there fields name, iconURL, password and type. Which always will have value «password».
  82. We have registration form. When customer has clicked button "Register"…
  83. const signUpHandler = async (event) => {
      // Registration
      const form = event.currentTarget;
      if (window?.PasswordCredential) {
        const credential = await navigator.
          credentials.create({
            password: form
          });
        navigator.credentials.store(credential);
      }
    }
    We can create credential object right from the form. And save it into the browser.
  84. const signUpHandler = (event) => {
      // Registration
      const form = event.currentTarget;
      if (window?.PasswordCredential) {
        const credential = new PasswordCredential(form);
        navigator.credentials.store(credential);
      }
    }
    We can make the code bit more readable by using PasswordCredential constructor.
  85. Curious fact: if customer using strong password generated y browser, then browser will just save credentials, without need in any additional permission from the customer.
  86. Otherwise customer will see this popup:
  87. function signUpHandler (event) {
      // Registration
      const form = event.currentTarget;
      const data = new FormData(form);
    
      if (window?.PasswordCredential) {
        // …
      }
    }
    But what if we need more control over credentials?
  88. const credential =
      new PasswordCredential({
        id: data.get('email'),
        name: data.get('firstName'),
        password: data.get('password'),
        iconURL: data.get('avatar'),
      });
    navigator.credentials.store(credential);
    We can create object manually. This will allow us to add avatar image, or set which field will be used as a username.
  89. UI, as a result, can look much more appealing.
  90. navigator.credentials.get()
    Ok, now, when we did save the credentials, how we can use them? We can get them from the browser with get method.
  91. CredentialRequestOptions:

    1. mediation
      • silent
      • optional
      • required
    2. signal
    You should pass there CredentialRequestOptions object as an argument. And now, it is important to know what mediation is.
  92. CredentialRequestOptions:

    1. mediation
      • silent
      • optional
      • required
    2. signal
    silent means that browser should return credentials without any interaction from the customer side, if it's possible and null otherwise. Browser can do that if:
    1. Browser do have credentials for this origin.
    2. Browser have only one set of credentials for this origin.
    3. Prevent Silent Access method was not called.
  93. CredentialRequestOptions:

    1. mediation
      • silent
      • optional
      • required
    2. signal
    optional meand that if browser can return credentials without need of customer interaction — it will do that. Otherwise it will show customer a popup to select which credential he want to use, if there more then one. And null if no credentials for this origin or customer closed the popup.
  94. CredentialRequestOptions:

    1. mediation
      • silent
      • optional
      • required
    2. signal
    required means that browser always will see the popup, that request his permission.
  95. CredentialRequestOptions:

    1. mediation
      • silent
      • optional
      • required
    2. signal
    signal is just an instance of AbortSignal. Which allow us to close popup programmatically.
  96. PasswordCredential:

    1. password: true
    PasswordCredential extend CredentialRequestOptions and add just one field: password, which have value "true". This means that we only request from browser credentials of password type.
  97. (async () => {
      // Only run code if customer not logged in
      if (window?.PasswordCredential) {
        const credential =
          await navigator.credentials.get({
            password: true,
            mediation: 'silent',
          });
        if (credential === null) return;
        // Authentication
        // Error handling
    }})();
    To make silent login, like in Twitter, we need at every page, for Anonymous customer add this widget. It request credential with mediation silent. If we will get it — great, let's auth customer. Otherwise — just do nothing. It's ok.
  98. PasswordCredentialData:

    1. id
    2. name
    3. iconURL
    4. password
    5. type: "password"
    Inside credential objects is what we put there.
  99. In case of sucessfull authentication we will see popup
  100. if (window?.PasswordCredential) {
      const credential =
        await navigator.credentials.get({
          password: true,
          mediation: 'optional',
        });
      if (credential === null) return;
      // Authentication
      // Error handling
    }
    When customer clicked login button we can request credentials with mediation: 'optional'. Customer express wish to authenticate and popup wouldn't fill unexpected or out of place.
  101. If he will select account or give permission to authenticate — perfect. Otherwise we can show him normal login page.
  102. if (navigator?.credentials
        .preventSilentAccess) {
      await navigator?.credentials
        .preventSilentAccess(id);
    }
    After customer click logout, we need to call preventSilentAccess. Otherwise after refreshing the page customer will be logged back in. Which hardly wouldn't be an expected behaviour.
  103. But most important here is cross-platform login. With it's help we can login on a descktop and then automatically login on mobile, where it's harder to do.
  104. Let's see how it works! I don't have Android phone, so I will use emulator. I started it beforehand and login into my google account.
  105. FederatedCredentialData:

    • id
    • name
    • iconURL
    • provider
    • protocol
    • type: "federated"
    Second credential type is FederatedCredentialData. Same as PasswordCredentialData, but for social login. I will not stop here because of time constrains.
  106. It work in Blink. So most% of customers having it. There are also marked iOS support, but it's not fully true.
  107. Apple saying that there are support, but in fact it's only for PublicKeyCredential. Which we will discuss later.
  108. It looks like Password and FederatedCredential not in the priority list.
  109. Mozilla don't see it's a priority as well.
  110. The Credential Management API (Web Fundamentals)

    Read great article about Credential Management from Google.
  111. Credential Management

    And specification as well!
  112. Web OTP API

    Remember we discussed OTP declarative autofill and how it's not supported by Android?
  113. OTPCredential

    It does support OTPCredential instead. Which do the same but better!
  114. if ('OTPCredential' in window) {
      const { code, type } = await
        navigator.credentials.get({
          otp: { transport: ['sms'] }
        });
    
      if (!code) return;
      // Authentication
    }
    Request credentials with otp type and sms transport.
  115. Brower will ask permission to read SMS, then return the code.
  116. SMS requirements:

    1. Message should contain 4-10 numerals. Minimum one as a number.
    Message should contain 4-10 numerals. Minimum one as a number.
  117. SMS requirements:

    1. Message should be sent from the number that are not in the customer address book.
    This way specification ensure that we wouldn't read customer private messaging.
  118. Verify phone numbers on the web with the Web OTP API (Web Fundamentals)

    Great article at Web Fundamentals
  119. Origin-bound one-time codes delivered via SMS

    Specification on SMS format
  120. Web OTP API

    OTP related specification
  121. WebAuthn

  122. PublicKeyCredential

  123. Cross-platform

  124. Platform-dependent

  125. Face unlock
  126. Platform key:

    1. Customer register
    2. Request permission to use the key
    3. If customer confirm: register the key and save id in the browser
    4. When we want to authenticate customer — using the key
    If we are talking about simplifying authentication we are interested in the platform keys specifically.
  127. Key registration:

    1. Send data about platform and customer to the server, getting back validation object
    2. Pass it to platform and get PublicKeyCredential
    3. Send PublicKeyCredential to server to validate
    4. Getting back Сredential Id, which we can store on a client
  128. if ('PublicKeyCredential' in window) {
      /* … */
    }
  129. const hasPlatformAuthenticator
      = await PublicKeyCredential
      ?.isUserVerifyingPlatformAuthenticatorAvailable();
  130. PublicKeyCredential
      .is
        User
        Verifying
        Platform
        Authenticator
        Available();
  131. const data = new FormData(form);
    const response = await fetch(
      '/challenge/register',
      {
        method: 'POST',
        body: JSON.stringify({
          name: data.get('name'),
          email: data.get('email'),
          'new-password': data.get('new-password'),
        }),
      });
    
    const options = await response.json();
  132. const credential = await navigator.credentials
      .create({
        publicKey: options
      });
  133. {
      id: "nZOlyXf57erlQbudIIuOrQEdRUs",
      rawId: [ArrayBuffer],
      response: {
        attestationObject: [ArrayBuffer],
        clientDataJSON: [ArrayBuffer],
      },
      type: "public-key",
    }
  134. const body = JSON.stringify(attestation);
    
    const response = await fetch(`/register`, {
      method: "POST",
      body,
    });
  135. const { credentialId } = await response.json();
    localStorage.setItem('credentialId', credentialId);
  136. How to login with platform key:

    1. Checking if we have saved Сredential Id
    2. Request challenge from the server
  137. How to login with platform key:

    1. Pass challenge to the platform and getting PublicKeyCredential back
    2. Send PublicKeyCredential to the server, so we can check the challenge
  138. const credentialId = localStorage.getItem('credentialId');
    if (credentialId === null) return;
  139. const response = await fetch(
      '/challenge/login',
      { method: 'POST' }
    );
    
    const options = await response.json();
  140. const { challenge } = options;
    const credentialId = localStorage.getItem('credentialId');
    options.allowCredentials = [{
      type: "public-key",
      id: toBuffer(credentialId),
    }];
  141. const credential = await navigator.credentials.get({
      publicKey: options
    });
  142. {
      id: "FtjWT4fIT9…zs2LRxuG5fBRj9bA",
      rawId: [ArrayBuffer],
      response: {
        authenticatorData: [ArrayBuffer],
        clientDataJSON: [ArrayBuffer],
        signature: [ArrayBuffer],
        userHandle: null,
      },
      type: "public-key",
    }
  143. const body = JSON.stringify(authenticator);
    await fetch('/login', {
      method: "POST",
      body
    });
  144. WebAuthn demo application

  145. Emulate Authenticators and Debug WebAuthn in Chrome DevTools

  146. A curated list of awesome WebAuthn/FIDO2 resources

  147. WebAuthn guide

  148. Introduction to WebAuthn API (Yuriy Ackermann)

  149. Meet Face ID and Touch ID for the web (WWDC 2020 video)

  150. Meet Face ID and Touch ID for the web (Webkit blog post)

  151. Web Authentication and Windows Hello

  152. Enabling Strong Authentication with WebAuthn

  153. Fido Alliance

  154. Verifiable Credentials Data Model

  155. Yubico (YubiKey)

  156. Web Authentication: An API for accessing Public Key Credentials

  157. QRCODE with presentation link
  158. Made by Ukraine.