├── .nvmrc ├── .eslintignore ├── src ├── index.ts ├── constants.ts ├── utils.ts ├── types.ts └── generate_api_key.ts ├── .gitignore ├── dist ├── index.d.ts ├── index.d.ts.map ├── index.js.map ├── types.js.map ├── generate_api_key.d.ts.map ├── constants.d.ts.map ├── types.js ├── utils.d.ts.map ├── generate_api_key.d.ts ├── constants.d.ts ├── constants.js.map ├── utils.js.map ├── utils.d.ts ├── constants.js ├── types.d.ts.map ├── utils.js ├── index.js ├── types.d.ts ├── generate_api_key.js.map └── generate_api_key.js ├── .mocharc.json ├── .travis.yml ├── .nycrc.json ├── .editorconfig ├── .cspell.json ├── tsconfig.json ├── .eslintrc.js ├── CHANGELOG.md ├── .github └── workflows │ ├── coverage.yml │ └── test.yml ├── package.json ├── test ├── types │ └── generate_api_key.test-d.ts └── generate_api_key.spec.ts ├── LICENSE └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | v14 -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .nyc_output/ 2 | .vscode/ 3 | coverage/ 4 | dist/ 5 | node_modules/ -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { generateApiKey, default } from './generate_api_key'; 2 | export * from './types'; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage/ 3 | .nyc_output/ 4 | .vscode/ 5 | package-lock.json 6 | yarn.lock 7 | *.log 8 | test/__*__.js -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | export { generateApiKey, default } from './generate_api_key'; 2 | export * from './types'; 3 | //# sourceMappingURL=index.d.ts.map -------------------------------------------------------------------------------- /.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": ["spec.ts"], 3 | "require": ["ts-node/register"], 4 | "reporter": "spec", 5 | "spec": "test/**/*.spec.ts" 6 | } -------------------------------------------------------------------------------- /dist/index.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7D,cAAc,SAAS,CAAC"} -------------------------------------------------------------------------------- /dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,uDAA6D;AAApD,kHAAA,cAAc,OAAA;AAAE,4HAAA,OAAO,OAAA;AAChC,0CAAwB"} -------------------------------------------------------------------------------- /dist/types.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AAAA,+BAA+B;AAClB,QAAA,iBAAiB,GAAG;IAC/B,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,QAAQ;CACA,CAAC"} -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "14" 4 | - "16" 5 | - "lts/*" 6 | script: 7 | - npm run test:lint 8 | - npm run test:spelling 9 | - npm run test:types 10 | - npm run test:cov 11 | after_success: 12 | - npm run test:coveralls -------------------------------------------------------------------------------- /.nycrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "src/**/*.ts" 4 | ], 5 | "exclude": [], 6 | "reporter": [ 7 | "text", 8 | "html", 9 | "lcovonly" 10 | ], 11 | "extension": [ 12 | ".ts" 13 | ], 14 | "all": false, 15 | "cache": true 16 | } -------------------------------------------------------------------------------- /dist/generate_api_key.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"generate_api_key.d.ts","sourceRoot":"","sources":["../src/generate_api_key.ts"],"names":[],"mappings":"AAUA,OAAO,EAGL,iBAAiB,EACjB,aAAa,EAMd,MAAM,SAAS,CAAC;AAkNjB;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,aAAa,iBAAiB,KAAQ,aAgDhE,CAAC;AAEF,eAAe,cAAc,CAAC"} -------------------------------------------------------------------------------- /dist/constants.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,eAAO,MAAM,sBAAsB,QAKzB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,gBAAgB,QAInB,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;GAEG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC"} -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | insert_final_newline = true 10 | 11 | [*.json] 12 | insert_final_newline = false 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | insert_final_newline = false 17 | -------------------------------------------------------------------------------- /dist/types.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.generationMethods = void 0; 4 | // Array of generation methods. 5 | exports.generationMethods = [ 6 | 'string', 7 | 'bytes', 8 | 'base32', 9 | 'base62', 10 | 'uuidv4', 11 | 'uuidv5', 12 | ]; 13 | //# sourceMappingURL=types.js.map -------------------------------------------------------------------------------- /dist/utils.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,SAAS,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,UAAW,GAAG,KAAG,OAErC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,YAAY,UAAW,GAAG,KAAG,OASzC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,YACZ,iBAAiB,YAChB,wBAAwB,KACjC,iBAEF,CAAC"} -------------------------------------------------------------------------------- /dist/generate_api_key.d.ts: -------------------------------------------------------------------------------- 1 | import { GenerationOptions, ApiKeyResults } from './types'; 2 | /** 3 | * Generates a simple API key or a batch of API keys based on 4 | * the provided generation method. 5 | * 6 | * @param options The API key generation options. 7 | * @returns The API key or array of API keys. 8 | */ 9 | export declare const generateApiKey: (options?: GenerationOptions) => ApiKeyResults; 10 | export default generateApiKey; 11 | //# sourceMappingURL=generate_api_key.d.ts.map -------------------------------------------------------------------------------- /dist/constants.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The default character pool that is used 3 | * when generating an API key. 4 | */ 5 | export declare const DEFAULT_CHARACTER_POOL: string; 6 | /** 7 | * Base62 character pool. 8 | */ 9 | export declare const BASE62_CHAR_POOL: string; 10 | /** 11 | * The default minimum length for an API key. 12 | */ 13 | export declare const DEFAULT_MIN_LENGTH = 16; 14 | /** 15 | * The default maximum length for an API key. 16 | */ 17 | export declare const DEFAULT_MAX_LENGTH = 32; 18 | //# sourceMappingURL=constants.d.ts.map -------------------------------------------------------------------------------- /.cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2", 3 | "language": "en", 4 | "files": [ 5 | "*.md", 6 | "*.js", 7 | "src/**/*.ts", 8 | "test/**/*.spec.ts", 9 | "test/**/*.test-d.ts", 10 | ".github/workflows/*.yml" 11 | ], 12 | "words": [ 13 | "uuidv", 14 | "codecov", 15 | "lcov" 16 | ], 17 | "allowCompoundWords": true, 18 | "ignorePaths": [ 19 | "package.json", 20 | "package-lock.json", 21 | "yarn.lock", 22 | "tsconfig.json", 23 | "node_modules/**" 24 | ], 25 | "ignoreRegExpList": [ 26 | "/'[^\\s]{20,}'/", 27 | "/'ABCDEFGHI'/" 28 | ] 29 | } -------------------------------------------------------------------------------- /dist/constants.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAA,mBAAmB;AACnB,MAAM,eAAe,GAAG,4BAA4B,CAAC;AACrD,MAAM,eAAe,GAAG,eAAe,CAAC,WAAW,EAAE,CAAC;AACtD,MAAM,gBAAgB,GAAG,YAAY,CAAC;AACtC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAEnC;;;GAGG;AACU,QAAA,sBAAsB,GAAG;IACpC,GAAG,eAAe;IAClB,GAAG,eAAe;IAClB,GAAG,gBAAgB;IACnB,GAAG,iBAAiB;CACrB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAEX;;GAEG;AACU,QAAA,gBAAgB,GAAG;IAC9B,GAAG,gBAAgB;IACnB,GAAG,eAAe;IAClB,GAAG,eAAe;CACnB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAEX;;GAEG;AACU,QAAA,kBAAkB,GAAG,EAAE,CAAC;AAErC;;GAEG;AACU,QAAA,kBAAkB,GAAG,EAAE,CAAC"} -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2017", 4 | "module": "commonjs", 5 | "pretty": true, 6 | "declaration": true, 7 | "declarationMap": true, 8 | "sourceMap": true, 9 | "outDir": "./dist", 10 | "baseUrl": "./src", 11 | "strict": false, 12 | "noUnusedLocals": true, 13 | "noUnusedParameters": true, 14 | "noImplicitReturns": false, 15 | "noFallthroughCasesInSwitch": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "allowSyntheticDefaultImports": true, 18 | "moduleResolution": "node", 19 | "esModuleInterop": true 20 | }, 21 | "include": [ 22 | "src/**/*.ts" 23 | ] 24 | } -------------------------------------------------------------------------------- /dist/utils.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;AAEA;;;;;GAKG;AACI,MAAM,QAAQ,GAAG,CAAC,KAAU,EAAW,EAAE;IAC9C,OAAO,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,YAAY,MAAM,CAAC,CAAC;AAChE,CAAC,CAAC;AAFW,QAAA,QAAQ,YAEnB;AAEF;;;;;GAKG;AACI,MAAM,YAAY,GAAG,CAAC,KAAU,EAAW,EAAE;IAClD,kBAAkB;IAClB,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;QAC7B,OAAO,KAAK,CAAC;KACd;SAAM;QACL,OAAO,CAAC,KAAK,IAAI,GAAG,CAAC;YACnB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,QAAQ;YACrD,CAAC,CAAC,KAAK,CAAC;KACX;AACH,CAAC,CAAC;AATW,QAAA,YAAY,gBASvB;AAEF;;;;;;;GAOG;AACI,MAAM,UAAU,GAAG,CACxB,OAA0B,EAC1B,QAAkC,EACf,EAAE;IACrB,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAC9C,CAAC,CAAC;AALW,QAAA,UAAU,cAKrB"} -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | // Character pools. 2 | const LOWER_CHAR_POOL = 'abcdefghijklmnopqrstuvwxyz'; 3 | const UPPER_CHAR_POOL = LOWER_CHAR_POOL.toUpperCase(); 4 | const NUMBER_CHAR_POOL = '0123456789'; 5 | const SPECIAL_CHAR_POOL = '-._~+/'; 6 | 7 | /** 8 | * The default character pool that is used 9 | * when generating an API key. 10 | */ 11 | export const DEFAULT_CHARACTER_POOL = [ 12 | ...LOWER_CHAR_POOL, 13 | ...UPPER_CHAR_POOL, 14 | ...NUMBER_CHAR_POOL, 15 | ...SPECIAL_CHAR_POOL, 16 | ].join(''); 17 | 18 | /** 19 | * Base62 character pool. 20 | */ 21 | export const BASE62_CHAR_POOL = [ 22 | ...NUMBER_CHAR_POOL, 23 | ...LOWER_CHAR_POOL, 24 | ...UPPER_CHAR_POOL, 25 | ].join(''); 26 | 27 | /** 28 | * The default minimum length for an API key. 29 | */ 30 | export const DEFAULT_MIN_LENGTH = 16; 31 | 32 | /** 33 | * The default maximum length for an API key. 34 | */ 35 | export const DEFAULT_MAX_LENGTH = 32; -------------------------------------------------------------------------------- /dist/utils.d.ts: -------------------------------------------------------------------------------- 1 | import { GenerationOptions, DefaultGenerationOptions } from './types'; 2 | /** 3 | * Checks for a string value. 4 | * 5 | * @param value The value to check. 6 | * @returns Whether the value is a string or not. 7 | */ 8 | export declare const isString: (value: any) => boolean; 9 | /** 10 | * Checks for a natural number. 11 | * 12 | * @param value The value to check. 13 | * @returns Whether the value is a natural number or not. 14 | */ 15 | export declare const isNaturalNum: (value: any) => boolean; 16 | /** 17 | * Gets the options for the generation method. Uses 18 | * default values if certain options are missing. 19 | * 20 | * @param options The provided options. 21 | * @param defaults The default options. 22 | * @returns The modified options. 23 | */ 24 | export declare const getOptions: (options: GenerationOptions, defaults: DefaultGenerationOptions) => GenerationOptions; 25 | //# sourceMappingURL=utils.d.ts.map -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | plugins: [ 5 | '@typescript-eslint' 6 | ], 7 | extends: [ 8 | 'eslint:recommended', 9 | 'plugin:@typescript-eslint/eslint-recommended', 10 | 'plugin:@typescript-eslint/recommended', 11 | ], 12 | rules: { 13 | 'no-console': 'error', 14 | 'max-len': [ 'error', 120, 2, { 15 | ignoreUrls: true, 16 | ignoreComments: false, 17 | ignoreRegExpLiterals: true, 18 | ignoreStrings: true, 19 | ignoreTemplateLiterals: true, 20 | }], 21 | 'semi': [ 'error', 'always' ], 22 | '@typescript-eslint/no-explicit-any': 'off', 23 | }, 24 | overrides: [ 25 | { 26 | files: [ 'test/generate_api_key.spec.ts' ], 27 | rules: { 28 | '@typescript-eslint/no-var-requires': 'off', 29 | '@typescript-eslint/ban-ts-comment': [ 30 | 'error', 31 | { 32 | 'ts-ignore': false, 33 | }, 34 | ], 35 | }, 36 | } 37 | ] 38 | }; -------------------------------------------------------------------------------- /dist/constants.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.DEFAULT_MAX_LENGTH = exports.DEFAULT_MIN_LENGTH = exports.BASE62_CHAR_POOL = exports.DEFAULT_CHARACTER_POOL = void 0; 4 | // Character pools. 5 | const LOWER_CHAR_POOL = 'abcdefghijklmnopqrstuvwxyz'; 6 | const UPPER_CHAR_POOL = LOWER_CHAR_POOL.toUpperCase(); 7 | const NUMBER_CHAR_POOL = '0123456789'; 8 | const SPECIAL_CHAR_POOL = '-._~+/'; 9 | /** 10 | * The default character pool that is used 11 | * when generating an API key. 12 | */ 13 | exports.DEFAULT_CHARACTER_POOL = [ 14 | ...LOWER_CHAR_POOL, 15 | ...UPPER_CHAR_POOL, 16 | ...NUMBER_CHAR_POOL, 17 | ...SPECIAL_CHAR_POOL, 18 | ].join(''); 19 | /** 20 | * Base62 character pool. 21 | */ 22 | exports.BASE62_CHAR_POOL = [ 23 | ...NUMBER_CHAR_POOL, 24 | ...LOWER_CHAR_POOL, 25 | ...UPPER_CHAR_POOL, 26 | ].join(''); 27 | /** 28 | * The default minimum length for an API key. 29 | */ 30 | exports.DEFAULT_MIN_LENGTH = 16; 31 | /** 32 | * The default maximum length for an API key. 33 | */ 34 | exports.DEFAULT_MAX_LENGTH = 32; 35 | //# sourceMappingURL=constants.js.map -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [v1.0.2](https://github.com/pauldenver/generate-api-key/compare/v1.0.1...v1.0.2) 6 | 7 | ### Bug Fixes 8 | 9 | * Updated how the key length is determined when using the `bytes` method ([#3](https://github.com/pauldenver/generate-api-key/issues/3)). 10 | * Fixed a typo in the `chance.natural` usage in the `string` method. 11 | 12 | ## [v1.0.1](https://github.com/pauldenver/generate-api-key/compare/v1.0.0...v1.0.1) 13 | 14 | ### Features 15 | 16 | * Converted the project from JavaScript to TypeScript (also addresses [#1](https://github.com/pauldenver/generate-api-key/issues/1)). 17 | * Updated module dependencies to their current versions. 18 | 19 | ### Minor changes 20 | 21 | * Removed the `codecov` NPM module dependency and its associated NPM script. The module has been deprecated. 22 | * Added config files for `mocha` and `nyc`. 23 | 24 | ### Breaking changes 25 | 26 | * Updated the supported Node.js engine to versions 14 or greater. 27 | 28 | ## [v1.0.0](https://github.com/pauldenver/generate-api-key/commit/730c8b2a99d66fa68fb199161422d990228a1161) 29 | 30 | * Initial `generate-api-key` library release. -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { GenerationOptions, DefaultGenerationOptions } from './types'; 2 | 3 | /** 4 | * Checks for a string value. 5 | * 6 | * @param value The value to check. 7 | * @returns Whether the value is a string or not. 8 | */ 9 | export const isString = (value: any): boolean => { 10 | return (typeof value === 'string' || value instanceof String); 11 | }; 12 | 13 | /** 14 | * Checks for a natural number. 15 | * 16 | * @param value The value to check. 17 | * @returns Whether the value is a natural number or not. 18 | */ 19 | export const isNaturalNum = (value: any): boolean => { 20 | // Check the type. 21 | if (typeof value !== 'number') { 22 | return false; 23 | } else { 24 | return (value >= 0.0) 25 | ? (Math.floor(value) === value) && value !== Infinity 26 | : false; 27 | } 28 | }; 29 | 30 | /** 31 | * Gets the options for the generation method. Uses 32 | * default values if certain options are missing. 33 | * 34 | * @param options The provided options. 35 | * @param defaults The default options. 36 | * @returns The modified options. 37 | */ 38 | export const getOptions = ( 39 | options: GenerationOptions, 40 | defaults: DefaultGenerationOptions 41 | ): GenerationOptions => { 42 | return Object.assign({}, defaults, options); 43 | }; -------------------------------------------------------------------------------- /dist/types.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,iBAAiB,sEAOpB,CAAC;AAEX;;GAEG;AACF,oBAAY,gBAAgB,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnE;;GAEG;AACH,UAAU,uBAAuB;IAC/B,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,uBAAuB;IACtE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,sBAAuB,SAAQ,uBAAuB;IACrE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,uBAAuB;IACtE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,oBAAY,uBAAuB,GAAG,uBAAuB,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,uBAAuB;IACtE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,uBAAuB;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,oBAAY,iBAAiB,GACzB,uBAAuB,GACvB,sBAAsB,GACtB,uBAAuB,GACvB,uBAAuB,GACvB,uBAAuB,GACvB,uBAAuB,CAAC;AAE3B;;GAEG;AACJ,MAAM,WAAW,wBAAwB;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,oBAAY,aAAa,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC"} -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Coverage 2 | 3 | on: 4 | push: 5 | pull_request: 6 | types: 7 | - opened 8 | - synchronize 9 | - reopened 10 | - ready_for_review 11 | - converted_to_draft 12 | branches: 13 | - main 14 | 15 | permissions: 16 | contents: read 17 | 18 | concurrency: 19 | group: ${{ github.workflow }}-${{ github.ref }} 20 | cancel-in-progress: true 21 | 22 | jobs: 23 | coverage: 24 | runs-on: ${{ matrix.os }} 25 | 26 | strategy: 27 | matrix: 28 | node-version: 29 | - 14.x 30 | - 16.x 31 | - 18.x 32 | os: 33 | - ubuntu-latest 34 | 35 | steps: 36 | - name: ⬇️ Checking out ${{ github.ref_name }} 37 | uses: actions/checkout@v3 38 | 39 | - name: ⎔ Using Node.js ${{ matrix.node-version }} on ${{ matrix.os }} 40 | uses: actions/setup-node@v3 41 | with: 42 | node-version: ${{ matrix.node-version }} 43 | - run: npm i -g npm@8 44 | - run: npm -v 45 | 46 | - name: 📥 Install dependencies 47 | run: npm install 48 | 49 | - name: 📝 Generate coverage report 50 | run: npm run test:cov 51 | 52 | - name: 📤 Upload coverage to Coveralls 53 | uses: coverallsapp/github-action@master 54 | with: 55 | github-token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /dist/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.getOptions = exports.isNaturalNum = exports.isString = void 0; 4 | /** 5 | * Checks for a string value. 6 | * 7 | * @param value The value to check. 8 | * @returns Whether the value is a string or not. 9 | */ 10 | const isString = (value) => { 11 | return (typeof value === 'string' || value instanceof String); 12 | }; 13 | exports.isString = isString; 14 | /** 15 | * Checks for a natural number. 16 | * 17 | * @param value The value to check. 18 | * @returns Whether the value is a natural number or not. 19 | */ 20 | const isNaturalNum = (value) => { 21 | // Check the type. 22 | if (typeof value !== 'number') { 23 | return false; 24 | } 25 | else { 26 | return (value >= 0.0) 27 | ? (Math.floor(value) === value) && value !== Infinity 28 | : false; 29 | } 30 | }; 31 | exports.isNaturalNum = isNaturalNum; 32 | /** 33 | * Gets the options for the generation method. Uses 34 | * default values if certain options are missing. 35 | * 36 | * @param options The provided options. 37 | * @param defaults The default options. 38 | * @returns The modified options. 39 | */ 40 | const getOptions = (options, defaults) => { 41 | return Object.assign({}, defaults, options); 42 | }; 43 | exports.getOptions = getOptions; 44 | //# sourceMappingURL=utils.js.map -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __exportStar = (this && this.__exportStar) || function(m, exports) { 14 | for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); 15 | }; 16 | var __importDefault = (this && this.__importDefault) || function (mod) { 17 | return (mod && mod.__esModule) ? mod : { "default": mod }; 18 | }; 19 | Object.defineProperty(exports, "__esModule", { value: true }); 20 | exports.default = exports.generateApiKey = void 0; 21 | var generate_api_key_1 = require("./generate_api_key"); 22 | Object.defineProperty(exports, "generateApiKey", { enumerable: true, get: function () { return generate_api_key_1.generateApiKey; } }); 23 | Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(generate_api_key_1).default; } }); 24 | __exportStar(require("./types"), exports); 25 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | pull_request: 6 | types: 7 | - opened 8 | - synchronize 9 | - reopened 10 | - ready_for_review 11 | - converted_to_draft 12 | branches: 13 | - main 14 | 15 | permissions: 16 | contents: read 17 | 18 | concurrency: 19 | group: ${{ github.workflow }}-${{ github.ref }} 20 | cancel-in-progress: true 21 | 22 | jobs: 23 | test: 24 | runs-on: ${{ matrix.os }} 25 | 26 | strategy: 27 | matrix: 28 | node-version: 29 | - 14.x 30 | - 16.x 31 | - 18.x 32 | os: 33 | - ubuntu-latest 34 | 35 | steps: 36 | - name: ⬇️ Checking out ${{ github.ref_name }} 37 | uses: actions/checkout@v3 38 | 39 | - name: ⎔ Using Node.js ${{ matrix.node-version }} on ${{ matrix.os }} 40 | uses: actions/setup-node@v3 41 | with: 42 | node-version: ${{ matrix.node-version }} 43 | - run: npm i -g npm@8 44 | - run: npm -v 45 | 46 | - name: 📥 Install dependencies 47 | run: npm install 48 | 49 | - name: 🧹 Run the linter 50 | run: npm run test:lint 51 | 52 | - name: 🔤 Check spelling 53 | run: npm run test:spelling 54 | 55 | - name: 🔎 Check the Typescript types 56 | run: npm run test:types 57 | 58 | - name: 👟 Run tests 59 | run: | 60 | npm run test:unit 61 | npm run test:unit:build -------------------------------------------------------------------------------- /dist/types.d.ts: -------------------------------------------------------------------------------- 1 | export declare const generationMethods: readonly ["string", "bytes", "base32", "base62", "uuidv4", "uuidv5"]; 2 | /** 3 | * Generation methods for key/token generation. 4 | */ 5 | export declare type GenerationMethod = (typeof generationMethods)[number]; 6 | /** 7 | * API key base generation options. 8 | */ 9 | interface __BaseGenerationOptions { 10 | method?: GenerationMethod; 11 | prefix?: string; 12 | batch?: number; 13 | } 14 | /** 15 | * Generation options for the "string" method. 16 | */ 17 | export interface StringGenerationOptions extends __BaseGenerationOptions { 18 | min?: number; 19 | max?: number; 20 | length?: number; 21 | pool?: string; 22 | } 23 | /** 24 | * Generation options for the "bytes" method. 25 | */ 26 | export interface BytesGenerationOptions extends __BaseGenerationOptions { 27 | min?: number; 28 | max?: number; 29 | length?: number; 30 | } 31 | /** 32 | * Generation options for the "base32" method. 33 | */ 34 | export interface Base32GenerationOptions extends __BaseGenerationOptions { 35 | dashes?: boolean; 36 | } 37 | /** 38 | * Generation options for the "base62" method. 39 | */ 40 | export declare type Base62GenerationOptions = __BaseGenerationOptions; 41 | /** 42 | * Generation options for the "uuidv4" method. 43 | */ 44 | export interface UuidV4GenerationOptions extends __BaseGenerationOptions { 45 | dashes?: boolean; 46 | } 47 | /** 48 | * Generation options for the "uuidv5" method. 49 | */ 50 | export interface UuidV5GenerationOptions extends __BaseGenerationOptions { 51 | name?: string; 52 | namespace?: string; 53 | dashes?: boolean; 54 | } 55 | /** 56 | * API key/token generation options. 57 | */ 58 | export declare type GenerationOptions = StringGenerationOptions | BytesGenerationOptions | Base32GenerationOptions | Base62GenerationOptions | UuidV4GenerationOptions | UuidV5GenerationOptions; 59 | /** 60 | * Default generation options. 61 | */ 62 | export interface DefaultGenerationOptions { 63 | min?: number; 64 | max?: number; 65 | pool?: string; 66 | dashes?: boolean; 67 | } 68 | /** 69 | * Results from the API key/token generation. 70 | */ 71 | export declare type ApiKeyResults = string | string[]; 72 | export {}; 73 | //# sourceMappingURL=types.d.ts.map -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | // Array of generation methods. 2 | export const generationMethods = [ 3 | 'string', 4 | 'bytes', 5 | 'base32', 6 | 'base62', 7 | 'uuidv4', 8 | 'uuidv5', 9 | ] as const; 10 | 11 | /** 12 | * Generation methods for key/token generation. 13 | */ 14 | export type GenerationMethod = (typeof generationMethods)[number]; 15 | 16 | /** 17 | * API key base generation options. 18 | */ 19 | interface __BaseGenerationOptions { 20 | method?: GenerationMethod; 21 | prefix?: string; 22 | batch?: number; 23 | } 24 | 25 | /** 26 | * Generation options for the "string" method. 27 | */ 28 | export interface StringGenerationOptions extends __BaseGenerationOptions { 29 | min?: number; 30 | max?: number; 31 | length?: number; 32 | pool?: string; 33 | } 34 | 35 | /** 36 | * Generation options for the "bytes" method. 37 | */ 38 | export interface BytesGenerationOptions extends __BaseGenerationOptions { 39 | min?: number; 40 | max?: number; 41 | length?: number; 42 | } 43 | 44 | /** 45 | * Generation options for the "base32" method. 46 | */ 47 | export interface Base32GenerationOptions extends __BaseGenerationOptions { 48 | dashes?: boolean; 49 | } 50 | 51 | /** 52 | * Generation options for the "base62" method. 53 | */ 54 | export type Base62GenerationOptions = __BaseGenerationOptions; 55 | 56 | /** 57 | * Generation options for the "uuidv4" method. 58 | */ 59 | export interface UuidV4GenerationOptions extends __BaseGenerationOptions { 60 | dashes?: boolean; 61 | } 62 | 63 | /** 64 | * Generation options for the "uuidv5" method. 65 | */ 66 | export interface UuidV5GenerationOptions extends __BaseGenerationOptions { 67 | name?: string; 68 | namespace?: string; 69 | dashes?: boolean; 70 | } 71 | 72 | /** 73 | * API key/token generation options. 74 | */ 75 | export type GenerationOptions = 76 | | StringGenerationOptions 77 | | BytesGenerationOptions 78 | | Base32GenerationOptions 79 | | Base62GenerationOptions 80 | | UuidV4GenerationOptions 81 | | UuidV5GenerationOptions; 82 | 83 | /** 84 | * Default generation options. 85 | */ 86 | export interface DefaultGenerationOptions { 87 | min?: number; 88 | max?: number; 89 | pool?: string; 90 | dashes?: boolean; 91 | } 92 | 93 | /** 94 | * Results from the API key/token generation. 95 | */ 96 | export type ApiKeyResults = string | string[]; 97 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "generate-api-key", 3 | "description": "A library for generating random API key/access tokens", 4 | "version": "1.0.2", 5 | "main": "./dist/index.js", 6 | "types": "./dist/index.d.ts", 7 | "keywords": [ 8 | "REST", 9 | "RESTful", 10 | "API", 11 | "access", 12 | "token", 13 | "key" 14 | ], 15 | "author": "Paul Proctor ", 16 | "homepage": "https://github.com/pauldenver/generate-api-key", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/pauldenver/generate-api-key.git" 20 | }, 21 | "bugs": { 22 | "url": "https://github.com/pauldenver/generate-api-key/issues" 23 | }, 24 | "files": [ 25 | "dist" 26 | ], 27 | "scripts": { 28 | "build": "run-s build:clean build:main", 29 | "build:main": "tsc -p tsconfig.json", 30 | "build:clean": "shx rm -rf ./dist ./coverage ./.nyc_output", 31 | "build:watch": "tsc -p tsconfig.json -w", 32 | "lint:fix": "eslint ./{src,test}/**/*.ts --fix", 33 | "test:lint": "eslint ./{src,test}/**/*.ts", 34 | "test:unit": "mocha --config .mocharc.json", 35 | "test:unit:build": "cross-env TEST_BUILD=true mocha --config .mocharc.json", 36 | "test:cov": "nyc mocha --config .mocharc.json", 37 | "test:coveralls": "nyc report --reporter=text-lcov | coveralls", 38 | "test:spelling": "cspell -c .cspell.json", 39 | "test:types": "tsd" 40 | }, 41 | "dependencies": { 42 | "base-x": "^4.0.0", 43 | "chance": "^1.1.8", 44 | "rfc4648": "^1.5.2", 45 | "uuid": "^8.3.2" 46 | }, 47 | "devDependencies": { 48 | "@types/chai": "^4.3.1", 49 | "@types/chance": "^1.1.3", 50 | "@types/mocha": "^9.1.1", 51 | "@types/node": "^18.0.6", 52 | "@types/uuid": "^8.3.4", 53 | "@typescript-eslint/eslint-plugin": "^5.30.6", 54 | "@typescript-eslint/parser": "^5.30.6", 55 | "chai": "^4.3.6", 56 | "coveralls": "^3.1.1", 57 | "cross-env": "^7.0.3", 58 | "cspell": "^6.3.0", 59 | "eslint": "^8.20.0", 60 | "eslint-plugin-import": "^2.26.0", 61 | "mocha": "^10.0.0", 62 | "npm-run-all": "^4.1.5", 63 | "nyc": "^15.1.0", 64 | "shx": "^0.3.4", 65 | "ts-node": "^10.9.1", 66 | "tsd": "^0.22.0", 67 | "typescript": "^4.7.4" 68 | }, 69 | "tsd": { 70 | "directory": "test/types" 71 | }, 72 | "engines": { 73 | "node": ">=14" 74 | }, 75 | "license": "Apache-2.0" 76 | } 77 | -------------------------------------------------------------------------------- /test/types/generate_api_key.test-d.ts: -------------------------------------------------------------------------------- 1 | import { 2 | expectType, 3 | expectNotType, 4 | expectAssignable, 5 | expectNotAssignable 6 | } from 'tsd'; 7 | import { 8 | generateApiKey, 9 | generationMethods, 10 | GenerationMethod, 11 | ApiKeyResults, 12 | StringGenerationOptions, 13 | BytesGenerationOptions, 14 | Base32GenerationOptions, 15 | Base62GenerationOptions, 16 | UuidV4GenerationOptions, 17 | UuidV5GenerationOptions 18 | } from '../../src'; 19 | 20 | // Check the generation methods. 21 | generationMethods.forEach((m) => expectType(m)); 22 | 23 | expectNotType('other'); 24 | 25 | // Check the 'string' generation methods. 26 | expectAssignable({ 27 | method: 'string' 28 | }); 29 | 30 | expectAssignable({ 31 | method: 'string', 32 | length: 25 33 | }); 34 | 35 | expectAssignable({ 36 | method: 'string', 37 | prefix: 'test', 38 | min: 8, 39 | max: 32 40 | }); 41 | 42 | expectAssignable({ 43 | method: 'string', 44 | length: 25, 45 | pool: 'ABCDEFGHI', 46 | batch: 5, 47 | prefix: 'test', 48 | min: 8, 49 | max: 24, 50 | }); 51 | 52 | expectNotAssignable({ 53 | method: 'string', 54 | dashes: true 55 | }); 56 | 57 | // Check the 'bytes' generation methods. 58 | expectAssignable({ 59 | method: 'bytes' 60 | }); 61 | 62 | expectAssignable({ 63 | method: 'bytes', 64 | length: 10 65 | }); 66 | 67 | expectAssignable({ 68 | method: 'bytes', 69 | length: 10, 70 | prefix: 'test', 71 | batch: 2 72 | }); 73 | 74 | expectNotAssignable({ 75 | method: 'bytes', 76 | pool: 'ABCDEFGHI' 77 | }); 78 | 79 | // Check the 'base32' generation methods. 80 | expectAssignable({ 81 | method: 'base32' 82 | }); 83 | 84 | expectAssignable({ 85 | method: 'base32', 86 | dashes: true 87 | }); 88 | 89 | expectAssignable({ 90 | method: 'base32', 91 | dashes: false, 92 | prefix: 'test', 93 | batch: 5 94 | }); 95 | 96 | expectNotAssignable({ 97 | method: 'base32', 98 | length: 12 99 | }); 100 | 101 | // Check the 'base62' generation methods. 102 | expectAssignable({ 103 | method: 'base62' 104 | }); 105 | 106 | expectAssignable({ 107 | method: 'base62', 108 | prefix: 'test', 109 | batch: 4 110 | }); 111 | 112 | expectNotAssignable({ 113 | method: 'base62', 114 | max: 15 115 | }); 116 | 117 | // Check the 'uuidv4' generation methods. 118 | expectAssignable({ 119 | method: 'uuidv4' 120 | }); 121 | 122 | expectAssignable({ 123 | method: 'uuidv4', 124 | dashes: true 125 | }); 126 | 127 | expectAssignable({ 128 | method: 'uuidv4', 129 | dashes: false, 130 | prefix: 'test', 131 | batch: 8 132 | }); 133 | 134 | expectNotAssignable({ 135 | method: 'uuidv4', 136 | min: 5 137 | }); 138 | 139 | // Check the 'uuidv5' generation methods. 140 | expectAssignable({ 141 | method: 'uuidv5', 142 | name: 'Testing', 143 | namespace: 'UUID' 144 | }); 145 | 146 | expectAssignable({ 147 | method: 'uuidv5', 148 | name: 'Testing', 149 | namespace: 'UUID', 150 | dashes: true 151 | }); 152 | 153 | expectAssignable({ 154 | method: 'uuidv5', 155 | name: 'Testing', 156 | namespace: 'UUID', 157 | dashes: true, 158 | prefix: 'test', 159 | batch: 3 160 | }); 161 | 162 | expectNotAssignable({ 163 | method: 'uuidv5', 164 | name: 'Testing', 165 | namespace: 'UUID', 166 | pool: 'ABCDEFGHI' 167 | }); 168 | 169 | expectAssignable('some-key'); 170 | expectAssignable([ 'key1', 'key2' ]); 171 | expectNotAssignable(8743); 172 | 173 | // Check the generation results. 174 | expectType(generateApiKey()); 175 | 176 | generationMethods.forEach((m) => { 177 | expectType(generateApiKey({ method: m })); 178 | }); -------------------------------------------------------------------------------- /dist/generate_api_key.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"generate_api_key.js","sourceRoot":"","sources":["../src/generate_api_key.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/generate_api_key.ts: -------------------------------------------------------------------------------- 1 | import Chance from 'chance'; 2 | import { 3 | v4 as uuidv4, 4 | v5 as uuidv5, 5 | validate as validateUUID, 6 | V4Options 7 | } from 'uuid'; 8 | import { randomBytes } from 'crypto'; 9 | import { base32 } from 'rfc4648'; 10 | import baseX from 'base-x'; 11 | import { 12 | generationMethods, 13 | GenerationMethod, 14 | GenerationOptions, 15 | ApiKeyResults, 16 | StringGenerationOptions, 17 | BytesGenerationOptions, 18 | Base32GenerationOptions, 19 | UuidV4GenerationOptions, 20 | UuidV5GenerationOptions 21 | } from './types'; 22 | import { 23 | isString, 24 | isNaturalNum, 25 | getOptions 26 | } from './utils'; 27 | import { 28 | DEFAULT_CHARACTER_POOL, 29 | BASE62_CHAR_POOL, 30 | DEFAULT_MIN_LENGTH, 31 | DEFAULT_MAX_LENGTH 32 | } from './constants'; 33 | 34 | /** 35 | * Creates an API key using random bytes. 36 | * 37 | * @param options API key options. 38 | * @returns The API key. 39 | */ 40 | const getCryptoApiKey = (options: BytesGenerationOptions): string => { 41 | let apiKey: string; 42 | 43 | // Get the options. 44 | options = getOptions(options, { 45 | min: DEFAULT_MIN_LENGTH, 46 | max: DEFAULT_MAX_LENGTH, 47 | }); 48 | 49 | // Get a 'Chance' instance. 50 | const chance = new Chance(); 51 | 52 | // Determine the length for the key. 53 | const length = options.length ?? chance.natural({ 54 | min: options.min, 55 | max: options.max 56 | }); 57 | 58 | // Set the total bytes. 59 | const totalBytes = Math.ceil(length / 2); 60 | 61 | // Generate the API key. 62 | apiKey = randomBytes(totalBytes).toString('hex'); 63 | 64 | // Check the key length. 65 | if (apiKey.length > length) { 66 | const endIndex = apiKey.length - (apiKey.length - length); 67 | apiKey = apiKey.slice(0, endIndex); 68 | } 69 | 70 | return apiKey; 71 | }; 72 | 73 | /** 74 | * Creates an API key using random string generation. 75 | * 76 | * @param options API key options. 77 | * @returns The API key. 78 | */ 79 | const getRandomStringApiKey = (options: StringGenerationOptions): string => { 80 | // Get the options. 81 | options = getOptions(options, { 82 | min: DEFAULT_MIN_LENGTH, 83 | max: DEFAULT_MAX_LENGTH, 84 | pool: DEFAULT_CHARACTER_POOL, 85 | }); 86 | 87 | // Get a 'Chance' instance. 88 | const chance = new Chance(); 89 | 90 | // Determine the length for the key. 91 | const length = options.length ?? chance.natural({ 92 | min: options.min, 93 | max: options.max 94 | }); 95 | 96 | // Generate the string. 97 | return chance.string({ length, pool: options.pool }); 98 | }; 99 | 100 | /** 101 | * Creates an API key using Base32 Crockford encoding. 102 | * 103 | * @param options API key options. 104 | * @returns The API key. 105 | */ 106 | const getBase32CrockfordApiKey = (options: Base32GenerationOptions): string => { 107 | // Get the options. 108 | options = getOptions(options, { dashes: true }); 109 | 110 | // Create the uuid options. 111 | const v4options: V4Options = { 112 | random: randomBytes(16), 113 | }; 114 | 115 | // Create a new UUID. 116 | const uuid = uuidv4(v4options); 117 | // Split at the dashes. 118 | const uuidParts = uuid.split('-'); 119 | 120 | // Convert the UUID into 4 equally separate parts. 121 | const partsArr = [ 122 | uuidParts[0], 123 | `${uuidParts[1]}${uuidParts[2]}`, 124 | `${uuidParts[3]}${uuidParts[4].substring(0, 4)}`, 125 | uuidParts[4].substring(4), 126 | ]; 127 | 128 | // Iterate through each part. 129 | const apiKeyArr: string[] = partsArr.map((value: string) => { 130 | // Get every two characters. 131 | const valueArr = value.match(/.{1,2}/g); 132 | // Convert each value into a number. 133 | const numArr = valueArr.map((item) => Number(`0x${item}`)); 134 | // Create the string. 135 | return base32.stringify(numArr, { pad: false }); 136 | }); 137 | 138 | // Check if we should add dashes. 139 | return (options.dashes) ? apiKeyArr.join('-') : apiKeyArr.join(''); 140 | }; 141 | 142 | /** 143 | * Creates an API key using Base62 encoding. 144 | * 145 | * @returns The API key. 146 | */ 147 | const getBase62ApiKey = (): string => { 148 | // Create the uuid options. 149 | const v4options: V4Options = { 150 | random: randomBytes(16), 151 | }; 152 | 153 | // Set the encoding alphabet for Base62. 154 | const base62: baseX.BaseConverter = baseX(BASE62_CHAR_POOL); 155 | 156 | // Create a new UUID. 157 | const uuid = uuidv4(v4options); 158 | // Create the UUID buffer. 159 | const uuidBuffer: Uint8Array = Buffer.from(uuid.replace(/-/g, ''), 'hex'); 160 | 161 | // Generate the API key. 162 | return base62.encode(uuidBuffer); 163 | }; 164 | 165 | /** 166 | * Creates an API key using random UUID Version 4 generation. 167 | * 168 | * @param options API key options. 169 | * @returns The API key. 170 | */ 171 | const getUuidV4ApiKey = (options: UuidV4GenerationOptions): string => { 172 | // Get the options. 173 | options = getOptions(options, { dashes: true }); 174 | 175 | // Create the uuid options. 176 | const v4options: V4Options = { 177 | random: randomBytes(16), 178 | }; 179 | 180 | // Generate the API key. 181 | const apiKey = uuidv4(v4options); 182 | 183 | // Check if we should remove dashes. 184 | return (!options.dashes) ? apiKey.replace(/-/g, '') : apiKey; 185 | }; 186 | 187 | /** 188 | * Creates an API key using random UUID Version 5 generation. 189 | * 190 | * @param options API key options. 191 | * @returns The API key. 192 | */ 193 | const getUuidV5ApiKey = (options: UuidV5GenerationOptions): string => { 194 | // Get the options. 195 | options = getOptions(options, { dashes: true }); 196 | 197 | if (!isString(options.name)) { 198 | throw new TypeError(`The required 'name' option must be a string.`); 199 | } 200 | 201 | if (!options.batch) { 202 | if (!isString(options.namespace)) { 203 | throw new TypeError(`The required 'namespace' option must be a string.`); 204 | } 205 | 206 | if (!validateUUID(options.namespace)) { 207 | throw new Error(`The required 'namespace' option must be a valid UUID.`); 208 | } 209 | } 210 | 211 | // Create the uuid options. 212 | const v4options: V4Options = { 213 | random: randomBytes(16), 214 | }; 215 | 216 | /* 217 | * Get the namespace. When using batch processing 218 | * create a namespace UUID. A namespace must be unique. 219 | */ 220 | const namespace: string = (options.batch) 221 | ? uuidv4(v4options) 222 | : options.namespace; 223 | 224 | // Generate the API key. 225 | const apiKey = uuidv5(options.name, namespace); 226 | 227 | // Check if we should remove dashes. 228 | return (!options.dashes) ? apiKey.replace(/-/g, '') : apiKey; 229 | }; 230 | 231 | /** 232 | * Generates a simple API key or a batch of API keys based on 233 | * the provided generation method. 234 | * 235 | * @param options The API key generation options. 236 | * @returns The API key or array of API keys. 237 | */ 238 | export const generateApiKey = (options: GenerationOptions = {}): ApiKeyResults => { 239 | // Get the generation method. 240 | const genMethod: GenerationMethod = options.method ?? 'string'; 241 | 242 | // Check for a valid generation method. 243 | if (!generationMethods.includes(genMethod)) { 244 | throw new Error('Received an unknown API key generation method.'); 245 | } 246 | 247 | // Check for batch generation. 248 | if (options.batch) { 249 | if (!isNaturalNum(options.batch)) { 250 | throw new TypeError(`The 'batch' option must be a natural number > 0.`); 251 | } 252 | } else if (options.batch === 0) { 253 | throw new TypeError(`The 'batch' option must be a natural number > 0.`); 254 | } 255 | 256 | // An object literal for creating the API key. 257 | const getApiKey = (method: GenerationMethod) => ({ 258 | base62: () => getBase62ApiKey(), 259 | base32: () => getBase32CrockfordApiKey(options as Base32GenerationOptions), 260 | bytes: () => getCryptoApiKey(options as BytesGenerationOptions), 261 | string: () => getRandomStringApiKey(options as StringGenerationOptions), 262 | uuidv4: () => getUuidV4ApiKey(options as UuidV4GenerationOptions), 263 | uuidv5: () => getUuidV5ApiKey(options as UuidV5GenerationOptions), 264 | })[method]; 265 | 266 | // Check for batch generation. 267 | if (options.batch) { 268 | // Generate the keys. 269 | const apiKeys: string[] = [ ...Array(options.batch) ].map(() => { 270 | // Generate the API key. 271 | const apiKey = getApiKey(genMethod)(); 272 | 273 | // Add a prefix if necessary. 274 | return (options.prefix) 275 | ? `${options.prefix}.${apiKey}` 276 | : apiKey; 277 | }); 278 | 279 | return apiKeys; 280 | } else { 281 | // Generate the API key. 282 | const apiKey: string = getApiKey(genMethod)(); 283 | // Add a prefix if necessary. 284 | return (options.prefix) ? `${options.prefix}.${apiKey}` : apiKey; 285 | } 286 | }; 287 | 288 | export default generateApiKey; -------------------------------------------------------------------------------- /dist/generate_api_key.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.generateApiKey = void 0; 7 | const chance_1 = __importDefault(require("chance")); 8 | const uuid_1 = require("uuid"); 9 | const crypto_1 = require("crypto"); 10 | const rfc4648_1 = require("rfc4648"); 11 | const base_x_1 = __importDefault(require("base-x")); 12 | const types_1 = require("./types"); 13 | const utils_1 = require("./utils"); 14 | const constants_1 = require("./constants"); 15 | /** 16 | * Creates an API key using random bytes. 17 | * 18 | * @param options API key options. 19 | * @returns The API key. 20 | */ 21 | const getCryptoApiKey = (options) => { 22 | var _a; 23 | let apiKey; 24 | // Get the options. 25 | options = (0, utils_1.getOptions)(options, { 26 | min: constants_1.DEFAULT_MIN_LENGTH, 27 | max: constants_1.DEFAULT_MAX_LENGTH, 28 | }); 29 | // Get a 'Chance' instance. 30 | const chance = new chance_1.default(); 31 | // Determine the length for the key. 32 | const length = (_a = options.length) !== null && _a !== void 0 ? _a : chance.natural({ 33 | min: options.min, 34 | max: options.max 35 | }); 36 | // Set the total bytes. 37 | const totalBytes = Math.ceil(length / 2); 38 | // Generate the API key. 39 | apiKey = (0, crypto_1.randomBytes)(totalBytes).toString('hex'); 40 | // Check the key length. 41 | if (apiKey.length > length) { 42 | const endIndex = apiKey.length - (apiKey.length - length); 43 | apiKey = apiKey.slice(0, endIndex); 44 | } 45 | return apiKey; 46 | }; 47 | /** 48 | * Creates an API key using random string generation. 49 | * 50 | * @param options API key options. 51 | * @returns The API key. 52 | */ 53 | const getRandomStringApiKey = (options) => { 54 | var _a; 55 | // Get the options. 56 | options = (0, utils_1.getOptions)(options, { 57 | min: constants_1.DEFAULT_MIN_LENGTH, 58 | max: constants_1.DEFAULT_MAX_LENGTH, 59 | pool: constants_1.DEFAULT_CHARACTER_POOL, 60 | }); 61 | // Get a 'Chance' instance. 62 | const chance = new chance_1.default(); 63 | // Determine the length for the key. 64 | const length = (_a = options.length) !== null && _a !== void 0 ? _a : chance.natural({ 65 | min: options.min, 66 | max: options.max 67 | }); 68 | // Generate the string. 69 | return chance.string({ length, pool: options.pool }); 70 | }; 71 | /** 72 | * Creates an API key using Base32 Crockford encoding. 73 | * 74 | * @param options API key options. 75 | * @returns The API key. 76 | */ 77 | const getBase32CrockfordApiKey = (options) => { 78 | // Get the options. 79 | options = (0, utils_1.getOptions)(options, { dashes: true }); 80 | // Create the uuid options. 81 | const v4options = { 82 | random: (0, crypto_1.randomBytes)(16), 83 | }; 84 | // Create a new UUID. 85 | const uuid = (0, uuid_1.v4)(v4options); 86 | // Split at the dashes. 87 | const uuidParts = uuid.split('-'); 88 | // Convert the UUID into 4 equally separate parts. 89 | const partsArr = [ 90 | uuidParts[0], 91 | `${uuidParts[1]}${uuidParts[2]}`, 92 | `${uuidParts[3]}${uuidParts[4].substring(0, 4)}`, 93 | uuidParts[4].substring(4), 94 | ]; 95 | // Iterate through each part. 96 | const apiKeyArr = partsArr.map((value) => { 97 | // Get every two characters. 98 | const valueArr = value.match(/.{1,2}/g); 99 | // Convert each value into a number. 100 | const numArr = valueArr.map((item) => Number(`0x${item}`)); 101 | // Create the string. 102 | return rfc4648_1.base32.stringify(numArr, { pad: false }); 103 | }); 104 | // Check if we should add dashes. 105 | return (options.dashes) ? apiKeyArr.join('-') : apiKeyArr.join(''); 106 | }; 107 | /** 108 | * Creates an API key using Base62 encoding. 109 | * 110 | * @returns The API key. 111 | */ 112 | const getBase62ApiKey = () => { 113 | // Create the uuid options. 114 | const v4options = { 115 | random: (0, crypto_1.randomBytes)(16), 116 | }; 117 | // Set the encoding alphabet for Base62. 118 | const base62 = (0, base_x_1.default)(constants_1.BASE62_CHAR_POOL); 119 | // Create a new UUID. 120 | const uuid = (0, uuid_1.v4)(v4options); 121 | // Create the UUID buffer. 122 | const uuidBuffer = Buffer.from(uuid.replace(/-/g, ''), 'hex'); 123 | // Generate the API key. 124 | return base62.encode(uuidBuffer); 125 | }; 126 | /** 127 | * Creates an API key using random UUID Version 4 generation. 128 | * 129 | * @param options API key options. 130 | * @returns The API key. 131 | */ 132 | const getUuidV4ApiKey = (options) => { 133 | // Get the options. 134 | options = (0, utils_1.getOptions)(options, { dashes: true }); 135 | // Create the uuid options. 136 | const v4options = { 137 | random: (0, crypto_1.randomBytes)(16), 138 | }; 139 | // Generate the API key. 140 | const apiKey = (0, uuid_1.v4)(v4options); 141 | // Check if we should remove dashes. 142 | return (!options.dashes) ? apiKey.replace(/-/g, '') : apiKey; 143 | }; 144 | /** 145 | * Creates an API key using random UUID Version 5 generation. 146 | * 147 | * @param options API key options. 148 | * @returns The API key. 149 | */ 150 | const getUuidV5ApiKey = (options) => { 151 | // Get the options. 152 | options = (0, utils_1.getOptions)(options, { dashes: true }); 153 | if (!(0, utils_1.isString)(options.name)) { 154 | throw new TypeError(`The required 'name' option must be a string.`); 155 | } 156 | if (!options.batch) { 157 | if (!(0, utils_1.isString)(options.namespace)) { 158 | throw new TypeError(`The required 'namespace' option must be a string.`); 159 | } 160 | if (!(0, uuid_1.validate)(options.namespace)) { 161 | throw new Error(`The required 'namespace' option must be a valid UUID.`); 162 | } 163 | } 164 | // Create the uuid options. 165 | const v4options = { 166 | random: (0, crypto_1.randomBytes)(16), 167 | }; 168 | /* 169 | * Get the namespace. When using batch processing 170 | * create a namespace UUID. A namespace must be unique. 171 | */ 172 | const namespace = (options.batch) 173 | ? (0, uuid_1.v4)(v4options) 174 | : options.namespace; 175 | // Generate the API key. 176 | const apiKey = (0, uuid_1.v5)(options.name, namespace); 177 | // Check if we should remove dashes. 178 | return (!options.dashes) ? apiKey.replace(/-/g, '') : apiKey; 179 | }; 180 | /** 181 | * Generates a simple API key or a batch of API keys based on 182 | * the provided generation method. 183 | * 184 | * @param options The API key generation options. 185 | * @returns The API key or array of API keys. 186 | */ 187 | const generateApiKey = (options = {}) => { 188 | var _a; 189 | // Get the generation method. 190 | const genMethod = (_a = options.method) !== null && _a !== void 0 ? _a : 'string'; 191 | // Check for a valid generation method. 192 | if (!types_1.generationMethods.includes(genMethod)) { 193 | throw new Error('Received an unknown API key generation method.'); 194 | } 195 | // Check for batch generation. 196 | if (options.batch) { 197 | if (!(0, utils_1.isNaturalNum)(options.batch)) { 198 | throw new TypeError(`The 'batch' option must be a natural number > 0.`); 199 | } 200 | } 201 | else if (options.batch === 0) { 202 | throw new TypeError(`The 'batch' option must be a natural number > 0.`); 203 | } 204 | // An object literal for creating the API key. 205 | const getApiKey = (method) => ({ 206 | base62: () => getBase62ApiKey(), 207 | base32: () => getBase32CrockfordApiKey(options), 208 | bytes: () => getCryptoApiKey(options), 209 | string: () => getRandomStringApiKey(options), 210 | uuidv4: () => getUuidV4ApiKey(options), 211 | uuidv5: () => getUuidV5ApiKey(options), 212 | })[method]; 213 | // Check for batch generation. 214 | if (options.batch) { 215 | // Generate the keys. 216 | const apiKeys = [...Array(options.batch)].map(() => { 217 | // Generate the API key. 218 | const apiKey = getApiKey(genMethod)(); 219 | // Add a prefix if necessary. 220 | return (options.prefix) 221 | ? `${options.prefix}.${apiKey}` 222 | : apiKey; 223 | }); 224 | return apiKeys; 225 | } 226 | else { 227 | // Generate the API key. 228 | const apiKey = getApiKey(genMethod)(); 229 | // Add a prefix if necessary. 230 | return (options.prefix) ? `${options.prefix}.${apiKey}` : apiKey; 231 | } 232 | }; 233 | exports.generateApiKey = generateApiKey; 234 | exports.default = exports.generateApiKey; 235 | //# sourceMappingURL=generate_api_key.js.map -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2021 Paul Proctor 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /test/generate_api_key.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import { v4 as uuidv4 } from 'uuid'; 3 | import path from 'path'; 4 | import { existsSync } from 'fs'; 5 | import { 6 | DEFAULT_CHARACTER_POOL, 7 | BASE62_CHAR_POOL 8 | } from '../src/constants'; 9 | 10 | // Check what we are testing. 11 | const shouldTestBuild = ( 12 | process.env.TEST_BUILD 13 | && existsSync(path.resolve(__dirname, '../dist/index.js')) 14 | ); 15 | 16 | /* 17 | * Determine if we are testing the Typescript files 18 | * or the JavaScript build. 19 | */ 20 | const { generateApiKey } = shouldTestBuild 21 | ? require('../dist/index') 22 | : require('../src'); 23 | 24 | describe('generateApiKey', () => { 25 | context(`'bytes' method`, () => { 26 | it('should create an API key with the default options', () => { 27 | // Create the API key. 28 | const apiKey = generateApiKey({ method: 'bytes' }); 29 | 30 | expect(apiKey).to.be.a('string'); 31 | expect(apiKey).to.have.lengthOf.within(16, 32); 32 | }); 33 | 34 | it('should create an API key with a set length', () => { 35 | // Create the API key. 36 | const apiKey = generateApiKey({ method: 'bytes', length: 10 }); 37 | 38 | expect(apiKey).to.be.a('string'); 39 | expect(apiKey).to.have.lengthOf(10); 40 | }); 41 | 42 | it('should create an API key with an uneven set length', () => { 43 | // Create the API key. 44 | const apiKey = generateApiKey({ method: 'bytes', length: 7 }); 45 | 46 | expect(apiKey).to.be.a('string'); 47 | expect(apiKey).to.have.lengthOf(7); 48 | }); 49 | 50 | it(`should create an API key with a length within the 'min' and 'max' range`, () => { 51 | // Create the API key. 52 | const apiKey = generateApiKey({ method: 'bytes', min: 5, max: 15 }); 53 | 54 | expect(apiKey).to.be.a('string'); 55 | expect(apiKey).to.have.lengthOf.within(5, 15); 56 | }); 57 | 58 | it(`should create an API key with an uneven 'min' and 'max' range`, () => { 59 | // Create the API key. 60 | const apiKey = generateApiKey({ method: 'bytes', min: 15, max: 15 }); 61 | 62 | expect(apiKey).to.be.a('string'); 63 | expect(apiKey).to.have.lengthOf(15); 64 | }); 65 | }); 66 | 67 | context(`'string' method`, () => { 68 | it('should create an API key with the default options', () => { 69 | // Create the API key. 70 | const apiKey = generateApiKey({ method: 'string' }); 71 | 72 | expect(apiKey).to.be.a('string'); 73 | expect(apiKey).to.have.lengthOf.within(16, 32); 74 | [ ...apiKey ].forEach((c) => expect(DEFAULT_CHARACTER_POOL).to.include(c)); 75 | }); 76 | 77 | it('should create an API key with a set length', () => { 78 | // Create the API key. 79 | const apiKey = generateApiKey({ method: 'string', length: 15 }); 80 | 81 | expect(apiKey).to.be.a('string'); 82 | expect(apiKey).to.have.lengthOf(15); 83 | [ ...apiKey ].forEach((c) => expect(DEFAULT_CHARACTER_POOL).to.include(c)); 84 | }); 85 | 86 | it(`should create an API key with a length within the 'min' and 'max' range`, () => { 87 | // Create the API key. 88 | const apiKey = generateApiKey({ method: 'string', min: 10, max: 25 }); 89 | 90 | expect(apiKey).to.be.a('string'); 91 | expect(apiKey).to.have.lengthOf.within(10, 25); 92 | [ ...apiKey ].forEach((c) => expect(DEFAULT_CHARACTER_POOL).to.include(c)); 93 | }); 94 | }); 95 | 96 | context(`'base32' method`, () => { 97 | it('should create an API key with the default options', () => { 98 | // Create the API key. 99 | const apiKey = generateApiKey({ method: 'base32' }); 100 | 101 | expect(apiKey).to.be.a('string'); 102 | expect(apiKey).to.have.lengthOf(31); 103 | expect(apiKey).to.match(/[A-Z0-9]{7}-[A-Z0-9]{7}-[A-Z0-9]{7}-[A-Z0-9]{7}/); 104 | }); 105 | 106 | it('should create an API key without dashes', () => { 107 | // Create the API key. 108 | const apiKey = generateApiKey({ method: 'base32', dashes: false }); 109 | 110 | expect(apiKey).to.be.a('string'); 111 | expect(apiKey).to.have.lengthOf(28); 112 | expect(apiKey).to.match(/[A-Z0-9]{28}/); 113 | }); 114 | }); 115 | 116 | context(`'base62' method`, () => { 117 | it('should create an API key', () => { 118 | // Create the API key. 119 | const apiKey = generateApiKey({ method: 'base62' }); 120 | 121 | expect(apiKey).to.be.a('string'); 122 | expect(apiKey).to.have.lengthOf.at.most(22); 123 | [ ...apiKey ].forEach((c) => expect(BASE62_CHAR_POOL).to.include(c)); 124 | }); 125 | }); 126 | 127 | context(`'uuidv4' method`, () => { 128 | it('should create an API key with the default options', () => { 129 | // Create the API key. 130 | const apiKey = generateApiKey({ method: 'uuidv4' }); 131 | 132 | expect(apiKey).to.be.a('string'); 133 | expect(apiKey).to.have.lengthOf(36); 134 | expect(apiKey).to.match( 135 | /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/ 136 | ); 137 | }); 138 | 139 | it('should create an API key without dashes', () => { 140 | // Create the API key. 141 | const apiKey = generateApiKey({ method: 'uuidv4', dashes: false }); 142 | 143 | expect(apiKey).to.be.a('string'); 144 | expect(apiKey).to.have.lengthOf(32); 145 | expect(apiKey).to.match(/[0-9a-fA-F]{32}/); 146 | }); 147 | }); 148 | 149 | context(`'uuidv5' method`, () => { 150 | it('should create an API key with the default options', () => { 151 | // Create the API key. 152 | const apiKey = generateApiKey({ 153 | method: 'uuidv5', 154 | name: 'Super secret', 155 | namespace: uuidv4(), 156 | }); 157 | 158 | expect(apiKey).to.be.a('string'); 159 | expect(apiKey).to.have.lengthOf(36); 160 | expect(apiKey).to.match( 161 | /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/ 162 | ); 163 | }); 164 | 165 | it('should create API keys with a created namespace in batch mode', () => { 166 | // Create the API keys. 167 | const apiKeys = generateApiKey({ 168 | method: 'uuidv5', 169 | name: 'Super secret', 170 | batch: 2, 171 | }) as string[]; 172 | 173 | expect(apiKeys).to.be.an('array'); 174 | expect(apiKeys).to.have.lengthOf(2); 175 | 176 | apiKeys.forEach((key) => { 177 | expect(key).to.be.a('string'); 178 | expect(key).to.have.lengthOf(36); 179 | expect(key).to.match( 180 | /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/ 181 | ); 182 | }); 183 | }); 184 | 185 | it('should create an API key without dashes', () => { 186 | // Create the API key. 187 | const apiKey = generateApiKey({ 188 | method: 'uuidv5', 189 | name: 'Super secret', 190 | namespace: uuidv4(), 191 | dashes: false, 192 | }); 193 | 194 | expect(apiKey).to.be.a('string'); 195 | expect(apiKey).to.have.lengthOf(32); 196 | expect(apiKey).to.match(/[0-9a-fA-F]{32}/); 197 | }); 198 | 199 | it(`should throw an error for an invalid 'name' value`, () => { 200 | expect(() => generateApiKey({ 201 | method: 'uuidv5', 202 | namespace: uuidv4(), 203 | })).to.throw(TypeError, `The required 'name' option must be a string.`); 204 | 205 | expect(() => generateApiKey({ 206 | method: 'uuidv5', 207 | // @ts-ignore: "name" expects a string. 208 | name: 102, 209 | namespace: uuidv4(), 210 | })).to.throw(TypeError, `The required 'name' option must be a string.`); 211 | }); 212 | 213 | it(`should throw an error for an invalid 'namespace' value`, () => { 214 | expect(() => generateApiKey({ 215 | method: 'uuidv5', 216 | name: 'Very special', 217 | })).to.throw(TypeError, `The required 'namespace' option must be a string.`); 218 | 219 | expect(() => generateApiKey({ 220 | method: 'uuidv5', 221 | name: 'Very special', 222 | // @ts-ignore: "namespace" expects a string. 223 | namespace: {}, 224 | })).to.throw(TypeError, `The required 'namespace' option must be a string.`); 225 | 226 | expect(() => generateApiKey({ 227 | method: 'uuidv5', 228 | name: 'Very special', 229 | namespace: 'not a valid uuid', 230 | })).to.throw(Error, `The required 'namespace' option must be a valid UUID.`); 231 | }); 232 | }); 233 | 234 | it('should create an API key with a prefix', () => { 235 | // Create the API key. 236 | const apiKey = generateApiKey({ method: 'string', prefix: 'app' }) as string; 237 | 238 | expect(apiKey).to.be.a('string'); 239 | expect(apiKey.startsWith('app.')).to.be.true; 240 | expect(apiKey.replace(/^app\./, '')).to.have.lengthOf.within(16, 32); 241 | [ ...apiKey.replace(/^app\./, '') ].forEach((c) => expect(DEFAULT_CHARACTER_POOL).to.include(c)); 242 | }); 243 | 244 | it(`should create an API key using the 'string' method when not provided`, () => { 245 | // Create the API key. 246 | const apiKey = generateApiKey(); 247 | 248 | expect(apiKey).to.be.a('string'); 249 | expect(apiKey).to.have.lengthOf.within(16, 32); 250 | [ ...apiKey ].forEach((c) => expect(DEFAULT_CHARACTER_POOL).to.include(c)); 251 | }); 252 | 253 | it(`should throw an error for an invalid 'method' value`, () => { 254 | expect(() => generateApiKey({ 255 | // @ts-ignore: "method" expects a string. 256 | method: 99, 257 | })).to.throw(Error, 'Received an unknown API key generation method.'); 258 | 259 | expect(() => generateApiKey({ 260 | // @ts-ignore: "method" expects a valid method value. 261 | method: 'other', 262 | batch: 8, 263 | })).to.throw(Error, 'Received an unknown API key generation method.'); 264 | }); 265 | 266 | it('should generate a batch of API keys', () => { 267 | // Create the API keys. 268 | const apiKeys = generateApiKey({ method: 'string', batch: 10 }) as string[]; 269 | 270 | expect(apiKeys).to.be.an('array'); 271 | expect(apiKeys).to.have.lengthOf(10); 272 | 273 | apiKeys.forEach((key) => { 274 | expect(key).to.be.a('string'); 275 | expect(key).to.have.lengthOf.within(16, 32); 276 | [ ...key ].forEach((c) => expect(DEFAULT_CHARACTER_POOL).to.include(c)); 277 | }); 278 | }); 279 | 280 | it('should generate a batch of API keys with a prefix', () => { 281 | // Create the API keys. 282 | const apiKeys = generateApiKey({ 283 | method: 'string', 284 | batch: 5, 285 | prefix: 'test' 286 | }) as string[]; 287 | 288 | expect(apiKeys).to.be.an('array'); 289 | expect(apiKeys).to.have.lengthOf(5); 290 | 291 | apiKeys.forEach((key) => { 292 | expect(key).to.be.a('string'); 293 | expect(key.startsWith('test.')).to.be.true; 294 | expect(key.replace(/^test\./, '')).to.have.lengthOf.within(16, 32); 295 | [ ...key.replace(/^test\./, '') ].forEach((c) => expect(DEFAULT_CHARACTER_POOL).to.include(c)); 296 | }); 297 | }); 298 | 299 | it(`should throw an error for an invalid 'batch' value`, () => { 300 | expect(() => generateApiKey({ 301 | method: 'string', 302 | batch: 4.25, 303 | })).to.throw(TypeError, `The 'batch' option must be a natural number > 0.`); 304 | 305 | expect(() => generateApiKey({ 306 | method: 'string', 307 | batch: -5, 308 | })).to.throw(TypeError, `The 'batch' option must be a natural number > 0.`); 309 | 310 | expect(() => generateApiKey({ 311 | method: 'string', 312 | batch: 0, 313 | })).to.throw(TypeError, `The 'batch' option must be a natural number > 0.`); 314 | 315 | 316 | expect(() => generateApiKey({ 317 | method: 'string', 318 | // @ts-ignore: "batch" expects a number. 319 | batch: [], 320 | })).to.throw(TypeError, `The 'batch' option must be a natural number > 0.`); 321 | }); 322 | 323 | context(`should import 'generateApiKey' as a default import`, () => { 324 | const generateApiKeyDefault = shouldTestBuild 325 | ? require('../dist/index').default 326 | : require('../src').default; 327 | 328 | // Create the API key. 329 | const apiKey = generateApiKeyDefault(); 330 | 331 | expect(apiKey).to.be.a('string'); 332 | }); 333 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # generate-api-key 2 | 3 | [![GitHub package.json version (branch)][version-image]][npm-url] 4 | [![unit tests][tests-image]][tests-url] 5 | [![Build Status][travis-image]][travis-url] 6 | [![coverage][coverage-image]][coverage-url] 7 | [![Coverage Status][coveralls-image]][coveralls-url] 8 | [![CodeFactor][codefactor-image]][codefactor-url] 9 | 10 | 11 | `generate-api-key` is a library for generating random API (Application Programming Interface) 12 | keys or access tokens. By using this library, a Node.js backend service can generate API keys 13 | or access tokens, then issue them to users and/or other services that require access to the 14 | capabilities and resources provided by the API service. 15 | 16 | 17 | ## Table of contents 18 | 19 | - [Installation](#installation) 20 | - [Usage](#usage) 21 | - [Generation Methods](#generation-methods) 22 | - [Options](#options) 23 | - [`string` Method](#string-method) 24 | - [`bytes` Method](#bytes-method) 25 | - [`base32` Method](#base32-method) 26 | - [`base62` Method](#base62-method) 27 | - [`uuidv4` Method](#uuidv4-method) 28 | - [`uuidv5` Method](#uuidv5-method) 29 | - [Security](#security) 30 | - [Change Log](#change-log) 31 | - [License](#license) 32 |
33 | 34 | ## Installation 35 | 36 | Using NPM: 37 | 38 | ```bash 39 | $ npm install generate-api-key 40 | ``` 41 | 42 | Using Yarn: 43 | 44 | ```bash 45 | $ yarn add generate-api-key 46 | ``` 47 |
48 | 49 | ## Usage 50 | 51 | The `generate-api-key` library can generate API key/access tokens by utilizing several 52 | generation methods, such as `string`, `bytes`, `base32`, `base62`, `uuidv4`, and 53 | `uuidv5`. The `string` method is used by default. 54 |
55 | 56 | Importing: 57 | 58 | ```javascript 59 | // CommonJS Import 60 | const { generateApiKey } = require('generate-api-key'); 61 | // OR 62 | const generateApiKey = require('generate-api-key').default; 63 | 64 | // ES6 Import 65 | import { generateApiKey } from 'generate-api-key'; 66 | // OR 67 | import generateApiKey from 'generate-api-key'; 68 | ``` 69 | 70 | Example: 71 | 72 | ```javascript 73 | import generateApiKey from 'generate-api-key'; 74 | 75 | // Generate the API key. 76 | generateApiKey(); // ⇨ 'q_EaTiX+xbBXLyO05.+zDXjI+Qi_X0v' 77 | ``` 78 |
79 | 80 | ### Generation Methods 81 | 82 | | Method | Description | 83 | | -------- | ---------------------------------------------------------------------- | 84 | | `string` | Creates an API key/access token using random string generation | 85 | | `bytes` | Creates an API key/access token using random bytes | 86 | | `base32` | Creates an API key/access token using a random UUID and converting it
into a [Douglas Crockford Base32](https://en.wikipedia.org/wiki/Base32#Crockford's_Base32) encoded string | 87 | | `base62` | Creates an API key using Base62 encoding | 88 | | `uuidv4` | Creates an API key/access token using random UUID Version 4 generation | 89 | | `uuidv5` | Creates an API key/access token using random UUID Version 5 generation | 90 |
91 | 92 | ## Options 93 | 94 | ### `string` Method 95 | 96 | Creates an API key/access token using random string generation. 97 | 98 | | Name | Default Value | Description | 99 | | ---------| ------------- | ----------------------------------------------------------------- | 100 | | `method` | `string` | To use the `string` generation method | 101 | | `min` | `16` | The minimum length of the API key (ignored if `length` is given) | 102 | | `max` | `32` | The maximum length of the API key (ignored if `length` is given) | 103 | | `length` | `undefined` | The length of the API key | 104 | | `pool` | `abcdefghijklmnopqrstuvwxyz`
`ABCDEFGHIJKLMNOPQRSTUVWXYZ`
`0123456789-._~+/` | The characters used for the API key generation | 105 | | `prefix` | `undefined` | A string prefix for the API key, followed by a period (`.`) | 106 | | `batch` | `undefined` | The number of API keys to generate | 107 |
108 | 109 | Examples: 110 | 111 | ```javascript 112 | import generateApiKey from 'generate-api-key'; 113 | 114 | // Generate the API key. The 'string' method is used by default. 115 | generateApiKey(); // ⇨ 'q_EaTiX+xbBXLyO05.+zDXjI+Qi_X0v' 116 | 117 | // Provide the generation method. 118 | generateApiKey({ method: 'string' }); // ⇨ 'Zt1HbMcLKxk6~nnW' 119 | 120 | // Create an API key with a certain length. 121 | generateApiKey({ method: 'string', length: 8 }); // ⇨ 'TNJ6-Lo4' 122 | 123 | // Create an API key with a length between a certain range. 124 | generateApiKey({ method: 'string', min: 10, max: 20 }); // ⇨ 'ENwiOFdP8cWj' 125 | 126 | // Create an API key with a certain pool of characters. 127 | generateApiKey({ 128 | method: 'string', 129 | pool: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 130 | }); // ⇨ 'QFLSGIDLOUAELQZTQXMHQNJ' 131 | 132 | // Create an API key with a prefix. 133 | generateApiKey({ method: 'string', prefix: 'test_app' }); // ⇨ 'test_app.aTd34Rli0nir70/8' 134 | 135 | // Create a batch (certain amount) of API keys. 136 | generateApiKey({ method: 'string', batch: 5 }); // ⇨ 137 | // [ 138 | // 'w05KkI9AWhKxzvPFtXotUva-', 139 | // 'YFL0ICl4PtLD8Y/oQ20iyAE', 140 | // 'vJFbfeP_cpMYsH9l5BVHY23Ss', 141 | // '29~LIlSjDYFr5OrhU3f', 142 | // 'UQc8Tp1d9elWAh7KDIMkjz2moFs' 143 | // ] 144 | ``` 145 |
146 | 147 | ### `bytes` Method 148 | 149 | Creates an API key/access token using random bytes. 150 | 151 | | Name | Default Value | Description | 152 | | ---------| ------------- | ----------------------------------------------------------------- | 153 | | `method` | `bytes` | To use the `bytes` generation method | 154 | | `min` | `16` | The minimum length of the API key (ignored if `length` is given) | 155 | | `max` | `32` | The maximum length of the API key (ignored if `length` is given) | 156 | | `length` | `undefined` | The length of the API key | 157 | | `prefix` | `undefined` | A string prefix for the API key, followed by a period (`.`) | 158 | | `batch` | `undefined` | The number of API keys to generate | 159 |
160 | 161 | Examples: 162 | 163 | ```javascript 164 | import generateApiKey from 'generate-api-key'; 165 | 166 | // Provide the generation method. 167 | generateApiKey({ method: 'bytes' }); // ⇨ '6f31bfc3717d63e7bd21' 168 | 169 | // Create an API key with a certain length. 170 | generateApiKey({ method: 'bytes', length: 12 }); // ⇨ '47a8dcbc79f6' 171 | 172 | // Create an API key with a length between a certain range. 173 | generateApiKey({ method: 'bytes', min: 12, max: 25 }); // ⇨ 'fae27c801b5092bc' 174 | 175 | // Create an API key with a prefix. 176 | generateApiKey({ method: 'bytes', prefix: 'test_app' }); // ⇨ 'test_app.8daaa6b26c79030db1a1448261' 177 | 178 | // Create a batch (certain amount) of API keys. 179 | generateApiKey({ method: 'bytes', batch: 5 }); // ⇨ 180 | // [ 181 | // '0d5a87f007aae092a6', 182 | // '96a62b4438d82645506b', 183 | // 'abd4e4199311fb1e2a818a4a', 184 | // 'ddbb04b2375ba050cb506e89df', 185 | // '2ee3db86329865d8' 186 | // ] 187 | ``` 188 |
189 | 190 | ### `base32` Method 191 | 192 | Creates an API key/access token using a random UUID and converting it into a [Douglas Crockford Base32](https://en.wikipedia.org/wiki/Base32#Crockford's_Base32) encoded string. 193 | 194 | | Name | Default Value | Description | 195 | | ---------| ------------- | ----------------------------------------------------------------- | 196 | | `method` | `base32` | To use the `base32` generation method | 197 | | `dashes` | `true` | Add dashes (`-`) to the API key or not | 198 | | `prefix` | `undefined` | A string prefix for the API key, followed by a period (`.`) | 199 | | `batch` | `undefined` | The number of API keys to generate | 200 |
201 | 202 | Examples: 203 | 204 | ```javascript 205 | import generateApiKey from 'generate-api-key'; 206 | 207 | // Provide the generation method. 208 | generateApiKey({ method: 'base32' }); // ⇨ '2NOLH5I-43EEK7A-R6YRK3I-BRCIQNQ' 209 | 210 | // Create an API key without the dashes. 211 | generateApiKey({ method: 'base32', dashes: false }); // ⇨ 'MPZ6G2QTAXUGBYTCLKEGYZG6UXAY' 212 | 213 | // Create an API key with a prefix. 214 | generateApiKey({ method: 'base32', prefix: 'test_app' }); // ⇨ 'test_app.PMKC6DQ-2LZECPY-RVRF6YI-INGIEMQ' 215 | 216 | // Create a batch (certain amount) of API keys. 217 | generateApiKey({ method: 'base32', batch: 5 }); // ⇨ 218 | // [ 219 | // 'EGIKGTI-PTLUW7I-QBLUBNA-FH4P2EI', 220 | // 'MOHE7XQ-DRKEQ4I-RO6JVDQ-GIJRLKA', 221 | // 'UDVJBFI-JFTUWYA-XE4YCBA-FUYXQBY', 222 | // '22EFRQI-2AWULDI-QE2PUCY-MXG36RI', 223 | // '42CXULQ-SDPU6ZA-RQ6QPBQ-4BMZCOA' 224 | // ] 225 | ``` 226 |
227 | 228 | ### `base62` Method 229 | 230 | Creates an API key using Base62 encoding. 231 | 232 | | Name | Default Value | Description | 233 | | ---------| ------------- | ----------------------------------------------------------------- | 234 | | `method` | `base62` | To use the `base62` generation method | 235 | | `prefix` | `undefined` | A string prefix for the API key, followed by a period (`.`) | 236 | | `batch` | `undefined` | The number of API keys to generate | 237 |
238 | 239 | Examples: 240 | 241 | ```javascript 242 | import generateApiKey from 'generate-api-key'; 243 | 244 | // Provide the generation method. 245 | generateApiKey({ method: 'base62' }); // ⇨ '2AEmXhHtNJkIAqL1S3So6G' 246 | 247 | // Create an API key with a prefix. 248 | generateApiKey({ method: 'base62', prefix: 'test_app' }); // ⇨ 'test_app.1aHVuwNwzITpzJWl40OPvx' 249 | 250 | // Create a batch (certain amount) of API keys. 251 | generateApiKey({ method: 'base62', batch: 5 }); // ⇨ 252 | // [ 253 | // '1kCFJsoNBpnX65k84Y8clk', 254 | // '5rAZLlhh9pQuV6weF2RxGg', 255 | // '4kBS1ciuLadz340qeXQGl6', 256 | // '3DXocnIyt236ymZAHWYOs6', 257 | // 'oPv07XcusmLM1UsHniMZy' 258 | // ] 259 | ``` 260 |
261 | 262 | ### `uuidv4` Method 263 | 264 | Creates an API key/access token using random UUID Version 4 generation. 265 | 266 | | Name | Default Value | Description | 267 | | ---------| ------------- | ----------------------------------------------------------------- | 268 | | `method` | `uuidv4` | To use the `uuidv4` generation method | 269 | | `dashes` | `true` | Add dashes (`-`) to the API key or not | 270 | | `prefix` | `undefined` | A string prefix for the API key, followed by a period (`.`) | 271 | | `batch` | `undefined` | The number of API keys to generate | 272 |
273 | 274 | Examples: 275 | 276 | ```javascript 277 | import generateApiKey from 'generate-api-key'; 278 | 279 | // Provide the generation method. 280 | generateApiKey({ method: 'uuidv4' }); // ⇨ 'c40c974f-307e-490e-8d4e-0c8f31f21df3' 281 | 282 | // Create an API key without the dashes. 283 | generateApiKey({ method: 'uuidv4', dashes: false }); // ⇨ 'be1748535cad474191b34a4aa4299a9d' 284 | 285 | // Create an API key with a prefix. 286 | generateApiKey({ method: 'uuidv4', prefix: 'test_app' }); // ⇨ 'test_app.1dfdf2c1-7365-4625-b7d9-d9db5210f18d' 287 | 288 | // Create a batch (certain amount) of API keys. 289 | generateApiKey({ method: 'uuidv4', batch: 5 }); // ⇨ 290 | // [ 291 | // 'd822611a-0600-4edf-9697-21ba5c79e6d7', 292 | // '596ac0ae-c4a0-4803-b796-8f239c8431ba', 293 | // '0f3819f3-b417-4c4c-b674-853473800265', 294 | // '882332bf-4215-41ce-a573-a3c8f5d47a24', 295 | // 'f269c1f6-77d5-464d-8229-769c0b3a21f7' 296 | // ] 297 | ``` 298 |
299 | 300 | ### `uuidv5` Method 301 | 302 | Creates an API key/access token using random UUID Version 5 generation. 303 | 304 | | Name | Default Value | Description | 305 | | -------------| ------------- | ----------------------------------------------------------------- | 306 | | `method` | `uuidv5` | To use the `uuidv5` generation method | 307 | | `name` | `undefined` | A unique name to use for the generation | 308 | | `namespace` | `undefined` | The UUID to use for the generation (ignored if `batch` is given) | 309 | | `dashes` | `true` | Add dashes (`-`) to the API key or not | 310 | | `prefix` | `undefined` | A string prefix for the API key, followed by a period (`.`) | 311 | | `batch` | `undefined` | The number of API keys to generate | 312 |
313 | 314 | Examples: 315 | 316 | ```javascript 317 | import generateApiKey from 'generate-api-key'; 318 | 319 | // Provide the generation method with the name and namespace. 320 | generateApiKey({ 321 | method: 'uuidv5', 322 | name: 'production app', 323 | namespace: '1dfdf2c1-7365-4625-b7d9-d9db5210f18d' 324 | }); // ⇨ 'd683c168-377f-528d-8d9a-b7f1551ecb44' 325 | 326 | // Create an API key without the dashes. 327 | generateApiKey({ 328 | method: 'uuidv5', 329 | name: 'production app', 330 | namespace: '596ac0ae-c4a0-4803-b796-8f239c8431ba', 331 | dashes: false 332 | }); // ⇨ 'b1bc2cda0c1f5eb594495088a37339b8' 333 | 334 | // Create an API key with a prefix. 335 | generateApiKey({ 336 | method: 'uuidv5', 337 | name: 'production app', 338 | namespace: '0f3819f3-b417-4c4c-b674-853473800265', 339 | prefix: 'prod_app' 340 | }); // ⇨ 'prod_app.3f7e5d98-3aa9-5dcb-82e3-10d9a2fc412a' 341 | 342 | /* 343 | * Create a batch (certain amount) of API keys. When creating a 344 | * batch of 'uuidv5' API keys, a namespace is generated for each 345 | * key. The provided namespace is not used. 346 | */ 347 | generateApiKey({ 348 | method: 'uuidv5', 349 | name: 'production app', 350 | batch: 5 351 | }); // ⇨ 352 | // [ 353 | // '1e37088a-4dbf-5126-a255-071095e3a53b', 354 | // '23c3cfce-2cf1-5b80-856c-12cf6b5f4e88', 355 | // '46ec193e-237e-517b-a02e-7679510215d8', 356 | // 'cf646907-7bc7-5953-bf76-b7527d70b234', 357 | // '5ead0c39-0eac-57e8-831d-250932aeb1e0' 358 | // ] 359 | ``` 360 |
361 | 362 | ## Security 363 | 364 | When generating and storing API keys and access tokens please be mindful of secure 365 | database storage best practices. The reason API keys or access tokens are stored is to 366 | confirm the key/token that is provided (ex. HTTP request) is valid and issued by your 367 | organization or application (the same as a password). Just like a password, an API key 368 | or access token can provide direct access to data or services that require authentication. 369 | 370 | To authenticate an API key or access token, it is not necessary to know the raw 371 | key/token value, the key/token just needs to validated to be correct. API keys and 372 | access tokens should not be stored in plain text in your database, they should be 373 | stored as a hashed value. Consider using database storage concepts such as salting 374 | or peppering during the hashing process. 375 | 376 | Lastly, if you suspect the API credentials for your organization or application have 377 | been compromised, please revoke the keys and regenerate new keys. 378 |
379 | 380 | ## Change Log 381 | 382 | The [CHANGELOG](./CHANGELOG.md) contains descriptions of notable changes. 383 |
384 | 385 | ## License 386 | 387 | This software is licensed under the [Apache 2 license](./LICENSE). 388 | 389 | [npm-url]: https://www.npmjs.com/package/generate-api-key 390 | [version-image]: https://img.shields.io/github/package-json/v/pauldenver/generate-api-key/main?label=version&style=flat-square 391 | [tests-url]: https://github.com/pauldenver/generate-api-key/actions/workflows/test.yml 392 | [tests-image]: https://github.com/pauldenver/generate-api-key/actions/workflows/test.yml/badge.svg?branch=main 393 | [coverage-url]: https://github.com/pauldenver/generate-api-key/actions/workflows/coverage.yml 394 | [coverage-image]: https://github.com/pauldenver/generate-api-key/actions/workflows/coverage.yml/badge.svg?branch=main 395 | [travis-url]: https://travis-ci.com/pauldenver/generate-api-key 396 | [travis-image]: https://travis-ci.com/pauldenver/generate-api-key.svg?branch=main 397 | [coveralls-url]: https://coveralls.io/github/pauldenver/generate-api-key 398 | [coveralls-image]: https://coveralls.io/repos/github/pauldenver/generate-api-key/badge.svg?branch=main 399 | [codefactor-url]: https://www.codefactor.io/repository/github/pauldenver/generate-api-key/overview/main 400 | [codefactor-image]: https://www.codefactor.io/repository/github/pauldenver/generate-api-key/badge/main --------------------------------------------------------------------------------