diff options
Diffstat (limited to 'node_modules/jsonwebtoken/sign.js')
-rw-r--r-- | node_modules/jsonwebtoken/sign.js | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/node_modules/jsonwebtoken/sign.js b/node_modules/jsonwebtoken/sign.js new file mode 100644 index 0000000..f649ce4 --- /dev/null +++ b/node_modules/jsonwebtoken/sign.js @@ -0,0 +1,206 @@ +var timespan = require('./lib/timespan'); +var PS_SUPPORTED = require('./lib/psSupported'); +var jws = require('jws'); +var includes = require('lodash.includes'); +var isBoolean = require('lodash.isboolean'); +var isInteger = require('lodash.isinteger'); +var isNumber = require('lodash.isnumber'); +var isPlainObject = require('lodash.isplainobject'); +var isString = require('lodash.isstring'); +var once = require('lodash.once'); + +var SUPPORTED_ALGS = ['RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512', 'HS256', 'HS384', 'HS512', 'none'] +if (PS_SUPPORTED) { + SUPPORTED_ALGS.splice(3, 0, 'PS256', 'PS384', 'PS512'); +} + +var sign_options_schema = { + expiresIn: { isValid: function(value) { return isInteger(value) || (isString(value) && value); }, message: '"expiresIn" should be a number of seconds or string representing a timespan' }, + notBefore: { isValid: function(value) { return isInteger(value) || (isString(value) && value); }, message: '"notBefore" should be a number of seconds or string representing a timespan' }, + audience: { isValid: function(value) { return isString(value) || Array.isArray(value); }, message: '"audience" must be a string or array' }, + algorithm: { isValid: includes.bind(null, SUPPORTED_ALGS), message: '"algorithm" must be a valid string enum value' }, + header: { isValid: isPlainObject, message: '"header" must be an object' }, + encoding: { isValid: isString, message: '"encoding" must be a string' }, + issuer: { isValid: isString, message: '"issuer" must be a string' }, + subject: { isValid: isString, message: '"subject" must be a string' }, + jwtid: { isValid: isString, message: '"jwtid" must be a string' }, + noTimestamp: { isValid: isBoolean, message: '"noTimestamp" must be a boolean' }, + keyid: { isValid: isString, message: '"keyid" must be a string' }, + mutatePayload: { isValid: isBoolean, message: '"mutatePayload" must be a boolean' } +}; + +var registered_claims_schema = { + iat: { isValid: isNumber, message: '"iat" should be a number of seconds' }, + exp: { isValid: isNumber, message: '"exp" should be a number of seconds' }, + nbf: { isValid: isNumber, message: '"nbf" should be a number of seconds' } +}; + +function validate(schema, allowUnknown, object, parameterName) { + if (!isPlainObject(object)) { + throw new Error('Expected "' + parameterName + '" to be a plain object.'); + } + Object.keys(object) + .forEach(function(key) { + var validator = schema[key]; + if (!validator) { + if (!allowUnknown) { + throw new Error('"' + key + '" is not allowed in "' + parameterName + '"'); + } + return; + } + if (!validator.isValid(object[key])) { + throw new Error(validator.message); + } + }); +} + +function validateOptions(options) { + return validate(sign_options_schema, false, options, 'options'); +} + +function validatePayload(payload) { + return validate(registered_claims_schema, true, payload, 'payload'); +} + +var options_to_payload = { + 'audience': 'aud', + 'issuer': 'iss', + 'subject': 'sub', + 'jwtid': 'jti' +}; + +var options_for_objects = [ + 'expiresIn', + 'notBefore', + 'noTimestamp', + 'audience', + 'issuer', + 'subject', + 'jwtid', +]; + +module.exports = function (payload, secretOrPrivateKey, options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } else { + options = options || {}; + } + + var isObjectPayload = typeof payload === 'object' && + !Buffer.isBuffer(payload); + + var header = Object.assign({ + alg: options.algorithm || 'HS256', + typ: isObjectPayload ? 'JWT' : undefined, + kid: options.keyid + }, options.header); + + function failure(err) { + if (callback) { + return callback(err); + } + throw err; + } + + if (!secretOrPrivateKey && options.algorithm !== 'none') { + return failure(new Error('secretOrPrivateKey must have a value')); + } + + if (typeof payload === 'undefined') { + return failure(new Error('payload is required')); + } else if (isObjectPayload) { + try { + validatePayload(payload); + } + catch (error) { + return failure(error); + } + if (!options.mutatePayload) { + payload = Object.assign({},payload); + } + } else { + var invalid_options = options_for_objects.filter(function (opt) { + return typeof options[opt] !== 'undefined'; + }); + + if (invalid_options.length > 0) { + return failure(new Error('invalid ' + invalid_options.join(',') + ' option for ' + (typeof payload ) + ' payload')); + } + } + + if (typeof payload.exp !== 'undefined' && typeof options.expiresIn !== 'undefined') { + return failure(new Error('Bad "options.expiresIn" option the payload already has an "exp" property.')); + } + + if (typeof payload.nbf !== 'undefined' && typeof options.notBefore !== 'undefined') { + return failure(new Error('Bad "options.notBefore" option the payload already has an "nbf" property.')); + } + + try { + validateOptions(options); + } + catch (error) { + return failure(error); + } + + var timestamp = payload.iat || Math.floor(Date.now() / 1000); + + if (options.noTimestamp) { + delete payload.iat; + } else if (isObjectPayload) { + payload.iat = timestamp; + } + + if (typeof options.notBefore !== 'undefined') { + try { + payload.nbf = timespan(options.notBefore, timestamp); + } + catch (err) { + return failure(err); + } + if (typeof payload.nbf === 'undefined') { + return failure(new Error('"notBefore" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60')); + } + } + + if (typeof options.expiresIn !== 'undefined' && typeof payload === 'object') { + try { + payload.exp = timespan(options.expiresIn, timestamp); + } + catch (err) { + return failure(err); + } + if (typeof payload.exp === 'undefined') { + return failure(new Error('"expiresIn" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60')); + } + } + + Object.keys(options_to_payload).forEach(function (key) { + var claim = options_to_payload[key]; + if (typeof options[key] !== 'undefined') { + if (typeof payload[claim] !== 'undefined') { + return failure(new Error('Bad "options.' + key + '" option. The payload already has an "' + claim + '" property.')); + } + payload[claim] = options[key]; + } + }); + + var encoding = options.encoding || 'utf8'; + + if (typeof callback === 'function') { + callback = callback && once(callback); + + jws.createSign({ + header: header, + privateKey: secretOrPrivateKey, + payload: payload, + encoding: encoding + }).once('error', callback) + .once('done', function (signature) { + callback(null, signature); + }); + } else { + return jws.sign({header: header, payload: payload, secret: secretOrPrivateKey, encoding: encoding}); + } +}; |