diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3c18776f..0714b004 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -52,5 +52,5 @@ Before submitting a pull request, please make sure the following is done: Thank you for contributing! -[pr-welcome-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square +[pr-welcome-badge]: https://img.shields.io/badge/welcome-PRs-brightgreen.svg?style=flat-square&longCache=true [pr-welcome-link]: https://github.com/yeojz/otplib/blob/master/CONTRIBUTING.md diff --git a/README.md b/README.md index d7c2a868..c25a14e5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ + # otplib > Time-based (TOTP) and HMAC-based (HOTP) One-Time Password library @@ -6,9 +7,8 @@ [![Build Status][circle-badge]][circle-link] [![Coverage Status][coveralls-badge]][coveralls-link] [![npm downloads][npm-downloads-badge]][npm-link] -[![PRs Welcome][pr-welcome-badge]][pr-welcome-link] +[![TypeScript Support][type-ts-badge]][type-ts-link] - - [About](#about) - [Demo and Documentation](#demo-and-documentation) @@ -335,11 +335,14 @@ qrcode.toDataURL(otpauth, (err, imageUrl) => { Check out: [CONTRIBUTING.md][pr-welcome-link] [![Support Project][coffee-badge]][coffee-link] +[![PRs Welcome][pr-welcome-badge]][pr-welcome-link] ## License `otplib` is [MIT licensed](./LICENSE) + + [npm-badge]: https://img.shields.io/npm/v/otplib.svg?style=flat-square [npm-link]: https://www.npmjs.com/package/otplib @@ -349,7 +352,7 @@ Check out: [CONTRIBUTING.md][pr-welcome-link] [circle-link]: https://circleci.com/gh/yeojz/otplib [coveralls-badge]: https://img.shields.io/coveralls/yeojz/otplib/master.svg?style=flat-square [coveralls-link]: https://coveralls.io/github/yeojz/otplib -[pr-welcome-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square +[pr-welcome-badge]: https://img.shields.io/badge/welcome-PRs-brightgreen.svg?style=flat-square&longCache=true [pr-welcome-link]: https://github.com/yeojz/otplib/blob/master/CONTRIBUTING.md [mdn-uint8array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array [mdn-crypto]: https://developer.mozilla.org/en-US/docs/Web/API/Window/crypto @@ -363,5 +366,7 @@ Check out: [CONTRIBUTING.md][pr-welcome-link] [rfc-6238-wiki]: http://en.wikipedia.org/wiki/Time-based_One-time_Password_Algorithm [donate-badge]: https://img.shields.io/badge/donate-%3C3-red.svg?longCache=true&style=flat-square [donate-link]: https://www.paypal.me/yeojz -[coffee-badge]: https://img.shields.io/badge/%E2%98%95%EF%B8%8F%20%20-buy%20me%20a%20coffee-orange.svg?longCache=true&style=flat-square +[coffee-badge]: https://img.shields.io/badge/%E2%98%95%EF%B8%8F%20-buy%20me%20a%20coffee-orange.svg?longCache=true&style=flat-square [coffee-link]: https://ko-fi.com/geraldyeo +[type-ts-badge]: https://img.shields.io/badge/included-.d.ts-blue.svg?style=flat-square&longCache=true +[type-ts-link]: https://github.com/yeojz/otplib/tree/master/packages/types-ts diff --git a/package.json b/package.json index f3b0221a..547fae96 100644 --- a/package.json +++ b/package.json @@ -78,20 +78,17 @@ }, "jest": { "coverageDirectory": "./coverage/", - "coveragePathIgnorePatterns": [ - "/scripts/*", - "/dist/*" - ], + "coveragePathIgnorePatterns": [], "modulePaths": [ "/packages/" ], + "roots": [ + "/packages/" + ], "resetMocks": true, "setupFiles": [], "testPathIgnorePatterns": [ - "/node_modules/", - "/dist/*", - "/scripts/*", - "/tests/" + "/node_modules/" ] }, "repl": [ diff --git a/packages/otplib-core/hotpDigest.js b/packages/otplib-core/hotpDigest.js index 65bc31d6..8f4dba36 100644 --- a/packages/otplib-core/hotpDigest.js +++ b/packages/otplib-core/hotpDigest.js @@ -25,7 +25,10 @@ function hotpDigest(secret, counter, options) { } // Convert secret to encoding for hmacSecret - const hmacSecret = options.createHmacSecret(secret, options); + const hmacSecret = options.createHmacSecret(secret, { + algorithm: options.algorithm, + encoding: options.encoding + }); // Ensure counter is a buffer or string (for HMAC creation) const hexCounter = hotpCounter(counter); diff --git a/packages/otplib-core/totpCounter.js b/packages/otplib-core/totpCounter.js index 94a25211..f29e5af9 100644 --- a/packages/otplib-core/totpCounter.js +++ b/packages/otplib-core/totpCounter.js @@ -2,7 +2,7 @@ * Generates a counter for TOTP * * @module otplib-core/totpCounter - * @param {string} epoch - starting time since the UNIX epoch (seconds) + * @param {number} epoch - starting time since the UNIX epoch (seconds) * @param {number} step - time step (seconds) * @return {float} */ diff --git a/packages/types-ts/index.d.ts b/packages/types-ts/index.d.ts index 430b765e..c1308412 100644 --- a/packages/types-ts/index.d.ts +++ b/packages/types-ts/index.d.ts @@ -1,52 +1,103 @@ -type CreateHmacSecretFunction = (secret: string, options: any) => Buffer; - -interface HOTPOptions { +interface hmacOptions { algorithm?: string; - createHmacSecret?: CreateHmacSecretFunction; + encoding?: string; +} + +type createHmacSecret = ( + secret: string, + options: hmacOptions +) => Buffer; + +interface hotpOptionsInterface extends hmacOptions { + createHmacSecret?: createHmacSecret; crypto?: any; digits?: number; - encoding?: string; } -interface HOTPVerifyOptions { +interface hotpVerifyOptionsInterface { token?: string; secret?: string; counter?: number; } -interface TOTPOptions extends HOTPOptions { +type hotpCheck = ( + token: string, + secret: string, + counter: number, + options: hotpOptionsInterface +) => boolean; + +type hotpCounter = (counter: number) => string; + +type hotpDigest = ( + secret: string, + counter: number, + options: hotpOptionsInterface +) => string; + +type hotpOptions = (options: any) => hotpOptionsInterface; + +type hotpSecret = createHmacSecret; + +type hotpToken = ( + secret: string, + counter: number, + options: hotpOptionsInterface +) => string; + +interface totpOptionsInterface extends hotpOptionsInterface { epoch?: any; step?: number; window?: number | number[]; } -interface TOTPVerifyOptions { +interface totpVerifyOptionsInterface { token?: string; secret?: string; } +type totpCheck = ( + token: string, + secret: string, + options: totpOptionsInterface +) => boolean; + +type totpCheckWithWindow = ( + token: string, + secret: string, + options: totpOptionsInterface +) => number | null; + +type totpCounter = (epoch: number, step: number) => number; + +type totpOptions = (options: any) => totpOptionsInterface; + +type totpSecret = createHmacSecret; + +type totpToken = (secret: string, options: totpOptionsInterface) => string; + declare class HOTP { HOTP: typeof HOTP; getClass(): typeof HOTP; - options: HOTPOptions; - optionsAll: HOTPOptions; + options: totpOptionsInterface; + optionsAll: totpOptionsInterface; resetOptions(): this; generate(secret: string, counter: number): string; check(token: string, secret: string, counter: number): boolean; - verify(opts: HOTPVerifyOptions): boolean; + verify(opts: hotpVerifyOptionsInterface): boolean; } declare class TOTP extends HOTP { TOTP: typeof TOTP; getClass(): typeof TOTP; - options: TOTPOptions; - optionsAll: TOTPOptions; + options: totpOptionsInterface; + optionsAll: totpOptionsInterface; generate(secret: string): string; check(token: string, secret: string): boolean; checkDelta(token: string, secret: string): number | null; - verify(opts: TOTPVerifyOptions): boolean; + verify(opts: totpVerifyOptionsInterface): boolean; } declare class Authenticator extends TOTP { @@ -82,3 +133,22 @@ declare module 'otplib/hotp' { const hotp: HOTP; export = hotp; } + +declare module 'otplib/core' { + interface core { + hotpCheck: hotpCheck; + hotpCounter: hotpCounter; + hotpDigest: hotpDigest; + hotpOptions: hotpOptions; + hotpSecret: hotpSecret; + hotpToken: hotpToken; + totpCheck: totpCheck; + totpCheckWithWindow: totpCheckWithWindow; + totpCounter: totpCounter; + totpOptions: totpOptions; + totpSecret: totpSecret; + totpToken: totpToken; + } + const lib: core; + export = lib; +} diff --git a/packages/types-ts/index.spec.ts b/packages/types-ts/index.spec.ts index 1ed2b7c4..3b7f755a 100644 --- a/packages/types-ts/index.spec.ts +++ b/packages/types-ts/index.spec.ts @@ -1,5 +1,6 @@ import * as otplib from 'otplib'; import authenticator = require('otplib/authenticator'); +import core = require('otplib/core'); import hotp = require('otplib/hotp'); import totp = require('otplib/totp'); @@ -7,8 +8,8 @@ const SECRET = '1234567890'; let secret = ''; let token = ''; -secret = otplib.authenticator.generateSecret(20); -token = otplib.authenticator.generate(secret); +secret = otplib.authenticator.generateSecret(20); // $ExpectType string +token = otplib.authenticator.generate(secret); // $ExpectType string otplib.authenticator.check(token, secret); // $ExpectType boolean otplib.authenticator.checkDelta(token, secret); // $ExpectType number | null @@ -27,7 +28,7 @@ authenticator.keyuri('me', 'otplib-test', secret); // $ExpectType string authenticator.Authenticator; authenticator.getClass(); -token = otplib.totp.generate(SECRET); +token = otplib.totp.generate(SECRET); // $ExpectType string otplib.totp.check(token, SECRET); // $ExpectType boolean otplib.totp.checkDelta(token, SECRET); // $ExpectType number | null otplib.totp.verify({ secret: SECRET, token }); // $ExpectType boolean @@ -39,7 +40,7 @@ totp.verify({ secret: SECRET, token }); // $ExpectType boolean totp.TOTP; totp.getClass(); -token = otplib.hotp.generate(SECRET, 1); +token = otplib.hotp.generate(SECRET, 1); // $ExpectType string otplib.hotp.check(token, SECRET, 1); // $ExpectType boolean otplib.hotp.verify({ secret: SECRET, token, counter: 1 }); // $ExpectType boolean otplib.hotp.HOTP; @@ -48,3 +49,17 @@ hotp.check(token, SECRET, 1); // $ExpectType boolean hotp.verify({ secret: SECRET, token, counter: 1 }); // $ExpectType boolean hotp.HOTP; hotp.getClass(); + +const hotpOpt = core.hotpOptions({}); +core.hotpCheck('123', SECRET, 0, hotpOpt); // $ExpectType boolean +core.hotpCounter(0); // $ExpectType string +core.hotpDigest(SECRET, 0, hotpOpt); // $ExpectType string +core.hotpSecret(SECRET, { algorithm: hotpOpt.algorithm, encoding: hotpOpt.encoding }); // $ExpectType Buffer +core.hotpToken(SECRET, 0, hotpOpt); // $ExpectType string + +const totpOpt = core.totpOptions({}); +core.totpCheck('123', SECRET, totpOpt); // $ExpectType boolean +core.totpCheckWithWindow('123', SECRET, totpOpt); // $ExpectType number | null +core.totpCounter(new Date().valueOf(), 0); // $ExpectType number +core.totpSecret(SECRET, { algorithm: totpOpt.algorithm, encoding: totpOpt.encoding }); // $ExpectType Buffer +core.totpToken(SECRET, totpOpt); // $ExpectType string diff --git a/packages/types-ts/tsconfig.json b/packages/types-ts/tsconfig.json index fa1ff72e..60bf52fb 100644 --- a/packages/types-ts/tsconfig.json +++ b/packages/types-ts/tsconfig.json @@ -15,6 +15,9 @@ "otplib": [ "index" ], + "otplib/core": [ + "core" + ], "otplib/hotp": [ "hotp" ],