
var rndm = require('rndm')
var scmp = require('scmp')
var uid = require('uid-safe')
var crypto = require('crypto')
var escape = require('base64-url').escape

module.exports = csrfTokens

function csrfTokens(options) {
  options = options || {}

  // adjustable lengths
  var secretLength = options.secretLength || 18 // the longer the better
  var saltLength = options.saltLength || 8 // doesn't need to be long

  // convert a secret + a salt to a token
  // this does NOT have to be cryptographically secure, so we don't use HMAC,
  // and we use sha1 because sha256 is unnecessarily long for cookies and stuff
  var tokenize = options.tokenize || csrfTokens.tokenize

  return {
    // create a secret key
    // this __should__ be cryptographically secure,
    // but generally client's can't/shouldn't-be-able-to access this so it really doesn't matter.
    secret: function secret(cb) {
      return uid(secretLength, cb)
    },

    // a sync version of secret()
    secretSync: function secretSync() {
      return uid.sync(secretLength)
    },

    // create a csrf token
    create: function create(secret) {
      return tokenize(secret, rndm(saltLength))
    },

    // verify whether a token is valid
    verify: function verify(secret, token) {
      if (!secret || typeof secret !== 'string') return false
      if (!token || typeof token !== 'string') return false
      var expected = tokenize(secret, token.split('-')[0])
      if (!expected) return false
      return scmp(token, expected)
    },
  }
}

csrfTokens.tokenize = function tokenize(secret, salt) {
  var hash = escape(crypto
    .createHash('sha1')
    .update(salt)
    .update('-')
    .update(secret)
    .digest('base64'))
  return salt + '-' + hash
}
