379

In this question Erik needs to generate a secure random token in Node.js. There's the method crypto.randomBytes that generates a random Buffer. However, the base64 encoding in node is not url-safe, it includes / and + instead of - and _. Therefore, the easiest way to generate such token I've found is

require('crypto').randomBytes(48, function(ex, buf) {
    token = buf.toString('base64').replace(/\//g,'_').replace(/\+/g,'-');
});

Is there a more elegant way?

9
  • What is the rest of the code?
    – Lion789
    Commented Aug 3, 2013 at 5:01
  • 7
    There's nothing more needed. What rest would you like to see?
    – Hubert OG
    Commented Aug 12, 2013 at 13:46
  • Nevermind, I got it to work, was just unsure of how you threw it in, but got a better grasp of the concept
    – Lion789
    Commented Aug 12, 2013 at 19:22
  • 1
    Shameless self-plug, I created yet another npm package: tokgen. You can specify allowed characters using a range syntax similar to character classes in regular expressions ('a-zA-Z0-9_-').
    – Max Truxa
    Commented Aug 9, 2016 at 19:18
  • 1
    This may be convenient for anyone who'd like a specific string length. The 3/4th's is to handle the base conversion. /*returns a base64 encoded string of length*/ function randomString(length){ return crypto.randomBytes(length*3/4).toString('base64'); } Works nice for those databases with those character limits. Commented Oct 19, 2017 at 21:51

16 Answers 16

475

Try crypto.randomBytes():

require('crypto').randomBytes(48, function(err, buffer) {
  var token = buffer.toString('hex');
});

The 'hex' encoding works in node v0.6.x or newer.

13
  • 4
    That seems better, thanks! A 'base64-url' encoding would be nice, though.
    – Hubert OG
    Commented Jan 13, 2012 at 19:40
  • 2
    Thanks for the tip, but I think the OP simply wanted the already-standard RFC 3548 section 4 "Base 64 Encoding with URL and Filename Safe Alphabet". IMO, replacing the characters is "elegant enough".
    – natevw
    Commented Oct 7, 2013 at 21:58
  • 18
    If you're looking for the above as a bash one-liner, you can do node -e "require('crypto').randomBytes(48, function(ex, buf) { console.log(buf.toString('hex')) });" Commented Feb 25, 2014 at 18:00
  • 28
    And you can always do buf.toString('base64') to get a Base64-encoded number. Commented Feb 25, 2014 at 18:11
  • 7
    A slightly more compact version of Dmitry's excellent one-liner: node -p "require('crypto').randomBytes(48).toString('hex');" (subbing base64 for hex) if desired
    – eddies
    Commented Aug 6, 2018 at 7:56
350

Synchronous option in-case if you are not a JS expert like me. Had to spend some time on how to access the inline function variable

var token = crypto.randomBytes(64).toString('hex');
7
  • 10
    Also in case you don't want to have everything nested. Thanks! Commented Oct 7, 2016 at 16:50
  • 5
    While this definitely works, do note that in most cases you'll want the async option demonstrated in thejh's answer.
    – Triforcey
    Commented Jun 10, 2018 at 21:18
  • 2
    const generateToken = (): Promise<string> => new Promise(resolve => randomBytes(48, (err, buffer) => resolve(buffer.toString('hex'))));
    – yantrab
    Commented May 1, 2019 at 4:21
  • 2
    @Triforcey can you explain why you usually would want the async option?
    – dcts
    Commented Jul 16, 2019 at 5:17
  • 2
    @thomas Random data can take a while to calculate depending on hardware. In some cases if the computer runs out of random data it'll just return something in it's place. However in other cases it's possible the computer will delay the return of random data (which is actually what you would want) resulting in a slow call.
    – Triforcey
    Commented Jul 17, 2019 at 15:38
113

1. Using nanoid third party library [NEW!]


A tiny, secure, URL-friendly, unique string ID generator for JavaScript

https://github.com/ai/nanoid

import { nanoid } from "nanoid";
const id = nanoid(48);

2. Base 64 Encoding with URL and Filename Safe Alphabet


Page 7 of RCF 4648 describes how to encode in base 64 with URL safety.

This is natively supported by Node.js >=v14.18.0:

const crypto = require("crypto");

/** Sync */
function randomStringAsBase64Url(size) {
  return crypto.randomBytes(size).toString("base64url");
}

Usage example:

randomStringAsBase64Url(20);
// Returns "AXSGpLVjne_f7w5Xg-fWdoBwbfs" which is 27 characters length.

Note that the returned string length will not match with the size argument (size != final length).

If you are using Node.js <v14.18.0 you can use an existing library like base64url to do the job. The function will be:

const crypto = require("crypto");
const base64url = require("base64url");
    
/** Sync */
function randomStringAsBase64Url(size) {
  return base64url(crypto.randomBytes(size));
}

3. Crypto random values from limited set of characters


Beware that with this solution the generated random string is not uniformly distributed.

You can also build a strong random string from a limited set of characters like that:

const crypto = require("crypto");

/** Sync */
function randomString(length, chars) {
  if (!chars) {
    throw new Error("Argument 'chars' is undefined");
  }

  const charsLength = chars.length;
  if (charsLength > 256) {
    throw new Error("Argument 'chars' should not have more than 256 characters"
      + ", otherwise unpredictability will be broken");
  }

  const randomBytes = crypto.randomBytes(length);
  let result = new Array(length);

  let cursor = 0;
  for (let i = 0; i < length; i++) {
    cursor += randomBytes[i];
    result[i] = chars[cursor % charsLength];
  }

  return result.join("");
}

/** Sync */
function randomAsciiString(length) {
  return randomString(length,
    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
}

Usage example:

randomAsciiString(20);
// Returns "rmRptK5niTSey7NlDk5y" which is 20 characters length.

randomString(20, "ABCDEFG");
// Returns "CCBAAGDGBBEGBDBECDCE" which is 20 characters length.
9
  • 2
    @Lexynux Solution 1 (Base 64 Encoding with URL and Filename Safe Alphabet) because it is the strongest solution in term of security. This solution only encode the key and does not interfere with the key production process.
    – Yves M.
    Commented Dec 28, 2015 at 13:28
  • 9
    Beware that the generated random string is not uniformly distributed. An easy example to show this is, that for a character set of length 255, and a string length of 1, the chance of the first character appearing is twice as high. Commented Jul 25, 2016 at 4:09
  • 1
    @Dodekeract Yes, you are talking about solution 2.. That's why solution 1 is way more strong
    – Yves M.
    Commented Jul 25, 2016 at 8:41
  • 2
    @AlexeySh. a bit hard to remember as this was nearly a decade ago by now. I think it’s about the danger of just using mod/division rest to hash which character you get out of the randomness. For example, if you roll a random byte (0-255) and then only provide 255 possible characters the random 0 = random 255 = first possible character, so it’s not uniform Commented Apr 1, 2023 at 0:25
  • 1
    For option 2, note that since Node v14.18.0 'base64url' is supported as a built-in encoding, so you can just crypto.randomBytes(size).toString('base64url') now.
    – Tim Perry
    Commented Jul 4, 2023 at 17:29
22

As of Node.js 14.18 and 15.7, url-safe base64 encoding support is built-in:

const token = crypto.randomBytes(48).toString('base64url');

If you want to use the async version (because the function may have to wait for entropy), it can be promisified to align better with modern patterns:

const randomBytesAsync = util.promisify(crypto.randomBytes);

const token = (await randomBytesAsync(48)).toString('base64url');
21

The up-to-date right way to do this asynchronously using ES 2016 standards of async and await (as of Node 7) would be the following:

const crypto = require('crypto');

function generateToken({ stringBase = 'base64', byteLength = 48 } = {}) {
  return new Promise((resolve, reject) => {
    crypto.randomBytes(byteLength, (err, buffer) => {
      if (err) {
        reject(err);
      } else {
        resolve(buffer.toString(stringBase));
      }
    });
  });
}

async function handler(req, res) {
   // default token length
   const newToken = await generateToken();
   console.log('newToken', newToken);

   // pass in parameters - adjust byte length
   const shortToken = await generateToken({byteLength: 20});
   console.log('newToken', shortToken);
}

This works out of the box in Node 7 without any Babel transformations

1
14

Random URL and filename string safe (1 liner)

Crypto.randomBytes(48).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
1
  • A wonderful answer in it's simplicity! Just be aware that it could stall the event loop in an indeterministic way (only relevant if it's used often, in a somewhat loaded, time-sensitive system). Otherwise, do the same thing, but using the async version of randomBytes. See nodejs.org/api/… Commented Sep 28, 2016 at 19:50
11

With async/await and promisification.

const crypto = require('crypto')
const randomBytes = Util.promisify(crypto.randomBytes)
const plain = (await randomBytes(24)).toString('base64').replace(/\W/g, '')

Generates something similar to VjocVHdFiz5vGHnlnwqJKN0NdeHcz8eM

10

Check out:

var crypto = require('crypto');
crypto.randomBytes(Math.ceil(length/2)).toString('hex').slice(0,length);
2
  • 1
    Nice! Absolutely underrated solution. Would be great if you rename "length" to "desiredLength" and initiate it with a value before using it :) Commented Jul 25, 2017 at 21:07
  • For anyone wondering, the ceil and slice calls are necessary for desired lengths that are odd. For even lengths, they don't change anything.
    – Seth
    Commented Sep 12, 2018 at 20:42
