diff --git a/.github/lock.yml b/.github/lock.yml index ea7cf67..74eee94 100644 --- a/.github/lock.yml +++ b/.github/lock.yml @@ -1,5 +1,5 @@ --- -ignoreUnless: {{ STALE_BOT }} +ignoreUnless: { { STALE_BOT } } --- # Configuration for Lock Threads - https://github.com/dessant/lock-threads-app diff --git a/.github/stale.yml b/.github/stale.yml index d21cf6c..8b47808 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,5 +1,5 @@ --- -ignoreUnless: {{ STALE_BOT }} +ignoreUnless: { { STALE_BOT } } --- # Number of days of inactivity before an issue becomes stale daysUntilStale: 60 diff --git a/.npmrc b/.npmrc index 43c97e7..62a81f0 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ package-lock=false +auto-install-peers=true diff --git a/README.md b/README.md index 8f91cce..ec7d410 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## Introduction -Two Factor Authentication using `node-2fa` integration for AdonisJS. +Two Factor Authentication using `2fa-node` integration for AdonisJS. ## Documentation @@ -17,12 +17,9 @@ See documentation at [adonis-2fa.netlify.app](https://adonis-2fa.netlify.app) AdonisJS 2FA is open-sourced software licensed under the [MIT license](LICENSE.md). [gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/nulix-dev/adonis-2fa/test.yml?style=for-the-badge -[gh-workflow-url]: https://github.com/nulix-dev/adonis-2fa/actions/workflows/test.yml "Github action" - +[gh-workflow-url]: https://github.com/nulix-dev/adonis-2fa/actions/workflows/test.yml 'Github action' [npm-image]: https://img.shields.io/npm/v/@nulix/adonis-2fa/latest.svg?style=for-the-badge&logo=npm -[npm-url]: https://www.npmjs.com/package/@nulix/adonis-2fa/v/latest "npm" - +[npm-url]: https://www.npmjs.com/package/@nulix/adonis-2fa/v/latest 'npm' [typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript - [license-url]: LICENSE.md [license-image]: https://img.shields.io/github/license/nulix-dev/adonis-2fa?style=for-the-badge diff --git a/docs/bin/serve.ts b/docs/bin/serve.ts index a5dd7c6..7c4de1d 100644 --- a/docs/bin/serve.ts +++ b/docs/bin/serve.ts @@ -57,7 +57,7 @@ async function defineRoutes(app: ApplicationService) { return response.redirect(redirectsCollection[request.url()]) } - for (let collection of collections) { + for (const collection of collections) { await collection.refresh() const entry = collection.findByPermalink(request.url()) if (entry) { diff --git a/docs/content/.DS_Store b/docs/content/.DS_Store deleted file mode 100644 index 7ed6d65..0000000 Binary files a/docs/content/.DS_Store and /dev/null differ diff --git a/docs/content/docs/introduction.md b/docs/content/docs/introduction.md index 42dc2bc..d2e09a7 100644 --- a/docs/content/docs/introduction.md +++ b/docs/content/docs/introduction.md @@ -4,7 +4,7 @@ summary: Adonis-2FA is a library for managing Two Factor Authentication in your # Introduction -Adonis-2FA is a library for managing Two Factor Authentication in your AdonisJS project build on top of [node-2fa](https://github.com/jeremyscalpello/node-2fa). +Adonis-2FA is a library for managing Two Factor Authentication in your AdonisJS project build on top of [2fa-node](https://github.com/FinotiLucas/2fa-node). The package it self does not store any secret or data on your behalf. It only give you access the methods to implement a two factor authentication flow and create recovery codes. You can store that information inside a database and use the [auth](https://docs.adonisjs.com/guides/auth) package to login the user within your application. diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..6483062 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,13 @@ +import pluginJs from '@eslint/js' +import tseslint from 'typescript-eslint' + +/** @type {import('eslint').Linter.Config[]} */ +export default [ + { files: ['**/*.{js,mjs,cjs,ts}'] }, + { + ignores: ['**/bin/*', '**/docs/*', '**/tests/*', 'tsconfig.json'], + }, + + pluginJs.configs.recommended, + ...tseslint.configs.recommended, +] diff --git a/package.json b/package.json index 77b8aac..94d5843 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "clean": "del-cli build", "copy:templates": "copyfiles \"stubs/**/*.stub\" build", "typecheck": "tsc --noEmit", - "lint": "eslint . --ext=.ts", + "lint": "eslint --fix", "format": "prettier --write .", "test": "node --import=./tsnode.esm.js --enable-source-maps bin/test.ts", "prec8:test": "npm run lint", @@ -38,35 +38,49 @@ "version": "npm run build", "prepublishOnly": "npm run build" }, - "keywords": [ - "adonisjs", - "2fa", - "adonis" - ], - "author": "brunolipe-a, nulix", - "license": "MIT", "devDependencies": { - "@adonisjs/assembler": "^7.0.0", - "@adonisjs/core": "^6.2.0", - "@adonisjs/eslint-config": "^1.2.1", - "@adonisjs/prettier-config": "^1.2.1", - "@adonisjs/tsconfig": "^1.2.1", - "@japa/assert": "^2.1.0", - "@japa/runner": "^3.1.1", - "@swc/core": "^1.3.102", - "@types/node": "^20.10.7", - "c8": "^9.0.0", + "@adonisjs/assembler": "^7.8.2", + "@adonisjs/core": "^6.17.1", + "@adonisjs/eslint-config": "^1.3.0", + "@adonisjs/prettier-config": "^1.4.0", + "@adonisjs/tsconfig": "^1.4.0", + "@eslint/js": "^9.18.0", + "@japa/assert": "^4.0.1", + "@japa/runner": "^4.1.0", + "@swc/core": "^1.10.7", + "@types/node": "^22.10.7", + "c8": "^10.1.3", "copyfiles": "^2.4.1", - "del-cli": "^5.0.0", - "eslint": "^8.38.0", - "np": "^9.2.0", - "prettier": "^3.1.1", + "del-cli": "^6.0.0", + "eslint": "^9.18.0", + "np": "^10.1.0", + "prettier": "^3.4.2", + "prettier-edgejs": "^0.2.34", "ts-node": "^10.9.2", - "typescript": "^5.3.3" + "typescript": "^5.7.3", + "typescript-eslint": "^8.20.0" + }, + "dependencies": { + "2fa-node": "^0.0.5" }, "peerDependencies": { "@adonisjs/core": "^6.2.0" }, + "author": "brunolipe-a, nulix", + "license": "MIT", + "keywords": [ + "adonisjs", + "2fa", + "adonis" + ], + "types": "module", + "eslintConfig": { + "extends": "@adonisjs/eslint-config/package", + "ignorePatterns": [ + "docs/" + ] + }, + "prettier": "@adonisjs/prettier-config", "publishConfig": { "access": "public", "tag": "latest" @@ -87,15 +101,5 @@ "src/**", "!src/types.ts" ] - }, - "eslintConfig": { - "extends": "@adonisjs/eslint-config/package", - "ignorePatterns": [ - "docs/" - ] - }, - "prettier": "@adonisjs/prettier-config", - "dependencies": { - "node-2fa": "^2.0.3" } } diff --git a/providers/two_factor_auth_provider.ts b/providers/two_factor_auth_provider.ts index 61e14a4..645aae0 100644 --- a/providers/two_factor_auth_provider.ts +++ b/providers/two_factor_auth_provider.ts @@ -27,6 +27,7 @@ export default class TwoFactorAuthProvider { /** * Resolve config from the provider */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any const config = await configProvider.resolve(this.app, twoFactorConfigProvider) if (!config) { throw new RuntimeException( diff --git a/src/two_factor_auth_manager.ts b/src/two_factor_auth_manager.ts index 65791c5..b46fd27 100644 --- a/src/two_factor_auth_manager.ts +++ b/src/two_factor_auth_manager.ts @@ -1,4 +1,4 @@ -import * as twoFactor from 'node-2fa' +import * as twoFactor from '2fa-node' import { ResolvedTwoFactorAuthConfig, TwoFactorSecret } from './types.js' import { randomInt } from 'node:crypto' @@ -9,10 +9,11 @@ export class TwoFactorAuthManager { /** * Generate a `Secret` to the given user information */ - generateSecret(userInfo: string): TwoFactorSecret { - return twoFactor.generateSecret({ + async generateSecret(userInfo: string): Promise { + return await twoFactor.generateSecret({ name: this.config.issuer, account: userInfo, + counter: undefined, }) } @@ -29,13 +30,14 @@ export class TwoFactorAuthManager { */ verifyToken(secret: string = '', token: string, recoveryCodes: string[] = []) { const verifyResult = twoFactor.verifyToken(secret, token) + if (!verifyResult) { const isSecretInRecoveryCodes = recoveryCodes.includes(token) return isSecretInRecoveryCodes } - return verifyResult.delta === 0 // Valida token atual, não permitindo token já expirado ou token futuro + return verifyResult } /** diff --git a/tests/two_factor_auth_manager.spec.ts b/tests/two_factor_auth_manager.spec.ts index 3e8b0f5..125e4dc 100644 --- a/tests/two_factor_auth_manager.spec.ts +++ b/tests/two_factor_auth_manager.spec.ts @@ -9,7 +9,7 @@ test.group('TwoFactorAuthManager', () => { const email = 'johndoe@test.com' const manager = new TwoFactorAuthManager({ issuer }) - const twoFactorSecret = manager.generateSecret(email) + const twoFactorSecret = await manager.generateSecret(email) assert.properties(twoFactorSecret, ['secret', 'uri', 'qr']) assert.equal(twoFactorSecret.secret.length, 32) @@ -46,7 +46,7 @@ test.group('TwoFactorAuthManager', () => { test('it should be able to verify a valid secret and OTP', async ({ assert }) => { const manager = new TwoFactorAuthManager({ issuer }) - const { secret } = manager.generateSecret('any') + const { secret } = await manager.generateSecret('any') const token = manager.generateToken(secret)! @@ -58,7 +58,7 @@ test.group('TwoFactorAuthManager', () => { test('it not should be able to verify a valid secret and invalid OTP', async ({ assert }) => { const manager = new TwoFactorAuthManager({ issuer }) - const { secret } = manager.generateSecret('any') + const { secret } = await manager.generateSecret('any') const isValid = manager.verifyToken(secret, 'something') @@ -68,7 +68,7 @@ test.group('TwoFactorAuthManager', () => { test('it not should be able to verify a invalid secret and valid OTP', async ({ assert }) => { const manager = new TwoFactorAuthManager({ issuer }) - const { secret } = manager.generateSecret('any') + const { secret } = await manager.generateSecret('any') const token = manager.generateToken(secret)! diff --git a/tsconfig.json b/tsconfig.json index dd0bbdb..a0a38cc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,10 +1,10 @@ { "extends": "@adonisjs/tsconfig/tsconfig.package.json", "compilerOptions": { + "target": "esnext", + "moduleResolution": "nodenext", "rootDir": "./", - "outDir": "./build", + "outDir": "./build" }, - "exclude": [ - "docs" - ] + "exclude": ["docs"] }