JSPM

base62-token

1.1.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 54
  • Score
    100M100P100Q56021F
  • License SEE LICENSE IN LICENSE

Generate & Verify GitHub-style & npm-style Base62 Tokens

Package Exports

  • base62-token
  • base62-token/base62-token.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (base62-token) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

base62-token.js

Generate & Verify GitHub-style & npm-style Base62 Tokens

Works in Vanilla JS (Browsers), Node.js, and Webpack.

Online Demo

See the online Base62 token generator & verifier in action:

Install

Browser

<script src="https://unpkg.com/crc-32"></script>
<script src="https://unpkg.com/base62-token"></script>
var Base62Token = window.Base62Token;

Node.js / Webpack

npm install --save base62-token
var Base62Token = require("base62-token");

Usage

var dict = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var b62Token = Base62Token.create(dict);

var token = b62Token.generate("abc_", 30);
var verified = b62Token.verify(token);

API

Base62Token.generateDictionary(); // Return the Lexographic (a.k.a. GMP)
                                  // Base62 dictionary.

Base62Token.create(dictionary);   // Creates a token generator and verifier
                                  // 'dictionary' is any 62-char alphabet.
                                  // Returns a generator / verifier instance.

b62Token.generate(prefix, length); // Returns token string.
b62Token.verify(token);            // Returns true / false.
Base62Token.BITS_PER_CHARACTER    // 5.954196310386876
                                  // For reference: Base64 is an even 6

Base62Token.calcMinChars(bitlen); // calculate the minimum number of chars
                                  // needed to guarantee the target entropy.
                                  // ex: 173-bit entropy needs 30 chars

Base62Token.calcMinBits(charlen); // calculate the minimum entropy guaranteed
                                  // by the given number of characters
                                  // ex: 30 chars guarantees 178-bit entropy.

Base62Token.checksum(dict, str);  // generates an (unsigned) CRC-32 checksum
                                  // for the given string (where each char is
                                  // treated as a single byte).
                                  // Returns the Base62 encoded unsigned int.

Base62Token.encode(dict, n, pad); // encode a 32-bit int (i.e. CRC-32 checksum)
                                  // as Base62 in the given dictionary, with a
                                  // default pad of 6 (guarantees 32-bits).

Base62 Token Spec

GitHub Token Breakdown

The 40-character tokens are broken down into 3 consecutive parts:

pre_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxcccccc

  • Prefix: 4-char (ex: ghx_)
  • Entropy: 30-char (178-bits + leading 0 padding)
    • BITS_PER_CHAR = Math.log(62) / Math.log(2) // about 5.9541
    • BITS_PER_CHAR * 30 // about 178.6258
  • Checksum: 6-char CRC32 (32-bits, 4 bytes, 6 base62 characters)
    • (of entropy-only, not prefix)
    • BITS_PER_CHAR * 5 // about 35.7251
Prefix Entropy Checksum
pre_ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx cccccc

See

Pseudocode

const DICT = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
const PREFIX_LEN = 4
const CHECKSUM_LEN = 6

func GenerateBase62Token(prefix string, len int) string {
    entropy := []string{}
    for 0..len {
        index := math.RandomInt(62) // 0..61
        char := DICT[index]
        entropy = append(entropy, char)
    }
    chksum := crc32.Checksum(entropy) // uint32

    pad := CHECKSUM_LEN
    chksum62 := base62.Encode(DICT, chksum, pad)

    // ex: "ghp_" + "zQWBuTSOoRi4A9spHcVY5ncnsDkxkJ" + "0mLq17"
    return prefix + string(entropy) + chksum62
}

func VerifyBase62Token(token string) bool {
    // prefix is not used
    entropy := token[PREFIX_LEN:len(token)-CHECKSUM_LEN]
    chksum := base62.Decode(DICT, token[len(token)-CHECKSUM_LEN:]) // uint32

    return crc32.Checksum(entropy) == chksum
}

Standard Base62 Dictionaries

There are 3 widely-used, generic Base62 dictionaries, all of which are based on the alphanumeric character set (i.e. 0-9, A-Z, a-z).

For general encoding and decoding, you should use one of these:

  • Lexographic (digits, upper, lower)
    0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
  • BaseX (digits, lower, upper)
    0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
  • Truncated Base64 (upper, lower, digits)
    ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789

GitHub and NPM use the Lexographic (a.k.a. GMP) Base62 alphabet.

Legal

For a business license and/or commercial support ($99/year), please contact Root.

Copyright 2022 AJ ONeal
Copyright 2022 Root

MPL-2.0 (Open Source) | Terms of Use | Privacy Policy