otplib
Time-based (TOTP) and HMAC-based (HOTP) One-Time Password library
About
otplib
is a JavaScript One Time Password (OTP) library for OTP generation and verification.
It implements both HOTP - RFC 4226
and TOTP - RFC 6238,
and are tested against the test vectors provided in their respective RFC specifications.
These datasets can be found in the packages/tests-data
folder.
This library is also compatible with Google Authenticator,
and includes additional methods to allow you to work with Google Authenticator.
Features
- Typescript support
- Class interfaces
- Function interfaces
- Async interfaces
- Pluggable modules (crypto / base32)
crypto (node)
crypto-js
@ronomon/crypto-async
thirty-two
base32-encode
+ base32-decode
- Presets provided
browser
default (node)
default-async (same as default, but with async methods)
v11 (adapter for previous version)
Quick Start
References:
In Node.js
npm install otplib thirty-two
import { authenticator } from 'otplib/preset-default';
const secret = 'KVKFKRCPNZQUYMLXOVYDSQKJKZDTSRLD';
const token = authenticator.generate(secret);
try {
const isValid = authenticator.check(token, secret);
const isValid = authenticator.verify({ token, secret });
} catch (err) {
console.error(err);
}
In Browser
The browser preset is a self contained umd
module with Buffer
split out as an external dependency.
As such, there are 2 scripts required: preset-browser/index.js
and preset-browser/buffer.js
.
<script src="https://unpkg.com/otplib@^12.0.0/preset-browser/buffer.js"></script>
<script src="https://unpkg.com/otplib@^12.0.0/preset-browser/index.js"></script>
<script type="text/javascript">
</script>
The buffer.js
provided by this library is a cached copy
from https://www.npmjs.com/package/buffer.
You can also download and include the latest version via their project page.
In the above example, we are directly using the scripts hosted by unpkg.com
.
You can also npm install otplib
and get a copy from the node_modules/otplib/preset-browser
folder.
Migration Guide
This library follows semver
. As such, major version bumps usually mean API changes or behavior changes.
Please check upgrade notes for more information,
especially before making any major upgrades.
Check out the release notes associated with each tagged versions
in the releases page.
Migrating from v11.x
v12.x is a huge architectural and language rewrite. Please check out the docs if you are migrating.
A preset adapter is available to provide methods that behave like v11.x
of otplib
.
Link to v11.x README.md and v11.x API Docs.
import { authenticator } from 'otplib';
import { authenticator } from 'otplib/preset-v11';
Getting Started
This is a more in-depth setup guide for installing, configuring and customising
your dependencies for the library.
Check out the Quick Start guide if you do need / want
to customise any dependencies from the presets.
Install the Package
npm install otplib
Release Type | Version | Command |
---|
Current / Stable | | npm install otplib |
Release Candidate | | npm install otplib@next |
Choose Your Plugins
Adding Crypto
The crypto modules are used to generate the digest used to derive the OTP tokens from.
By default, Node.js has inbuilt crypto
functionality, but you might want to replace it
for certain environments that do not support it.
Currently out-of-the-box, there are some Crypto Plugins included.
Install the dependencies for one of them.
npm install crypto-js
Adding Base32
If you're using Google Authenticator, you'll need a base32 module for
encoding and decoding your secrets.
Currently out-of-the-box, there are some Base32 Plugins included.
Install the dependencies for one of them.
npm install thirty-two
npm install base32-encode base32-decode
Initialise your Instance
Using Classes
import { HOTP, TOTP, Authenticator } from 'otplib';
import { keyDecoder, keyEncoder } from 'otplib/plugin-thirty-two';
import { keyDecoder, keyEncoder } from 'otplib/plugin-base32-enc-dec';
import { createDigest, createRandomBytes } from 'otplib/plugin-crypto';
import { createDigest, createRandomBytes } from 'otplib/plugin-crypto-js';
const hotp = new HOTP({ createDigest });
const totp = new TOTP({ createDigest });
const authenticator = new Authenticator({
createDigest,
createRandomBytes,
keyDecoder,
keyEncoder
});
const token = hotp.generate(YOUR_SECRET, 0);
const token = totp.generate(YOUR_SECRET);
const token = authenticator.generate(YOUR_SECRET);
Using Functions
Alternatively, if you are using the functions directly instead of the classes,
pass these as options into the functions.
import {
hotpOptions,
hotpToken,
totpOptions,
totpToken,
authenticatorOptions,
authenticatorToken
} from 'otplib/core';
const token = hotpToken(YOUR_SECRET, 0, hotpOptions({ createDigest));
const token = totpToken(YOUR_SECRET, totpOptions({ createDigest));
const token = authenticatorToken(YOUR_SECRET, authenticatorOptions({
createDigest,
createRandomBytes,
keyDecoder,
keyEncoder
));
Available Options
All instantiated classes will have their options inherited from their respective options
generator. i.e. HOTP from hotpOptions
, TOTP from totpOptions
and Authenticator from authenticatorOptions
.
All OTP classes have an object setter and getter method to override these default options.
For example,
import { authenticator } from 'otplib/preset-default';
authenticator.options = {
step: 30,
window: 1
};
const opts = authenticator.options;
authenticator.resetOptions();
const opts = authenticator.allOptions();
HOTP Options
Option | Type | Description |
---|
algorithm | string | The algorithm used for calculating the HMAC. |
createDigest | function | Creates the digest which token is derived from. |
createHmacKey | function | Formats the secret into a HMAC key, applying transformations (like padding) where needed. |
digest | string | USE WITH CAUTION. Same digest = same token. Used in cases where digest is generated externally. (eg: async use cases) |
digits | integer | The length of the token. |
encoding | string | The encoding that was used on the secret. |
{
algorithm: 'sha1'
createDigest: undefined,
createHmacKey: hotpCreateHmacKey,
digits: 6,
encoding: 'ascii',
}
TOTP Options
Note: Includes all HOTP Options
Option | Type | Description |
---|
epoch | integer | USE WITH CAUTION. Same epoch = same token. Starting time since the UNIX epoch (seconds). Epoch is JavaScript formatted. i.e. Date.now() or UNIX time * 1000 |
step | integer | Time step (seconds) |
window | integer, [number, number] | Tokens in the previous and future x-windows that should be considered valid. If integer, same value will be used for both. Alternatively, define array: [past, future] |
{
createHmacKey: totpCreateHmacKey,
epoch: Date.now(),
step: 30,
window: 0,
}
Authenticator Options
Note: Includes all HOTP + TOTP Options
Option | Type | Description |
---|
createRandomBytes | function | Creates a random string containing the defined number of bytes to be used in generating a secret key. |
keyEncoder | function | Encodes a secret key into a Base32 string before it is sent to the user (in QR Code etc). |
keyDecoder | function | Decodes the Base32 string given by the user into a secret. |
{
encoding: 'hex',
createRandomBytes: undefined,
keyEncoder: undefined,
keyDecoder: undefined,
}
Async Options
The following options are modified for functions
and classes
which are postfixed with Async
.
eg: AuthenticatorAsync
, totpDigestAsync
, hotpTokenAsync
etc.
Option | Type | Output |
---|
createDigest | async function | function returns Promise<string> instead of string |
createHmacKey | async function | function returns Promise<string> instead of string |
createRandomBytes | async function | function returns Promise<string> instead of string |
keyEncoder | async function | function returns Promise<string> instead of string |
keyDecoder | async function | function returns Promise<string> instead of string |
Available Packages
This library has been split into 3 categories: core
, plugin
and preset
.
Core
Provides the core functionality of the library. Parts of the logic
has been separated out in order to provide flexibility to the library via
available plugins.
file | description |
---|
otplib/hotp | HOTP functions + class |
otplib/hotp | TOTP functions + class |
otplib/authenticator | Google Authenticator functions + class |
otplib/core | Aggregates hotp/totp/authenticator functions + class |
Core (Async)
file | description |
---|
otplib/hotp-async | async version of otplib/hotp |
otplib/hotp-async | async version of otplib/hotp |
otplib/authenticator-async | async version of otplib/authenticator |
otplib/core-async | async version of otplib/core |
Plugins
Crypto Plugins
plugin | type | depends on |
---|
otplib/plugin-crypto | sync | crypto (included in Node.js) |
otplib/plugin-crypto-js | sync | npm install crypto-js |
otplib/plugin-crypto-async-ronomon | async | npm install @ronomon/crypto-async |
These crypto plugins provides:
{
createDigest,
createRandomBytes,
}
Base32 Plugins
plugin | type | depends on |
---|
otplib/plugin-thirty-two | sync | npm install thirty-two |
otplib/plugin-base32-enc-dec | sync | npm install base32-encode base32-decode |
These Base32 plugins provides:
{
keyDecoder,
keyEncoder,
}
Presets
Presets are preconfigured HOTP, TOTP, Authenticator instances to
allow you to get started with the library quickly.
Each presets would need the corresponding dependent npm modules to be installed.
file | depends on | description |
---|
otplib/preset-default | npm install thirty-two | |
otplib/preset-default-async | npm install thirty-two @ronomon/crypto-async | async version of otplib/preset-default |
otplib/preset-browser | See Browser Compatibility | Webpack bundle and is self contained. |
otplib/preset-v11 | npm install thirty-two | Wrapper to adapt the APIs to v11.x compatible format |
Appendix
Type Definitions
TypeScript
support was introduced in v10.0.0
, which added type definitions over .js
files.
As of v12.0.0
, the library has been re-written in Typescript from the ground up.
Async Support
async
support was introduced in v12.0.0
.
This was added as some libraries like expo.io or even
the browser API (window.Crypto.subtle) started providing
only async methods.
There are 2 was to use async
- using async replacements, or handling digests separately.
Using Async Replacements
This is the simplest way to get started. Other than allOptions()
and resetOptions
,
all other methods are converted to async and thus needs to be Promise.resolve
or await
.
eg: await .generate(...)
, await .check(...)
import { AuthenticatorAsync } from 'otplib/core-async';
const authenticator = new AuthenticatorAsync({
});
const token = await authenticator.generate(secret);
Async over Sync Methods
In this method, you would essentially take over the digest generation, leaving
the library to handle the digest to token conversion.
import { Authenticator } from 'otplib/core';
import { authenticatorDigestAsync } from 'otplib/authenticator-async';
const authenticator = new Authenticator({
});
const digest = await authenticatorDigestAsync(secret, {
...authenticator.allOptions(),
createDigest: async (algorithm, hmacKey, counter) => 'string';
});
authenticator.options = { digest };
const token = authenticator.generate(secret);
authenticator.resetOptions();
Check the API Documentation for the full list of async functions.
Browser Compatiblity
otplib/preset-browser
is a umd
bundle with some node modules replaced to reduce the browser size.
The following defaults have been used:
- crypto:
crypto-js
- encoder:
base32-encode
- decoder:
base32-decode
To see what is included, you can take a look at packages/otplib-browser/index.ts
.
Browser bundle size
The approximate bundle sizes are as follows:
Bundle Type | Size |
---|
original | 324KB |
original, minified + gzipped | 102KB |
optimised | 30.9KB |
optimised, minified + gzipped | 9.53KB |
Paired with the gzipped browser buffer.js
module, it would be about 7.65KB + 9.53KB = 17.18KB
.
Length of Secrets
In RFC 6238, the secret / seed length for different algorithms are predefined:
HMAC-SHA1 - 20 bytes
HMAC-SHA256 - 32 bytes
HMAC-SHA512 - 64 bytes
As such, the length of the secret provided (after any decoding) will be padded and sliced
according to the expected length for respective algorithms.
Google Authenticator
Difference between Authenticator and TOTP
The default encoding option has been set to hex
(Authenticator) instead of ascii
(TOTP).
RFC3548 Base32
Note: RFC4648 obseletes RFC 3548.
Any encoders following the newer specifications will work.
Google Authenticator requires keys to be base32 encoded.
It also requires the base32 encoder to be RFC 3548 compliant.
OTP calculation will still work should you want to use
other base32 encoding methods (like Crockford's Base32)
but it will NOT be compatible with Google Authenticator.
const secret = authenticator.generateSecret();
const token = authenticator.generate(secret);
Displaying a QR code
You may want to generate and display a QR Code so that users can scan
instead of manually entering the secret. Google Authenticator and similar apps
take in a QR code that holds a URL with the protocol otpauth://
,
which you get from authenticator.keyuri
.
Google Authenticator will ignore the algorithm
, digits
, and step
options.
See the keyuri documentation
for more information.
If you are using a different authenticator app, check the documentation
for that app to see if any options are ignored, which will result in invalid tokens.
While this library provides the "otpauth" uri, you'll need a library to
generate the QR Code image.
An example is shown below:
import qrcode from 'qrcode';
import { authenticator } from 'otplib/preset-default';
const user = 'A user name, possibly an email';
const service = 'A service name';
const otpauth = authenticator.keyuri(user, service, secret);
const otpauth = authenticator.keyuri(
encodeURIComponent(user),
encodeURIComponent(service),
secret
);
qrcode.toDataURL(otpauth, (err, imageUrl) => {
if (err) {
console.log('Error with QR');
return;
}
console.log(imageUrl);
});
Note: For versions v10.x.x
and below, keyuri
does not URI encode
user
and service
. You'll need to do so before passing in the parameteres.
Getting Time Remaining / Time Used
Helper methods for getting the remaining time and used time within a validity period
of a totp
or authenticator
token were introduced in v10.0.0
.
authenticator.timeUsed();
authenticator.timeRemaining();
Using with Expo
Expo contains modified crypto implmentations targeted at the platform.
While otplib
does not provide an expo
specified package, with the re-architecture
of otplib
, you can now provide an expo native createDigest
to the library.
Alternatively, you can make use of crypto provided by otplib/plugin-crypto-js
or
the bundled browser umd module otplib/preset-browser
.
Pull Requests are much welcomed for a native expo implementation as well.
Exploring with local-repl
If you'll like to explore the library with local-repl
you can do so as well.
$ npm install
$ npm run build
$ npx local-repl
$ [otplib] > secret = 'KVKFKRCPNZQUYMLXOVYDSQKJKZDTSRLD'
$ [otplib] > otplib.authenticator.generate(secret)
Contributors
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors
specification. Contributions of any kind welcome!
License
otplib
is MIT licensed