123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- /*!
- * csrf
- * Copyright(c) 2014 Jonathan Ong
- * Copyright(c) 2015 Douglas Christopher Wilson
- * MIT Licensed
- */
- 'use strict'
- /**
- * Module dependencies.
- * @private
- */
- var rndm = require('rndm')
- var uid = require('uid-safe')
- var compare = require('tsscmp')
- var crypto = require('crypto')
- /**
- * Module variables.
- * @private
- */
- var EQUAL_GLOBAL_REGEXP = /=/g
- var PLUS_GLOBAL_REGEXP = /\+/g
- var SLASH_GLOBAL_REGEXP = /\//g
- /**
- * Module exports.
- * @public
- */
- module.exports = Tokens
- /**
- * Token generation/verification class.
- *
- * @param {object} [options]
- * @param {number} [options.saltLength=8] The string length of the salt
- * @param {number} [options.secretLength=18] The byte length of the secret key
- * @public
- */
- function Tokens (options) {
- if (!(this instanceof Tokens)) {
- return new Tokens(options)
- }
- var opts = options || {}
- var saltLength = opts.saltLength !== undefined
- ? opts.saltLength
- : 8
- if (typeof saltLength !== 'number' || !isFinite(saltLength) || saltLength < 1) {
- throw new TypeError('option saltLength must be finite number > 1')
- }
- var secretLength = opts.secretLength !== undefined
- ? opts.secretLength
- : 18
- if (typeof secretLength !== 'number' || !isFinite(secretLength) || secretLength < 1) {
- throw new TypeError('option secretLength must be finite number > 1')
- }
- this.saltLength = saltLength
- this.secretLength = secretLength
- }
- /**
- * Create a new CSRF token.
- *
- * @param {string} secret The secret for the token.
- * @public
- */
- Tokens.prototype.create = function create (secret) {
- if (!secret || typeof secret !== 'string') {
- throw new TypeError('argument secret is required')
- }
- return this._tokenize(secret, rndm(this.saltLength))
- }
- /**
- * Create a new secret key.
- *
- * @param {function} [callback]
- * @public
- */
- Tokens.prototype.secret = function secret (callback) {
- return uid(this.secretLength, callback)
- }
- /**
- * Create a new secret key synchronously.
- * @public
- */
- Tokens.prototype.secretSync = function secretSync () {
- return uid.sync(this.secretLength)
- }
- /**
- * Tokenize a secret and salt.
- * @private
- */
- Tokens.prototype._tokenize = function tokenize (secret, salt) {
- return salt + '-' + hash(salt + '-' + secret)
- }
- /**
- * Verify if a given token is valid for a given secret.
- *
- * @param {string} secret
- * @param {string} token
- * @public
- */
- Tokens.prototype.verify = function verify (secret, token) {
- if (!secret || typeof secret !== 'string') {
- return false
- }
- if (!token || typeof token !== 'string') {
- return false
- }
- var index = token.indexOf('-')
- if (index === -1) {
- return false
- }
- var salt = token.substr(0, index)
- var expected = this._tokenize(secret, salt)
- return compare(token, expected)
- }
- /**
- * Hash a string with SHA1, returning url-safe base64
- * @param {string} str
- * @private
- */
- function hash (str) {
- return crypto
- .createHash('sha1')
- .update(str, 'ascii')
- .digest('base64')
- .replace(PLUS_GLOBAL_REGEXP, '-')
- .replace(SLASH_GLOBAL_REGEXP, '_')
- .replace(EQUAL_GLOBAL_REGEXP, '')
- }
|