Web OTP Makes Authentication Easier & More Secure
Handling authentication is one of the most common requests I receive concerning progressive web applications.
We want to protect our systems, but we don't want to make it too complicated for customers to sign in.
We have relied on combinations of usernames and passwords for decades. The problem is humans struggle to remember secure passwords. Over time good applications require longer usernames and passwords that require a combination of various character types and length.
But these passwords don't look like real words are phrases easily remembered. We need a good way to increase security, without making it impossible for the average person on the street to sign into their account.
Authentication and authorization is a complicated topic and every application must implement these layers of protection. The complexity makes it difficult for many to comprehend. It also adds exponential complexity to developing and maintaining an application.
Proper authentication and identity is difficult and it should be. Simple authentication is easy to hack. No one wants their accounts hacked, much less own a website or application where user accounts are accessible.
Passwords have lost their ability to protect our accounts. Most adults use the same password everywhere. Most are using the same password for 5 to 10 years. This makes it more likely those accounts can and will be compromised.
Microsoft Edge users can see what sites their login credentials have been exposed and my credit reporting service tracks these as well.
This can leave your enterprise or service vulnerable. Not to mention your personal accounts across the web.
Gone are the days where you keep a user profile record with a username and password field using cookies to verify authentication. Today we use much more sophisticated and secure techniques. Token authentication using an identity service is the most common pattern.
It's the authentication process that adds the complexity. Most sites and apps still use a simple username/password experience. More and more you see added layers, like biometric authentication, rolling keys and multi-factor authentication (MFA).
- Introduction to Multi-Factor Authentication and One-Time Passwords
- How Web OTP API Works
- Example Web OTP Code
- Possible Gotcha's
- Implementing a Temporary One Time Code (TOTP) Authentication System
- Expanding OTP Channels
- Takeaways
Introduction to Multi-Factor Authentication and One-Time Passwords
A modern way to verify a user as they authenticate is multi-factor authentication. The original incarnation of this workflow involved a rolling RSA key.
If you recently watched HBO's The Flight Attendant, an RSA key was used to sign in to a dead man's account. So I guess you can say they are mainstream.
In theory, as the key or numerical combination changes the user has a temporary one time code (OTC).
These one time codes are temporary passwords that work for a short period of time. They are the Snapchat of authentication. Not only do they have a limited life span, once they are used they 'self destruct' so they are invalid for future sessions.
These temporary passwords are known as Temporary One Time Passwords or TOTP.
Today we have more ways to issue OTC values. This includes sending email, SMS and RSA key applications on verified devices.
The problem with these channels is they add more friction or steps to the authentication workflow. This reduces the chances a consumer will complete the login.
The user has to switch between applications to copy and paste the code or worse grab their phone and type the code from that device in their computer.
If you are like me your bank, employer and even Amazon asks for some form of OTP the first time you sign in on a new device. They are trying to prevent fraud.
Implementing OTC in web applications or any application for that matter is not trivial. It requires some heavier lifting for the developer and multiple steps by users.
The Web OTP API makes life a little easier for both the developer and user by streamlining the experience.
This is still a relatively new feature. It does have broad support. Both Chrome for Android and iPhone have common support.
The underlying tech utilizes the 'credentials' API, which all modern browsers support.
For me the most difficult issue has been testing because it requires the device to support SMS. And my laptop does not have SMS.
There are other channels either supported or in the works. I have a list after we see how OTP works.
How Web OTP API Works
A one time authorization code (OTAC) is a collection of 'random' characters used to verify a user's identity for a single session.
What defines a 'session' for your application is up to you. Many of my clients want user sessions to last for days and some want the user to be continually authenticated.
I have many applications that retain authentication for months at a time. Others require logging in every few hours.
One time codes can be used for any application or web app. The Web OTC API concerns websites and progressive web apps providing a native app experience.
The real advantage of one time codes is eliminating the need for a password. Not only are passwords easier to discover these days, people tend to forget them.
One time codes eliminate the need to remember today's crazy passwords. It also makes it almost impossible to hack.
What they do require is some form of trusted communication channel to send a random code from an identity server.
The authentication requires a multi-step conversation, which separates username and password.
The downside is authentication takes more steps to complete authentication.
Common OTC Communication Channels
There are many valid communication channels to provide OTCs.
- Phone
- SMS
- Mobile Phones
- Computer
- Scanning a QR or Barcode
- Physical Mail (seriously this exists! Talk about latency)
For the purpose of this article and the OTC API I am focusing on using SMS. This may also be called SMS verify.
This requires your identity server to include some form of multi-factor authentication (MFA). Quality identity services include MFA capabilities, including SMS as an option.
But you can configure your own SMS code provider, which is what I am doing here to demonstrate the API.
Example Web OTP Code
The incoming message is caught by the browser when your OTP enabled page is loaded. The message is intercepted, parsed and if it is in the right format a verification prompt is displayed.
Note: If your site is not [secured using HTTPS then the Web OTP API is disabled.
{sequence diagram}
In your page's script you will of course need the Web OTC API integrated. First verify the API is supported:
if ("OTPCredential" in window) {
...
}
The Web OTP API works in concert with the browser's Credential Store API. I won't dive into this provider's details, just show how to use the API to manage the one-time passcode.
The navigator object has a CredentialStore object, accessible through the credentials property. The following code configures the CredentialStore to return a promise that resolves once a credential is available that matches the configuration. If no matching provder is found the promise resolves with a null value.
Here we are configuring the method to look for a credential provided using SMS or text messaging.
const content = await navigator.credentials.get({
otp: {
transport:['sms']
}
});
In the full code you will that we also supply an AbortController. This deals with the authentication process being cancelled.
When handling the resolution of the credentials get method you can access the OTP value. Here it is being set to an input field in a form. Once the value is set the form is automatically submitted.
By automatically filling in the form and submitting it you are freeing the user from extra steps. You could also just add the OTP value to a direct API call using Fetch.
navigator.credentials.get({
otp: { transport:['sms'] },
signal: ac.signal
}).then(otp => {
input.value = otp.code;
if (form) {
form.submit();
}
});
{
code: "123456" // Obtained OTP
type: "otp" // `type` is always "otp"
}
An example form to match the above code looks like this:
<form>
<input autocomplete="one-time-code" required/>
<input type="submit">
</form>
Notice how the input field has the autocomplete attribute set to 'one-time-code'? This helps the browser (Safari in particular) know what input field corresponds to the code. If all goes well this input will be autofilled when the user verifies the automatic prompt.
The full code example puts all the peices together. You can also see how the user might able to cancel or abort the process.
if ('OTPCredential' in window) {
const input = document.querySelector('input[autocomplete="one-time-code"]');
if (!input) {
return;
}
const ac = new AbortController();
const form = input.closest('form');
if (form) {
form.addEventListener('submit', e => {
ac.abort();
});
}
navigator.credentials.get({
otp: { transport:['sms'] },
signal: ac.signal
}).then(otp => {
input.value = otp.code;
if (form) {
form.submit();
}
}).catch(err => {
console.log(err);
});
}
SMS Text Format
To trigger the OTC API event handler the SMS message must meet a standard format. The message text should include your site's (PWA) origin and of course the one time code.
The origin is the website domain. For example mine is love2dev.com. To trigger the API handler in your progressive web application @ must preceed the origin.
The OTC should be preceeded by the pound sign ('#').
For example:
Your OTP is: 123456.
@www.example.com #123456
The message can include any sort of additional text you want. It should be information that is helpful to the user.
Possible Gotcha's
As with any feature or API there may be scenarios where things don't work.
A common reason the user may not be prompted is due to OTP provider's phone number being in the contact list. This is due to an underlying dependency on a low level SMS management API. Generally OTP codes should come from an automated service and not one the user would add to a contact list.
User's may also experience issues when logged in with an enterprise or corporate profile. The IT department may have this feature turned off or disabled.
Implementing a Temporary One Time Code (TOTP) Authentication System
The Web OTP API is great to upgrade the security of your web applications, but what do you need on the server?
There is a standard for how codes are generated, Hashed Message Authentication Code (HMAC).
Expanding OTP Channels
Right now the Web OTP API focuses on SMS. But that may not be the best way to perform OTP in your application. I have started experimenting a little with different options. So I thought I would add those to this article to show what you may want to implement.
Using QR and Barcodes for TOTP
An interesting way to offer a one-time password is through a pictographic message. This is commonly done using a QR code. A barcode is another way because they encode numbers.
A QR code can also include a numeric or alphanumeric key. But you can also encode a link to an authentication URL with the code appended as a queryString parameter.
I prefer the queryString technique because it reduces the chance of user error. Good OTP codes are 6 or more digits long. 6 is within most of our abilities to remember short term. I say most realizing that is a stretch because we often forget one or more numbers toggling between the code source and the login screen.
Passing in a queryString means the code can be read by the PWA login page and automatically entered. You don't even have to display the code input. In that case it is hidden and the user does not even know about it.
I also like this technique for what I call a short term customer. Common use cases include visitors or new retail customers. A OTP via a QR code can allow you to create a pseudo anonymous profile for the customer. Once they complete their purchase you can push them down the sales funnel to a full account.
I also think about how many times I need to register when visiting an office. A QR code can be a great way to do a temporary authentication for the visit. You can also use that as a small hook to extend the relationship after the visit. It all depends on how you apply the concept.
Dealing with Lost or Stale Codes
The good news about using a OTP authentication system is they are more secure. This security comes with more friction in usability. Eventually the code expires, or maybe the code did not reach the user (it happens).
The good news is you can issue a new code.
A good practice would be to remove the first code, or make it not work. That way if the initial code was intercepted or compromised it cannot be used.
Takeaways
We will never stop needing to level up our system's authentication quality. One Time Passwords create temporary keyholes that can only unlock with a temporary key. This means the opportuntity to login with an exploited password a almost eliminated.
It also means real people will be spared of remembering passwords with enough entropy to be secure.
Of course the trade-off is you must add mechanisms to weave Web OTP into your progressive web apps as well as your server-side security.
I have already had clients inquiring about integrating 2 factor authentication mechansims. Web OTP is a nice way to implement this feature. I encourage you to add this layer to your authentication and/or biometric authentication.
The less mental strain you can force on your customers the better, but only when it enhances your security.