8

crypto-random-string is a nice module for this.

const cryptoRandomString = require('crypto-random-string');
 
cryptoRandomString({length: 10});                          // => '2cf05d94db'
cryptoRandomString({length: 10, type: 'base64'});          // => 'YMiMbaQl6I'
cryptoRandomString({length: 10, type: 'url-safe'});        // => 'YN-tqc8pOw'
cryptoRandomString({length: 10, type: 'numeric'});         // => '8314659141'
cryptoRandomString({length: 6, type: 'distinguishable'});  // => 'CDEHKM'
cryptoRandomString({length: 10, type: 'ascii-printable'}); // => '`#Rt8$IK>B'
cryptoRandomString({length: 10, type: 'alphanumeric'});    // => 'DMuKL8YtE7'
cryptoRandomString({length: 10, characters: 'abc'});       // => 'abaaccabac'

cryptoRandomString.async(options) add .async if you want to get a promise.

5

in your terminal just write

node -e "console.log(crypto.randomBytes(48).toString('hex'))"

Or in your code use:

const randomToken = () => {
   crypto.randomBytes(48).toString('hex');
}
4

Look at real_ates ES2016 way, it's more correct.

ECMAScript 2016 (ES7) way

import crypto from 'crypto';

function spawnTokenBuf() {
    return function(callback) {
        crypto.randomBytes(48, callback);
    };
}

