1 | const isObject = require('../help/is_object')
|
2 | const secs = require('../help/secs')
|
3 | const epoch = require('../help/epoch')
|
4 | const getKey = require('../help/get_key')
|
5 | const JWS = require('../jws')
|
6 |
|
7 | const isString = require('./shared_validations').isString.bind(undefined, TypeError)
|
8 |
|
9 | const validateOptions = (options) => {
|
10 | if (typeof options.iat !== 'boolean') {
|
11 | throw new TypeError('options.iat must be a boolean')
|
12 | }
|
13 |
|
14 | if (typeof options.kid !== 'boolean') {
|
15 | throw new TypeError('options.kid must be a boolean')
|
16 | }
|
17 |
|
18 | isString(options.subject, 'options.subject')
|
19 | isString(options.issuer, 'options.issuer')
|
20 |
|
21 | if (
|
22 | options.audience !== undefined &&
|
23 | (
|
24 | (typeof options.audience !== 'string' || !options.audience) &&
|
25 | (!Array.isArray(options.audience) || options.audience.length === 0 || options.audience.some(a => !a || typeof a !== 'string'))
|
26 | )
|
27 | ) {
|
28 | throw new TypeError('options.audience must be a string or an array of strings')
|
29 | }
|
30 |
|
31 | if (!isObject(options.header)) {
|
32 | throw new TypeError('options.header must be an object')
|
33 | }
|
34 |
|
35 | isString(options.algorithm, 'options.algorithm')
|
36 | isString(options.expiresIn, 'options.expiresIn')
|
37 | isString(options.notBefore, 'options.notBefore')
|
38 | isString(options.jti, 'options.jti')
|
39 | isString(options.nonce, 'options.nonce')
|
40 |
|
41 | if (options.now !== undefined && (!(options.now instanceof Date) || !options.now.getTime())) {
|
42 | throw new TypeError('options.now must be a valid Date object')
|
43 | }
|
44 | }
|
45 |
|
46 | module.exports = (payload, key, options = {}) => {
|
47 | if (!isObject(options)) {
|
48 | throw new TypeError('options must be an object')
|
49 | }
|
50 |
|
51 | const {
|
52 | algorithm, audience, expiresIn, header = {}, iat = true,
|
53 | issuer, jti, kid = true, nonce, notBefore, subject, now
|
54 | } = options
|
55 |
|
56 | validateOptions({
|
57 | algorithm, audience, expiresIn, header, iat, issuer, jti, kid, nonce, notBefore, now, subject
|
58 | })
|
59 |
|
60 | if (!isObject(payload)) {
|
61 | throw new TypeError('payload must be an object')
|
62 | }
|
63 |
|
64 | let unix
|
65 | if (expiresIn || notBefore || iat) {
|
66 | unix = epoch(now || new Date())
|
67 | }
|
68 |
|
69 | payload = {
|
70 | ...payload,
|
71 | sub: subject || payload.sub,
|
72 | aud: audience || payload.aud,
|
73 | iss: issuer || payload.iss,
|
74 | jti: jti || payload.jti,
|
75 | iat: iat ? unix : payload.iat,
|
76 | nonce: nonce || payload.nonce,
|
77 | exp: expiresIn ? unix + secs(expiresIn) : payload.exp,
|
78 | nbf: notBefore ? unix + secs(notBefore) : payload.nbf
|
79 | }
|
80 |
|
81 | key = getKey(key)
|
82 |
|
83 | let includeKid
|
84 |
|
85 | if (typeof options.kid === 'boolean') {
|
86 | includeKid = kid
|
87 | } else {
|
88 | includeKid = !key.secret
|
89 | }
|
90 |
|
91 | return JWS.sign(JSON.stringify(payload), key, {
|
92 | ...header,
|
93 | alg: algorithm || header.alg,
|
94 | kid: includeKid ? key.kid : header.kid
|
95 | })
|
96 | }
|