Giter VIP home page Giter VIP logo

time2fa's Introduction

Build & test npm

Time2fa

A comprehensive Node.js package that simplifies the implementation of One-Time Password (OTP) authentication using HMAC-based One-Time Password (HOTP) and Time-based One-Time Password (TOTP) algorithms.

Features

  • Support both HOTP and TOTP algorithms
  • Easy-to-use API for generating and verifying OTPs
  • Customizable OTP length, counters and time window
  • Supports various hashing algorithms (SHA-1, SHA-256, SHA-512)
  • Compatible with popular OTP generators like Google Authenticator and Authy

Installation

Install the package using NPM:

npm i --save time2fa

Usage/Examples

TOTP

Generate key

// Import Totp
import { Totp } from "time2fa";

const key = Totp.generateKey({ issuer: "N0C", user: "[email protected]" });

console.log(key);

// GenerateKey {
//   issuer: 'N0C',
//   user: '[email protected]',
//   config: { algo: 'sha1', digits: 6, period: 30, secretSize: 10 },
//   secret: 'ABCDEFGHIJKLMN12',
//   url: 'otpauth://totp/N0C:johndoe%40n0c.com?issuer=N0C&period=30&secret=ABCDEFGHIJKLMN12'
// }

Validate passcode

// Import Totp
import { Totp } from "time2fa";

const valid = Totp.validate({ passcode: "123456", secret: "ABCDEFGHIJKLMN12" });

console.log(valid);

// true || false

Generate passcodes

// Import Totp, and generateConfig for default configuration
import { Totp, generateConfig } from "time2fa";

const config = generateConfig();
const codes = Totp.generatePasscodes({ secret: "ABCDEFGHIJKLMN12" }, config);

console.log(codes);

// [ 123456 ]

QRCode generation

You must use an external library. For the example below we use qrcode.

// Import Totp and qrcode
import { Totp } from "time2fa";
import * as qrcode from "qrcode";

const key = Totp.generateKey({ issuer: "N0C", user: "[email protected]" });

console.log(key);

// GenerateKey {
//   issuer: 'N0C',
//   user: '[email protected]',
//   config: { algo: 'sha1', digits: 6, period: 30, secretSize: 10 },
//   secret: 'ABCDEFGHIJKLMN12',
//   url: 'otpauth://totp/N0C:johndoe%40n0c.com?issuer=N0C&period=30&secret=ABCDEFGHIJKLMN12'
// }

qrcode.toDataURL(key.url, (err, url) => {
  console.log(url); // Returns a Data URI containing a representation of the QR Code image.
});

HOTP

Generate Passcode

// Import Hotp, and generateConfig for default configuration and generateSecret
import { Hotp, generateConfig, generateSecret } from "time2fa";

const config = generateConfig();
const secret = generateSecret();

const code = Hotp.generatePasscode({ secret, counter: 1 }, config);

console.log(code);

// 123456

Validate passcode

// Import Hotp
import { Hotp } from "time2fa";

const valid = Hotp.validate({
  passcode: "123456",
  secret: "ABCDEFGHIJKLMN12",
  counter: 1,
});

console.log(valid);

// true || false

Helpers

generateConfig()

Generate default configuration

// Import generateConfig
import { generateConfig } from "time2fa";

const config = generateConfig();

console.log(config);

// { algo: 'sha1', digits: 6, period: 30, secretSize: 10 }

generateSecret()

Only support base32 at the moment

// Import generateSecret
import { generateSecret } from "time2fa";

const secret = generateSecret();

console.log(secret);

// ABCDEFGHIJKLMN12

generateUrl()

// Import generateSecret
import { generateUrl } from "time2fa";

const url = generateUrl({
  issuer: "N0C",
  user: "[email protected]",
  secret: "ABCDEFGHIJKLMN12",
});

console.log(url);

// otpauth://totp/N0C:johndoe%40n0c.com?issuer=N0C&period=30&secret=ABCDEFGHIJKLMN12

generateBackupCodes()

Backup code should only be used once

// Import generateBackupCodes
import { generateBackupCodes } from "time2fa";

const backupCodes = generateBackupCodes();

console.log(backupCodes);

// [
//   '810550', '236884',
//   '979342', '815504',
//   '835313', '529942',
//   '263100', '882025',
//   '204896', '516248'
// ]

Documentation

Functions

Helpers

generateConfig(config?: TotpConfig): ValidTotpConfig

generateSecret(secretSize: number = DEFAULT_TOTP_SECRET_SIZE): string

generateBackupCodes(numCodes = 10, codeLength = DEFAULT_TOTP_DIGITS): string[]

generateUrl(options: UrlOptions, config: ValidTotpConfig): string

Totp

Totp.generateKey(options: TotpOptions, config?: TotpConfig): GenerateKey

Totp.generatePasscodes(options: TotpCode, config: ValidTotpConfig): string[]

Totp.validate(options: TotpValidateOptions, config?: TotpConfig): boolean

Hotp