async function() {
    console.log((await spawnTokenBuf()).toString('base64'));
};

Generator/Yield Way

var crypto = require('crypto');
var co = require('co');

function spawnTokenBuf() {
    return function(callback) {
        crypto.randomBytes(48, callback);
    };
}

co(function* () {
    console.log((yield spawnTokenBuf()).toString('base64'));
});
5
  • @Jeffpowrs Indeed, Javascript is upgrading :) Lookup Promises and Generators!
    – basickarl
    Commented Nov 16, 2015 at 21:54
  • try await, another ECMA7 promise handler
    – Jain
    Commented Jul 8, 2016 at 6:50
  • I think that you should make the ES 2016 the first example on this as it is moving towards the "right way to do it" in most cases
    – real_ate
    Commented May 9, 2017 at 10:00
  • I added an answer of my own below that was specific to Node (using require instead of import). Was there a particular reason why you're using import? Do you have babel running?
    – real_ate
    Commented May 9, 2017 at 15:31
  • @real_ate Indeed I was, I've reverted to using CommonJS until import is officially supported.
    – basickarl
    Commented May 9, 2017 at 21:20
3

https://www.npmjs.com/package/crypto-extra has a method for it :)

var value = crypto.random(/* desired length */)
1
  • great! But isnt it .randomString (length, charset) (see documentation). So you could user for example crypto.randomString(12).
    – dcts
    Commented Jan 13, 2020 at 18:32
3

The npm module anyid provides flexible API to generate various kinds of string ID / code.

To generate random string in A-Za-z0-9 using 48 random bytes:

const id = anyid().encode('Aa0').bits(48 * 8).random().id();
// G4NtiI9OYbSgVl3EAkkoxHKyxBAWzcTI7aH13yIUNggIaNqPQoSS7SpcalIqX0qGZ

To generate fixed length alphabet only string filled by random bytes:

const id = anyid().encode('Aa').length(20).random().id();
// qgQBBtDwGMuFHXeoVLpt

Internally it uses crypto.randomBytes() to generate random.

2

Simple function that gets you a token that is URL safe and has base64 encoding! It's a combination of 2 answers from above.

const randomToken = () => {
    crypto.randomBytes(64).toString('base64').replace(/\//g,'_').replace(/\+/g,'-');
}
2

0 dependency free solution... Works in browsers, deno & nodejs (with new global web crypto)

const random = size => btoa(
  String.fromCharCode(
    ...crypto.getRandomValues(
      new Uint8Array(size)
    )
  )
).replaceAll('+', 'x').replaceAll('/', 'I').slice(0, size)

for (let i = 5; i--;) console.log(random(16))

All doe I would just have used one single uint8array \w predefined length and called crypto.getRandomValues whenever I need something uniq (and slice it if i have to) and never deal with strings or base64, base64 is just unnecessary overhead. (allocating lots of buffer to fast can be costly)

const buf256 = new Uint8Array(256)
const random = crypto.getRandomValues.bind(crypto, buf256)

for (let i = 5; i--;) random()//.slice()
0

You can use the random-token lib. it's very easy to use . :)

var randomToken = require('random-token').create('abcdefghijklmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789');
var token = randomToken(16);

And also you can not use different salt

var randomToken = require('random-token');
var token = randomToken(16); // output -> d8d4kd29c40f021 ```

Not the answer you're looking for? Browse other questions tagged or ask your own question.