PasswordLess

Authenticate with not what you know, but what you have.

Table of contents

No heading

No headings in the article.

How many different passwords do you have? The constant dilemma of choosing a different password and forgetting the important one, or choosing the same password but risk losing all, if one is out, how often do you go through this?

Password is one such entity that authenticates the user unless the user uses two-factor authentication. Passwords should always be hashed when storing as well. There must be something better to use, instead of passwords right?

Web Authentication (WebAuthn) promises us that alternative, it is an HTML- 5-based API, which means we can leverage the browser to use this API. No need to build an app, or publish it to stores and maintain the app. Just use simple Javascript on the browser and you are good to go.

Now for what WebAuthn does, it leverages the Public Key Cryptography and uses the authentication process that our devices already possess. This could be a fingerprint reader, TouchID, FaceId, or USB stick. Things that are already used for authentication.

Regarding the privacy of user data, these authentication processes are used alongside public-key cryptography to sign the authentication process and validate on the server.

Let's follow through with the steps to make use of the API.

const publicKeyCredentialCreationOptions = {
    challenge: Uint8Array.from(
        "12389somerandomstring", c => c.charCodeAt(0)),
    rp: {
        name: "Abiral Khanal",
    },
    user: {
        id: Uint8Array.from(
            "UZSL85T9AFC", c => c.charCodeAt(0)),
        name: "abiral@jobins.jp",
        displayName: "Jobins Dev",
    },
    pubKeyCredParams: [{alg: -7, type: "public-key"}],
    timeout: 60000,
    attestation: "none"
};

challenge: The challenge is random bytes generated on the server, and is needed to prevent "replay attacks".

rp: RP is "relying party" it is responsible for registering and authenticating the user. The id of rp must be a subset of the domain currently in the browser.

user: This is where the information of the user being registered is placed. Authenticators will use the id to associate with the user. The ID should not contain any personally identifying information. This can be stored by the authenticator.

pubKeyCredParams: This is an array of objects describing what public key types are acceptable to a server.

timeout: Time the user has to respond to register for authentication

attestation: The attestation data that is returned from the authenticator has information that could be used to track users. A value of "none" indicates that the server does not care about attestation.

After the object is passed to the browser, it returns the credentials object that is to be passed on to the server for parsing and storing the authentication.

    const credential = await navigator.credentials.get({
        publicKey: publicKeyCredentialRequestOptions
    });

Credentials formats as such is provided by the browser.

PublicKeyCredential {
    id: 'ADSUllKQmbqdGtpu4sjseh4cg2TxSvrbcHDTBsv4NSSX9...',
    rawId: ArrayBuffer(59),
    response: AuthenticatorAttestationResponse {
        clientDataJSON: ArrayBuffer(121),
        attestationObject: ArrayBuffer(306),
    },
    type: 'public-key'
}

The array buffer datatype should be converted to Base64 and then passed to the server.

If the validation process succeeded, the server would then store the public key bytes and credentialed them in a database, associated with the user.

Steps to Login back into the system

    const credential = await navigator.credentials.get({
        publicKey: {
             challenge: Uint8Array.from(
                    randomStringFromServer, c => c.charCodeAt(0)),
                allowCredentials: [{
                    id: Uint8Array.from(
                        credentialId, c => c.charCodeAt(0)),
                    type: 'public-key',
                    transports: ['usb', 'ble', 'nfc'],
                }],
                timeout: 60000,
            } 
 });

The credential object will be returned by the browser, once the options are supplied for the public key. The returned object will be a bit different compared to the credential provided during registration.

Once the assertions have been obtained, it is passed to the server for validation. The validation methods differ as per the method used and the authenticator used for making the credential.

A verification pseudo-code is displayed below.

  const storedCredential = await getCredentialFromDatabase(
      userHandle, credentialId);

  const signedData = (
      authenticatorDataBytes +
      hashedClientDataJSON);

  const signatureIsValid = storedCredential.publicKey.verify(
      signature, signedData);

  if (signatureIsValid) {
      return "user validated";
  } else {
      return "user validation failed. "
  }

This is a great solution, as users don't have to remember their password and can easily log in based on the tech they already have on their hands. Due to the abundance of fingerprint readers and face readers on our smartphones, this option provides a very convenient way to authenticate and register a user.

You can always read more about the web auth guide.

Webauth guide

You can look at web auth implemented library on node and examples here

Webauthn Lib