thoughts/node_modules/jsonwebtoken/sign.js
node_modules/jsonwebtoken/sign.jsRaw
1var Joi = require('joi');
2var timespan = require('./lib/timespan');
3var xtend = require('xtend');
4var jws = require('jws');
5var once = require('lodash.once');
6
7var sign_options_schema = Joi.object().keys({
8 expiresIn: [Joi.number().integer(), Joi.string()],
9 notBefore: [Joi.number().integer(), Joi.string()],
10 audience: [Joi.string(), Joi.array()],
11 algorithm: Joi.string().valid('RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512', 'HS256', 'HS384', 'HS512', 'none'),
12 header: Joi.object(),
13 encoding: Joi.string(),
14 issuer: Joi.string(),
15 subject: Joi.string(),
16 jwtid: Joi.string(),
17 noTimestamp: Joi.boolean()
18});
19
20var registered_claims_schema = Joi.object().keys({
21 iat: Joi.number(),
22 exp: Joi.number(),
23 nbf: Joi.number()
24}).unknown();
25
26
27var options_to_payload = {
28 'audience': 'aud',
29 'issuer': 'iss',
30 'subject': 'sub',
31 'jwtid': 'jti'
32};
33
34var options_for_objects = [
35 'expiresIn',
36 'notBefore',
37 'noTimestamp',
38 'audience',
39 'issuer',
40 'subject',
41 'jwtid',
42];
43
44module.exports = function (payload, secretOrPrivateKey, options, callback) {
45 options = options || {};
46
47 var isObjectPayload = typeof payload === 'object' &&
48 !Buffer.isBuffer(payload);
49
50 var header = xtend({
51 alg: options.algorithm || 'HS256',
52 typ: isObjectPayload ? 'JWT' : undefined
53 }, options.header);
54
55 function failure(err) {
56 if (callback) {
57 return callback(err);
58 }
59 throw err;
60 }
61
62
63 if (typeof payload === 'undefined') {
64 return failure(new Error('payload is required'));
65 } else if (isObjectPayload) {
66 var payload_validation_result = registered_claims_schema.validate(payload);
67
68 if (payload_validation_result.error) {
69 return failure(payload_validation_result.error);
70 }
71
72 payload = xtend(payload);
73 } else {
74 var invalid_options = options_for_objects.filter(function (opt) {
75 return typeof options[opt] !== 'undefined';
76 });
77
78 if (invalid_options.length > 0) {
79 return failure(new Error('invalid ' + invalid_options.join(',') + ' option for ' + (typeof payload ) + ' payload'));
80 }
81 }
82
83 if (typeof payload.exp !== 'undefined' && typeof options.expiresIn !== 'undefined') {
84 return failure(new Error('Bad "options.expiresIn" option the payload already has an "exp" property.'));
85 }
86
87 if (typeof payload.nbf !== 'undefined' && typeof options.notBefore !== 'undefined') {
88 return failure(new Error('Bad "options.notBefore" option the payload already has an "nbf" property.'));
89 }
90
91 var validation_result = sign_options_schema.validate(options);
92
93 if (validation_result.error) {
94 return failure(validation_result.error);
95 }
96
97 var timestamp = payload.iat || Math.floor(Date.now() / 1000);
98
99 if (!options.noTimestamp) {
100 payload.iat = timestamp;
101 } else {
102 delete payload.iat;
103 }
104
105 if (typeof options.notBefore !== 'undefined') {
106 payload.nbf = timespan(options.notBefore);
107 if (typeof payload.nbf === 'undefined') {
108 return failure(new Error('"notBefore" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60'));
109 }
110 }
111
112 if (typeof options.expiresIn !== 'undefined' && typeof payload === 'object') {
113 payload.exp = timespan(options.expiresIn, timestamp);
114 if (typeof payload.exp === 'undefined') {
115 return failure(new Error('"expiresIn" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60'));
116 }
117 }
118
119 Object.keys(options_to_payload).forEach(function (key) {
120 var claim = options_to_payload[key];
121 if (typeof options[key] !== 'undefined') {
122 if (typeof payload[claim] !== 'undefined') {
123 return failure(new Error('Bad "options.' + key + '" option. The payload already has an "' + claim + '" property.'));
124 }
125 payload[claim] = options[key];
126 }
127 });
128
129 var encoding = options.encoding || 'utf8';
130
131 if (typeof callback === 'function') {
132 callback = callback && once(callback);
133
134 jws.createSign({
135 header: header,
136 privateKey: secretOrPrivateKey,
137 payload: payload,
138 encoding: encoding
139 }).once('error', callback)
140 .once('done', function (signature) {
141 callback(null, signature);
142 });
143 } else {
144 return jws.sign({header: header, payload: payload, secret: secretOrPrivateKey, encoding: encoding});
145 }
146};