Hotp.generatePasscode(options: HotpCode, config: ValidTotpConfig): string

Hotp.validate(options: HotpValidateOptions, config?: TotpConfig): boolean

Interfaces / Parameters

TotpConfig

Parameter Type default Description
secretSize number 10 Optional - Secret size
period number 30 Optional - Period of time
digits number 6 Optional- Code length
algo Algorithms sha1 Optional - 'sha1' | 'sha256' | 'sha512'

ValidTotpConfig

Parameter Type default Description
secretSize number - Required - Secret size
period number - Required - Period of time
digits number - Required- Code length
algo Algorithms - Required - 'sha1' | 'sha256' | 'sha512'

TotpOptions

Parameter Type default Description
issuer string - Required - Issuer name
user string - Required - Username

UrlOptions

Parameter Type default Description
issuer string - Required - Issuer name
user string - Required - Username
secret string - Required - Secret

TotpCode

Parameter Type default Description
secret string - Required - Secret
drift number 0 Optional - Time tolerance

TotpValidateOptions

Parameter Type default Description
passcode string - Required - The passcode to validate
secret string - Required - Secret
drift number 0 Optional - Time tolerance

HotpCode

Parameter Type default Description
secret string - Required - Secret
counter number - Required - Custom counter value

HotpValidateOptions

Parameter Type default Description
passcode string - Required - The passcode to validate
secret string - Required - Secret
counter number - Required - Custom counter value

Contributing

All PR's are welcome!

Running Tests

To run tests, run the following command

npm run test

License

MIT

time2fa's People

Contributors

arku3 avatar godinc0 avatar n0cloud avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

time2fa's Issues

Validation of TOTP always false when adding custom period

Hi

I wanted to use your package. But I face an issue.

I tried to following to create a TOTP

  // Generate the OTP
  const config = generateConfig({
    period: 120,
  });
  const secret = generateSecret(config.secretSize);
  const codes = Totp.generatePasscodes({ secret: secret }, config);

and the following to validate (of course with passcode and secret from above)

  // Validate the OTP
  const config = generateConfig({
    period: 120,
  });
  const valid = Totp.validate({ passcode: '097991', secret: '2X4A3XCNPJJSHU4O' }, config);

This always results in false. When I remove the period: 120 in the config, it works.
Any idea? Is there a limit for the period?

Export Config interfaces

Description

Hi! I really like how easy this library is to implement.
However to easily wrap it in a Typescript class it would be neat to have the config object interfaces exported as well.

Thanks!

Not sure why this repo doesn't have more stars yet.

generateUrl function produces an invalid otpauth url "algorithm" paramater when attempting to scan into Google Authenticator

Hi,

Upon using the library I have noticed that the otpauth url produced by the generateUrl function causes issues when scanned by Google Authenticator. I believe this is because the algorithm parameter appended to the otpauth url string does not fit the specification for googles otpauth URL.

Currently TotpConfig has an algo typed property supporting 'sha1', 'sha256' & 'sha512' all lowercase. However, I believe google authenticator expects these to be capitalised when presented in the otpauth url within the algorithm parameter. This is suggested in the documentation here

Here is the defined type:
type Algorithms = "sha1" | "sha256" | "sha512";

Code the produces the invalid otpauth url:

const tokenConfig = time2fa.generateConfig({
    algo: 'sha256', // notice lowercase
    digits: 6,
    period: 60,
    secretSize: 10
})

const url = time2fa.generateUrl({ secret: 'S5V43NFEQPKEH3C4', issuer: 'exampleissuer', user: '[email protected]'}, tokenConfig)

This produces an otpauth like this:
otpauth://totp/exampleissuer:example%4example.com?issuer=exampleissuer&period=60&secret=S5V43NFEQPKEH3C4&algorithm=sha256
This causes Google Authenticator app to fail scanning the QR code. Showing the "Can't scan this QR code"

Code that produces valid otpauth url:

const tokenConfig = time2fa.generateConfig({
    algo: 'SHA256', // notice capitalised even though unsupported in terms of the type
    digits: 6,
    period: 60,
    secretSize: 10
})

  const url = time2fa.generateUrl({ secret: 'S5V43NFEQPKEH3C4', issuer: 'exampleissuer', user: '[email protected]'}, tokenConfig)

This produces an otpauth like this:
otpauth://totp/exampleissuer:example%4example.com?issuer=exampleissuer&period=60&secret=S5V43NFEQPKEH3C4&algorithm=SHA256
This scans correctly in Google Authenticator.

Therefore, I think you need to update your type "Algorithms" with the capitalised version or need to convert config.algo toUpperCase() when setting as a url param within generateUrl function.

Here:

if (config.algo !== DEFAULT_TOTP_ALGO) {
  params.set("algorithm", config.algo);
}

Error: Can't resolve 'crypto'

Getting this error with:

"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "^5.0.1",

imagei already tried adding the packages below to the dependencies in the package.json but then i get a different error

"crypto":"npm:crypto-browserify",
"stream":"npm:stream-browserify",
"buffer":"npm:buffer"

