├── .travis.yml ├── .gitignore ├── Makefile ├── CHANGELOG.md ├── package.json ├── index.test-d.ts ├── LICENSE ├── test └── test.js ├── index.d.ts ├── index.js └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | - "10" 5 | - "12" 6 | - "stable" 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | lib-cov 3 | *.seed 4 | *.log 5 | *.csv 6 | *.dat 7 | *.out 8 | *.pid 9 | *.gz 10 | *.tgz 11 | pids 12 | logs 13 | results 14 | build 15 | node_modules 16 | coverage 17 | coverage.html 18 | package-lock.json 19 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | MOCHA_OPTS= --check-leaks 2 | REPORTER = tap 3 | VERSION = $(shell node -e 'console.log(require("./package.json").version)') 4 | PACKAGE_NAME = $(shell node -e 'console.log(require("./package.json").name)') 5 | 6 | all: build test 7 | 8 | check: test 9 | 10 | build: 11 | npm install 12 | 13 | test: jshint test-unit test-typings 14 | 15 | test-unit: 16 | @NODE_ENV=test ./node_modules/.bin/mocha \ 17 | --reporter $(REPORTER) \ 18 | $(MOCHA_OPTS) 19 | 20 | test-typings: 21 | @./node_modules/.bin/tsd 22 | 23 | jshint: 24 | @./node_modules/.bin/jshint index.js 25 | 26 | package: clean test 27 | npm pack 28 | 29 | clean: 30 | rm -f $(PACKAGE_NAME)*.tgz 31 | 32 | .PHONY: test clean 33 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## [1.0.1] - 2020-03-23 5 | ### Added 6 | - None. 7 | ### Changed 8 | - Add files list to package.json so that test files are excluded from final tarball. 9 | ### Breaking Changes 10 | - None. 11 | 12 | ## [1.0.0] - 2020-03-23 (unpublished) 13 | ### Added 14 | - TypeScript definitions matching v0.x module. 15 | ### Changed 16 | - Added engines with min version of Node v10. Module continues to work on node versions going back to 0.8 but dev dependencies and tests require a modern version of Node. 17 | ### Breaking Changes 18 | - None for core library vs v0.x but the type definitions and engines changes may cause compile errors. 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rand-token", 3 | "version": "1.0.1", 4 | "description": "Generate random tokens", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "test": "make test" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/sehrope/node-rand-token" 13 | }, 14 | "keywords": [ 15 | "random", 16 | "uid", 17 | "token", 18 | "psuedorandom", 19 | "urandom", 20 | "crypto", 21 | "alpha-numeric" 22 | ], 23 | "author": "Sehrope Sarkuni ", 24 | "license": "MIT", 25 | "dependencies": {}, 26 | "devDependencies": { 27 | "jshint": "^2.11.0", 28 | "mocha": "^7.1.1", 29 | "tsd": "^0.11.0" 30 | }, 31 | "engines": { 32 | "node": ">= 10" 33 | }, 34 | "files": [ 35 | "index.js", 36 | "index.d.ts" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /index.test-d.ts: -------------------------------------------------------------------------------- 1 | import * as randToken from './index'; 2 | import { expectAssignable } from 'tsd'; 3 | 4 | expectAssignable(randToken.uid(10)); 5 | expectAssignable(randToken.uid(10, 'abcd1234')); 6 | 7 | expectAssignable(randToken.generate(10)); 8 | expectAssignable(randToken.generate(10, 'abcd1234')); 9 | 10 | expectAssignable(randToken.suid(10)); 11 | expectAssignable(randToken.suid(10, 1421452800000)); 12 | expectAssignable(randToken.suid(10, 1421452800000, 5)); 13 | 14 | expectAssignable(randToken.generator().generate(123)); 15 | expectAssignable(randToken.generator({ 16 | chars: 'abc1234', 17 | }).generate(123)); 18 | expectAssignable(randToken.generator({ 19 | source: 'default', 20 | }).generate(123)); 21 | expectAssignable(randToken.generator({ 22 | source: 'crypto', 23 | }).generate(123)); 24 | expectAssignable(randToken.generator({ 25 | source: 'math', 26 | }).generate(123)); 27 | expectAssignable(randToken.generator({ 28 | source: (size) => Buffer.alloc(size, 42), 29 | }).generate(123)); 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Sehrope Sarkuni 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const crypto = require('crypto'); 3 | 4 | describe('Create a default random token generater', function() { 5 | it('should create successfully', function() { 6 | var randtoken = require('../index.js'); 7 | }); 8 | 9 | it('should create tokens', function() { 10 | var randtoken = require('../index.js'); 11 | randtoken.generate(16); 12 | }); 13 | 14 | it('should create tokens of the specified length', function() { 15 | var randtoken = require('../index.js'); 16 | var token = randtoken.generate(16); 17 | assert(token.length, 16); 18 | }); 19 | 20 | it('should create tokens using the uid function', function() { 21 | var randtoken = require('../index.js'); 22 | var token = randtoken.uid(16); 23 | assert(token.length, 16); 24 | }); 25 | 26 | it('should create sequentially sorted tokens', function(done) { 27 | var randtoken = require('../index.js'); 28 | var token = randtoken.suid(16); 29 | function genAnotherAndCompare() { 30 | var secondToken = randtoken.suid(16); 31 | assert(token < secondToken); 32 | done(); 33 | }; 34 | setTimeout(genAnotherAndCompare, 10); 35 | }); 36 | }); 37 | 38 | var randtoken = require('../index.js'); 39 | 40 | var randSourceToTest = [ 41 | 'default', 42 | 'math', 43 | 'crypto', 44 | crypto.randomBytes 45 | ]; 46 | var tokenCharsToTest = [ 47 | {chars: 'a', regex: /^a{16}$/ }, 48 | {chars: 'alpha', regex: /^[a-z]{16}$/ }, 49 | {chars: 'ALPHA', regex: /^[A-Z]{16}$/ }, 50 | {chars: 'numeric', regex: /^[0-9]{16}$/ }, 51 | ]; 52 | 53 | function nameOf(test) { 54 | if (typeof(test) === 'function') { 55 | return test.name; 56 | } 57 | return test; 58 | } 59 | 60 | randSourceToTest.forEach(function(randSourceTest) { 61 | tokenCharsToTest.forEach(function(tokenCharTest) { 62 | describe('Generate a 16 character token with tokeChars=' + tokenCharTest.chars + 63 | ' and a randSource=' + nameOf(randSourceTest), function() { 64 | var generator; 65 | it('should create a generator successfully', function() { 66 | generator = randtoken.generator({source: randSourceTest, chars: tokenCharTest.chars}); 67 | }); 68 | 69 | it('should generate tokens that match the regex ' + tokenCharTest.regex, function() { 70 | for(var i=0;i<1000;i++) { 71 | var token = generator.generate(16); 72 | assert(tokenCharTest.regex.test(token)); 73 | } 74 | }); 75 | }); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Uses the default generator to generate a token of size characters. 3 | */ 4 | export function uid(size: number, chars?: string): string; 5 | 6 | /** 7 | * Alias for uid(...) function. 8 | */ 9 | export const generate: typeof uid; 10 | 11 | /** 12 | * Uses the default generator to generate mostly sequential ids 13 | * that can be compared with the usual string less-than/greater-than operators. 14 | */ 15 | export function suid(size: number, epoch?: number, prefixLength?: number): string; 16 | 17 | interface GeneratorOptions { 18 | /** 19 | * Source of randomness. 20 | * 21 | * Acceptable values are a string or a synchronous function with the 22 | * signature `(size: number): Buffer`. 23 | * 24 | * The following string values are also accepted: 25 | * 26 | * * `default` - This is a synonym for using the default of `crypto.pseudoRandomBytes`. You do not need to specify `default` and can simply create the generator without this option specified to get the same effect. 27 | * * `crypto` - This is a synonym for [`crypto.randomBytes`](http://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback). Note that this may throw an error during use if there is not enough entropy. 28 | * * `math` - This is a synonym for using the Math.random() function. 29 | */ 30 | source?: 'default' | 'math' | 'crypto' | ((size: number) => Buffer); 31 | 32 | /** 33 | * Characters to use when generating tokens. 34 | * 35 | * The following string values have special meaning: 36 | * 37 | * * `default` - default set of token characters which is numbers, lower case letters, and upper case letters (i.e. `0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`) 38 | * * `a-z` or `alpha` - lower case characters (i.e. `abcdefghijklmnopqrstuvwxyz`) 39 | * * `A-Z` or `ALPHA` - upper case characters (i.e. `ABCDEFGHIJKLMNOPQRSTUVWXYZ`) 40 | * * `0-9` or `numeric` - numbers only (i.e. `0123456789`) 41 | * * `base32` - use characters from the base32 alphabet, specifically `A-Z` and `2-7` 42 | * 43 | * Any other string value will be treated as an array of characters to use. 44 | * 45 | * Each character in the list has an equal chance of being used. For 46 | * example if a character is repeated twice, it will appear twice as often 47 | * in randomly generated tokens. `chars` may be at most 255 characters long. 48 | */ 49 | chars?: string; 50 | } 51 | 52 | interface RandTokenGenerator { 53 | generate(size: number): string; 54 | } 55 | 56 | /** 57 | * Create a random token generator using the given options. 58 | */ 59 | export function generator(options?: GeneratorOptions): RandTokenGenerator; 60 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | 4 | var crypto = require('crypto'); 5 | var assert = require('assert'); 6 | var cryptoRandomBytes = crypto.pseudoRandomBytes || crypto.randomBytes; 7 | 8 | var numeric = '0123456789'; 9 | var alphaLower = 'abcdefghijklmnopqrstuvwxyz'; 10 | var alphaUpper = alphaLower.toUpperCase(); 11 | // NOTE: This is explicitly in sortable order: 12 | var alphaNumeric = numeric + alphaUpper + alphaLower; 13 | 14 | var defaults = { 15 | "chars": 'default', 16 | "source": 'default' 17 | }; 18 | 19 | function validateTokenChars(tokenChars) { 20 | assert(tokenChars); 21 | assert(typeof(tokenChars) == 'string'); 22 | assert(tokenChars.length > 0); 23 | assert(tokenChars.length < 256); 24 | } 25 | 26 | function bufferAlloc(size) { 27 | if (Buffer.alloc) { 28 | return Buffer.alloc(size); 29 | } 30 | return new Buffer(size); 31 | } 32 | 33 | function buildGenerator(options) { 34 | assert(!options || typeof(options) == 'object'); 35 | options = options || {}; 36 | options.chars = options.chars || defaults.chars; 37 | options.source = options.source || defaults.source; 38 | 39 | // Allowed characters 40 | switch( options.chars ) { 41 | case 'default': 42 | options.chars = alphaNumeric; 43 | break; 44 | case 'a-z': 45 | case 'alpha': 46 | options.chars = alphaLower; 47 | break; 48 | case 'A-Z': 49 | case 'ALPHA': 50 | options.chars = alphaUpper; 51 | break; 52 | case '0-9': 53 | case 'numeric': 54 | options.chars = numeric; 55 | break; 56 | case 'base32': 57 | options.chars = alphaUpper + "234567"; 58 | break; 59 | default: 60 | // use the characters as is 61 | } 62 | validateTokenChars(options.chars); 63 | 64 | // Source of randomness: 65 | switch( options.source ) { 66 | case 'default': 67 | options.source = cryptoRandomBytes; 68 | break; 69 | case 'crypto': 70 | options.source = crypto.randomBytes; 71 | break; 72 | case 'math': 73 | options.source = function(size) { 74 | var buf = bufferAlloc(size); 75 | for(var i=0;i= 0); 110 | n = Math.floor(n); 111 | var ret = []; 112 | do { 113 | var index = n % 62; 114 | ret.push(alphaNumeric[index]); 115 | n = Math.floor(n / 62); 116 | } while( n > 0); 117 | return ret.reverse().join(""); 118 | } 119 | 120 | // Default epoch of "2000-01-01T00:00:00+00:00" 121 | var defaultEpoch = 946684800000; 122 | var defaultPrefixLength = 8; 123 | function suidPrefix(epoch, prefixLength) { 124 | var ret = base62(Date.now() - epoch); 125 | while( ret.length < prefixLength ) { 126 | ret = "0" + ret; 127 | } 128 | return ret; 129 | } 130 | 131 | var defaultGenerator = buildGenerator(); 132 | 133 | module.exports = { 134 | generator: buildGenerator, 135 | generate: defaultGenerator.generate, 136 | uid: defaultGenerator.generate, 137 | suid: function(length, epoch, prefixLength) { 138 | epoch = epoch || defaultEpoch; 139 | prefixLength = prefixLength || defaultPrefixLength; 140 | return suidPrefix(epoch, prefixLength) + defaultGenerator.generate(length); 141 | } 142 | }; 143 | })(); 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-rand-token 2 | 3 | Generate random tokens from your choice of randomness. 4 | 5 | [![NPM](https://nodei.co/npm/rand-token.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/rand-token/) 6 | 7 | [![Build Status](https://travis-ci.org/sehrope/node-rand-token.svg?branch=master)](https://travis-ci.org/sehrope/node-rand-token) 8 | 9 | 10 | # Installation 11 | 12 | Add it to your node.js project via: 13 | 14 | npm install rand-token --save 15 | 16 | # Usage 17 | 18 | // Create a token generator with the default settings: 19 | var randtoken = require('rand-token'); 20 | 21 | // Generate a 16 character alpha-numeric token: 22 | var token = randtoken.generate(16); 23 | 24 | // Use it as a replacement for uid: 25 | var uid = require('rand-token').uid; 26 | var token = uid(16); 27 | 28 | // Generate mostly sequential tokens: 29 | var suid = require('rand-token').suid; 30 | var token = suid(16); 31 | 32 | # TypeScript usage 33 | 34 | This module includes type definition: 35 | 36 | ```typescript 37 | import { uid, suid } from 'rand-token'; 38 | 39 | const token = uid(12); 40 | const otherToken = suid(16); 41 | ``` 42 | 43 | # Defaults 44 | 45 | The default set of allowed characters is all alpha-numeric characters. Specifically, lower case a through z, upper case A through Z, and the number 0 through 9. This gives you `(26 + 26 + 10)` = `62` possibilities per character. 46 | 47 | Using 8 character random tokens will give you a possible token space of 62^8 = 2.18 x 10^14 ~= 2^47 48 | 49 | Using 12 character random tokens will give you a possible token space of 62^12 = 3.22 x 10^21 ~= 2^71 50 | 51 | Using 16 character random tokens will give you a possible token space of 62^16 = 4.76 x 10^28 ~= 2^95 52 | 53 | # Functions 54 | 55 | ## uid(size) 56 | Uses the default generator to generate a token of `size` characters. 57 | 58 | ## suid(size, [epoch], [prefixLength]) 59 | Uses the default generator to generate *mostly* sequential ids that can be compared with the usual string less-than/greater-than operators. By mostly, it means that a second execution of this function within the same millisecond may generate an id that is less than the second. The same holds true for ids generated from separate node processes. There's no coordination whatsover. This is meant to be used for situations where being able to sort tokens would be convenient, but not strictly required. 60 | 61 | The generated tokens are of `size` characters prefixed with the time since the given epoch in base62 padded to `prefixLength` characters. The `epoch` parameter should be the unix time offset in milliseconds. The default `epoch` is 2000-01-01T00:00:00+00:00 and the default `prefixLength` is 8 characters. 62 | 63 | __NOTE:__ The prefix length is in addition to the `size` parameter. Calling this function as `suid(16)` will return a 24-character token back (8 + 16). 64 | 65 | ## generate(size, [chars]) 66 | 67 | Generates a token of `size` characters using either the specified `chars` or the default for the generator. 68 | 69 | If `chars` is specified it will be treated as an array of characters to use. Each character in the list has an equal chance of being used so if a character is repeated twice, it will appear twice as often in the randomly generated tokens. 70 | 71 | __Note__: Unlike the `generator(...)` function this function does not treat any string values of `chars` to be special and all values will be simply treated as an array of possible characters. 72 | 73 | ## generator([options]) 74 | 75 | Creates a custom token generator. 76 | 77 | Available options: 78 | 79 | * `source` - source of random bytes 80 | This should be either a string or a synchronous function with the signature `(size: number): Buffer`. 81 | 82 | The following string values are also accepted: 83 | 84 | * `default` - This is a synonym for using the default of `crypto.pseudoRandomBytes`. You do not need to specify `default` and can simply create the generator without this option specified to get the same effect. 85 | 86 | * `crypto` - This is a synonym for [`crypto.randomBytes`](http://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback). Note that this may throw an error during use if there is not enough entropy. 87 | 88 | * `math` - This is a synonym for using the Math.random() function. 89 | 90 | * `chars` - string representing the list of allowed characters to use for `generate(size)` calls. 91 | 92 | The following string values have special meaning: 93 | 94 | * `default` - default set of token characters which is numbers, lower case letters, and upper case letters (i.e. `0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`) 95 | * `a-z` || `alpha` - lower case characters (i.e. `abcdefghijklmnopqrstuvwxyz`) 96 | * `A-Z` || `ALPHA` - upper case characters (i.e. `ABCDEFGHIJKLMNOPQRSTUVWXYZ`) 97 | * `0-9` || `numeric` - numbers only (i.e. `0123456789`) 98 | * `base32` - use characters from the base32 alphabet, specifically `A-Z` and `2-7` 99 | 100 | Any other string value will be treated as an array of characters to use. 101 | 102 | Each character in the list has an equal chance of being used. For example if a character is repeated twice, it will appear twice as often in randomly generated tokens. `chars` may be at most 255 characters long. 103 | 104 | # Examples 105 | 106 | To replace usage of uid with the default generator (alpha-numeric): 107 | 108 | // Create a token generator with the default settings: 109 | var uid = require('rand-token').uid; 110 | 111 | // Generate a 16 character alpha-numeric token: 112 | var token = uid(16) 113 | 114 | To generate *mostly* sequential ids: 115 | 116 | // Create a token generator with the default settings: 117 | var suid = require('rand-token').suid; 118 | 119 | // Generate a 24 (16 + 8) character alpha-numeric token: 120 | var token = suid(16) 121 | 122 | To generate only lower case letters (a-z): 123 | 124 | // Create a token generator with the default settings: 125 | var randtoken = require('rand-token').generator({ 126 | chars: 'a-z' 127 | }); 128 | 129 | // Generate a 16 character token: 130 | var token = randtoken.generate(16); 131 | 132 | Alternatively, you can create a generator with the default options and pass the characters to use as the second parameter to `generate`: 133 | 134 | // Create a token generator with the default settings: 135 | var randtoken = require('rand-token').generator(); 136 | 137 | // Generate a 16 character token: 138 | var token = randtoken.generate(16, "abcdefghijklnmopqrstuvwxyz"); 139 | 140 | To generate only upper case letters with `crypto.randomBytes` as the random source: 141 | 142 | var crypto = require('crypto'); 143 | // Create the generator: 144 | var randtoken = require('rand-token').generator({ 145 | chars: 'A-Z', 146 | source: crypto.randomBytes 147 | }); 148 | 149 | // Generate a 16 character token: 150 | var token = randtoken.generate(16); 151 | 152 | 153 | # Dependencies 154 | 155 | None. 156 | 157 | # License 158 | 159 | This plugin is released under the MIT license. See the file [LICENSE](LICENSE). 160 | --------------------------------------------------------------------------------