var crypto = require('crypto');
var jwt = require('jsonwebtoken');
var JWS_REGEX = /^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/;
var Base64 = require('Base64');

function makeUrlSafe(s) {
  /*
   * Makes the given base64 encoded string urlsafe
   */
  var escaped = s.replace(/\+/g, '-').replace(/\//g, '_');
  return escaped.replace(/^=+/, '').replace(/=+$/, '');
}

function decodeBase64Url(base64UrlString) {
  try {
    return Base64.atob(toBase64(base64UrlString));
  } catch (e) {
    /* istanbul ignore else */
    if (e.name === 'InvalidCharacterError') {
      return undefined;
    } else {
      throw e;
    }
  }
}

function safeJsonParse(thing) {
  if (typeof (thing) === 'object') return thing;
  try {
    return JSON.parse(thing);
  } catch (e) {
    return undefined;
  }
}

function padString(string) {
  var segmentLength = 4;
  var diff = string.length % segmentLength;
  if (!diff)
      return string;
  var padLength = segmentLength - diff;

  while (padLength--)
    string += '=';
  return string;
}

function toBase64(base64UrlString) {
  var b64str = padString(base64UrlString)
    .replace(/\-/g, '+') // eslint-disable-line no-useless-escape
    .replace(/_/g, '/');
  return b64str;
}

function headerFromJWS(jwsSig) {
  var encodedHeader = jwsSig.split('.', 1)[0];
  return safeJsonParse(decodeBase64Url(encodedHeader));
}

exports.headerFromJWS = headerFromJWS;

exports.sign = function(apiSecret, feedId) {
  /*
   * Setup sha1 based on the secret
   * Get the digest of the value
   * Base64 encode the result
   *
   * Also see
   * https://github.com/tbarbugli/stream-ruby/blob/master/lib/stream/signer.rb
   * https://github.com/tschellenbach/stream-python/blob/master/stream/signing.py
   *
   * Steps
   * apiSecret: tfq2sdqpj9g446sbv653x3aqmgn33hsn8uzdc9jpskaw8mj6vsnhzswuwptuj9su
   * feedId: flat1
   * digest: Q\xb6\xd5+\x82\xd58\xdeu\x80\xc5\xe3\xb8\xa5bL1\xf1\xa3\xdb
   * token: UbbVK4LVON51gMXjuKViTDHxo9s
   */
  var hashedSecret = new crypto.createHash('sha1').update(apiSecret).digest();
  var hmac = crypto.createHmac('sha1', hashedSecret);
  var digest = hmac.update(feedId).digest('base64');
  var token = makeUrlSafe(digest);
  return token;
};

exports.JWTScopeToken = function(apiSecret, resource, action, opts) {
  /**
   * Creates the JWT token for feedId, resource and action using the apiSecret
   * @method JWTScopeToken
   * @memberof signing
   * @private
   * @param {string} apiSecret - API Secret key
   * @param {string} resource - JWT payload resource
   * @param {string} action - JWT payload action
   * @param {object} [options] - Optional additional options
   * @param {string} [options.feedId] - JWT payload feed identifier
   * @param {string} [options.userId] - JWT payload user identifier
   * @return {string} JWT Token
   */
  var options = opts || {},
      noTimestamp = options.expireTokens ? !options.expireTokens : true;
  var payload = {
    resource: resource,
    action: action,
  };

  if (options.feedId) {
    payload['feed_id'] = options.feedId;
  }

  if (options.userId) {
    payload['user_id'] = options.userId;
  }

  var token = jwt.sign(payload, apiSecret, { algorithm: 'HS256', noTimestamp: noTimestamp });
  return token;
};

exports.JWTUserSessionToken = function(apiSecret, userId, extraData={}, jwtOptions={}) {
  /**
   * Creates the JWT token that can be used for a UserSession
   * @method JWTUserSessionToken
   * @memberof signing
   * @private
   * @param {string} apiSecret - API Secret key
   * @param {string} userId - The user_id key in the JWT payload
   * @param {string} [extraData] - Extra that should be part of the JWT token
   * @param {object} [jwtOptions] - Options that can be past to jwt.sign
   * @return {string} JWT Token
   */
  var payload = {
    user_id: userId,
    ...extraData,
  };

  var token = jwt.sign(payload, apiSecret, { algorithm: 'HS256', ...jwtOptions });
  return token;
};


exports.isJWTSignature = function(signature) {
  /**
   * check if token is a valid JWT token
   * @method isJWTSignature
   * @memberof signing
   * @private
   * @param {string} signature - Signature to check
   * @return {boolean}
   */
  var token = signature.split(' ')[1] || signature;
  return JWS_REGEX.test(token) && !!headerFromJWS(token);
};

@@ -3760,24 +3760,119 @@
 Token%0A   */%0A
+  if (typeof userId !== 'string') %7B%0A    throw new TypeError('userId should be a string');%0A  %7D%0A%0A
   var payloa

Make sure JWTUserSessionToken only accepts a string for userId