image

Validation always return false when adding custom config on key generation

Validating always returns false when adding a custom config.

This key works when validated:

const key = Totp.generateKey({
      issuer: "app",
      user: ctx.session.user.email,
    });

This always returns false when validated:

const key = Totp.generateKey({
      issuer: "app",
      user: ctx.session.user.email,
    }, {
      period: 60,
    });

This also doesn't work:

const config = generateConfig();
const key = Totp.generateKey({
      issuer: "app",
      user: ctx.session.user.email,
    }, {
    ...config,
      period: 60,
    });

Consideration for ValidationError behavior in validate method

Hello,

First, thank you for this typescript/no external dep implementation of totp, it was about time to replace speakeasy.

I was writing the tests for my application and got a bit confused about a ValidationError: Invalid passcode exception being thrown. Initially, I thought something was broken in my NestJS application, because the string was actually the same one I used in my custom exception!

While reviewing the validate method in the TOTP (time based) implementation:

public validate(options: TotpValidateOptions, config?: TotpConfig): boolean {
  const validatedConfig = generateConfig(config);

  const passcode = options?.passcode.replace(/\s/g, "") || "";
  if (passcode.length !== validatedConfig.digits) {
    throw new ValidationError("Invalid passcode");
  }

  const codes = this.generatePasscodes(options, validatedConfig);

  if (codes.includes(passcode)) {
    return true;
  }

  return false;
}

I noticed that when the length of the passcode is not equal to validatedConfig.digits, the code throws a ValidationError stating "Invalid passcode".

Since the validation seems to be logically part of the overall functionality and not related to the configuration issues of the library itself, I was wondering if it's more appropriate for this situation to return false instead of throwing a ValidationError.

By adjusting this, users of the library might have a smoother experience, specifically when the throw error interrupts application flow. Instead, they could manage the false return value in a way that suits their specific context.

Or maybe we should update the docs to add the try/catch in the usage section.

Does this adjustment make sense from your perspective, or are there specific reasons that a ValidationError needs to be thrown in this method?

Thank you,
Renato

user object is missing in the generateUrl

 const config = { algo: "sha1", digits: 6, period: 30, secretSize: 10 };
 const secret = "F5LE3AAKV7OD264A";
console.log(generateUrl(
      {
        issuer: "N0C",
        user: "[email protected]",
        secret: secret
      },
      config
    ));

// result: https://totp/?issuer=N0C&period=30&secret=F5LE3AAKV7OD264A

Validating passcode evaluates to false

When generating a new TOTP is successful:

      const config = generateConfig({
        digits: 6,
        period: 30,
        algo: "sha256",
        secretSize: 10,
      });

      const secret = TOTP_SECRET; // from ENV variable;
      const codes = Totp.generatePasscodes({ secret }, config);
      const totp = codes[0];

But when validating the just newly created TOTP it evaluates to false:

    const { totp } = req.body;
    const secret = TOTP_SECRET; // from ENV variable

    const config = {
      digits: 6,
      period: 30,
      algo: "sha256",
      secretSize: 10,
    };

    const isValid = Totp.validate(
      {
        passcode: totp,
        secret: secret,
      },
      config
    );
    console.log({ isValid }); // Evaluates to false when the totp is valid

What could be wrong with my code?

Module not found: Error: Can't resolve 'crypto'

Hello,
on using generateKey i get the following error:

`ERROR in ./node_modules/time2fa/dist/index.mjs 1:0-28
Module not found: Error: Can't resolve 'crypto' in 'C:\Users\sheimann\source\repos\SecVault\secvaultweb\node_modules\time2fa\dist'

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "crypto": require.resolve("crypto-browserify") }'
- install 'crypto-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "crypto": false }

webpack compiled with 1 error and 1 warning`

Is there any dependency missing..??

Regards
Sascha

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Awaiting Schedule

These updates are awaiting their schedule. Click on a checkbox to get an update now.

  • chore(deps): lock file maintenance

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/build.yml
  • actions/checkout v4
  • actions/setup-node v4
  • actions/checkout v4
  • actions/setup-node v4
  • actions/checkout v4
  • actions/setup-node v4
.github/workflows/publish.yml
  • actions/checkout v4
  • actions/setup-node v4
npm
package.json
  • @antfu/ni ^0.21.2
  • @types/node ^20.0.0
  • @typescript-eslint/eslint-plugin ^7.0.0
  • @typescript-eslint/parser ^7.0.0
  • @vitest/coverage-v8 ^1.0.0
  • bumpp ^9.1.0
  • eslint ^8.38.0
  • eslint-config-prettier ^9.0.0
  • esno ^4.0.0
  • lint-staged ^15.0.0
  • prettier ^3.0.0
  • rimraf ^5.0.0
  • simple-git-hooks ^2.8.1
  • ts-node ^10.9.1
  • typescript ^5.0.3
  • unbuild ^2.0.0
  • vitest ^1.0.0

  • Check this box to trigger a request for Renovate to run again on this repository

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.