Skip to content

Commit

Permalink
use new base 32 encode and decode
Browse files Browse the repository at this point in the history
  • Loading branch information
kldeb committed Aug 28, 2024
1 parent c3d9a28 commit 8619bbf
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 21 deletions.
22 changes: 14 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
*/
import * as crypto from 'node:crypto'

/**
* @type {{ encode: (data: string | import('buffer').Buffer) => string, decode: (data: string) => import('buffer').Buffer }}
*/
import * as base32 from 'thirty-two'
import base32Encode from 'base32-encode'
import base32Decode from 'base32-decode'

// SHA1 is not secure, but in the context of TOTPs, it's unrealistic to expect
// security issues. Also, it's the default for compatibility with OTP apps.
Expand Down Expand Up @@ -118,18 +116,18 @@ function verifyHOTP(
* @param {string} [options.charSet='0123456789'] - The character set to use, defaults to the numbers 0-9.
* @param {string} [options.secret] The secret to use for the TOTP. It should be
* base32 encoded (you can use https://npm.im/thirty-two). Defaults to a random
* secret: base32.encode(crypto.randomBytes(10)).toString().
* secret: base32Encode(crypto.randomBytes(10), 'RFC4648').
* @returns {{otp: string, secret: string, period: number, digits: number, algorithm: string, charSet: string}}
* The OTP, secret, and config options used to generate the OTP.
*/
export function generateTOTP({
period = DEFAULT_PERIOD,
digits = DEFAULT_DIGITS,
algorithm = DEFAULT_ALGORITHM,
secret = base32.encode(crypto.randomBytes(10)).toString(),
secret = base32Encode(crypto.randomBytes(10), 'RFC4648'),
charSet = DEFAULT_CHAR_SET,
} = {}) {
const otp = generateHOTP(base32.decode(secret), {
const otp = generateHOTP(base32Decode(secret, 'RFC4648'), {
counter: getCounter(period),
digits,
algorithm,
Expand Down Expand Up @@ -205,7 +203,15 @@ export function verifyTOTP({
charSet,
window = DEFAULT_WINDOW,
}) {
return verifyHOTP(otp, base32.decode(secret), {
let decodedSecret
try {
decodedSecret = base32Decode(secret, 'RFC4648')
} catch (error) {
// If the secret is invalid, return null
return null
}

return verifyHOTP(otp, Buffer.from(decodedSecret), {
counter: getCounter(period),
digits,
window,
Expand Down
7 changes: 5 additions & 2 deletions index.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import assert from 'node:assert'
import { test } from 'node:test'
import base32 from 'thirty-two'
import base32Encode from 'base32-encode'
import { generateTOTP, getTOTPAuthUri, verifyTOTP } from './index.js'

test('OTP can be generated and verified', () => {
Expand All @@ -17,7 +17,10 @@ test('options can be customized', () => {
algorithm: 'SHA256',
period: 60,
digits: 8,
secret: base32.encode(Math.random().toString(16).slice(2)).toString(),
secret: base32Encode(
new TextEncoder().encode(Math.random().toString(16).slice(2)),
'RFC4648'
).toString(),
charSet: 'abcdef',
}
const { otp, ...config } = generateTOTP(options)
Expand Down
53 changes: 43 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
"author": "Kent C. Dodds <[email protected]> (https://kentcdodds.com/)",
"license": "MIT",
"dependencies": {
"thirty-two": "^1.0.2"
"base32-decode": "^1.0.0",
"base32-encode": "^2.0.0"
},
"engines": {
"node": ">=18"
Expand Down

0 comments on commit 8619bbf

Please sign in to comment.