383

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? Commented Aug 3, 2013 at 5:01
  • 7
    There's nothing more needed. What rest would you like to see? 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 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_-'). 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

478

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.

Updates for ES2021+ using Buffer

import {randomBytes} from "node:crypto";

function tokenGenerate(length=56) {
    return Buffer.from(randomBytes(length)).toString('hex');
}
Sign up to request clarification or add additional context in comments.

13 Comments

That seems better, thanks! A 'base64-url' encoding would be nice, though.
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')) });"
And you can always do buf.toString('base64') to get a Base64-encoded number.
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
@MuhammadUmer, assuming you are still using all of the 48 bytes generated in this example, the hex string will simply be longer than the base64 one, but have the same amount of randomness
|
357

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 Comments

Also in case you don't want to have everything nested. Thanks!
While this definitely works, do note that in most cases you'll want the async option demonstrated in thejh's answer.
const generateToken = (): Promise<string> => new Promise(resolve => randomBytes(48, (err, buffer) => resolve(buffer.toString('hex'))));
@Triforcey can you explain why you usually would want the async option?
@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.
|
114

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 Comments

@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.
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.
@Dodekeract Yes, you are talking about solution 2.. That's why solution 1 is way more strong
@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
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.
|
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');

Comments

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 Comment

I've updated this example to incorporate the newer method of passing named parameters as described here: 2ality.com/2011/11/keyword-parameters.html
14

Random URL and filename string safe (1 liner)

Crypto.randomBytes(48).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');

1 Comment

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/…
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

Comments

10

Check out:

var crypto = require('crypto');
crypto.randomBytes(Math.ceil(length/2)).toString('hex').slice(0,length);

2 Comments

Nice! Absolutely underrated solution. Would be great if you rename "length" to "desiredLength" and initiate it with a value before using it :)
For anyone wondering, the ceil and slice calls are necessary for desired lengths that are odd. For even lengths, they don't change anything.
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.

Comments

6

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');
}

Comments

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 Comments

@Jeffpowrs Indeed, Javascript is upgrading :) Lookup Promises and Generators!
try await, another ECMA7 promise handler
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
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 Indeed I was, I've reverted to using CommonJS until import is officially supported.
3

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

var value = crypto.random(/* desired length */)

1 Comment

great! But isnt it .randomString (length, charset) (see documentation). So you could user for example crypto.randomString(12).
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.

Comments

3

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()

Comments

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,'-');
}

Comments

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 ```

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.