1 | var _ = require('lodash');
|
2 | var jwt = require('jwt-simple');
|
3 | var check = require('check-types');
|
4 | var verify = check.verify;
|
5 | var Promise = require('rsvp').Promise;
|
6 |
|
7 | var AUTH_TOKEN_TTL = 15;
|
8 | var AUTH_TOKEN_TTL_DEV = Number.MAX_VALUE;
|
9 |
|
10 | module.exports = function (nodeEnv, tenants, opts) {
|
11 | verify.string(nodeEnv);
|
12 | verify.object(tenants);
|
13 | verify.maybe.object(opts);
|
14 | opts = opts || {};
|
15 | return function (signedRequestParam, authorizationHeader) {
|
16 | var token;
|
17 | var claims;
|
18 | var tenantId;
|
19 | var issuedAt;
|
20 | return new Promise(function (resolve) {
|
21 |
|
22 | token = signedRequestParam;
|
23 | if (!token) {
|
24 | token = authorizationHeader;
|
25 | if (!token) {
|
26 | throw new Error('Request signature required but not found');
|
27 | }
|
28 | var match = /^JWT\s+token\s*=\s*(.+)\s*$/i.exec(token);
|
29 | if (!match || !match[1]) {
|
30 | throw new Error('Authorization header was either incompatible or malformed');
|
31 | }
|
32 | token = match[1];
|
33 | }
|
34 |
|
35 |
|
36 | claims = jwt.decode(token, null, true);
|
37 |
|
38 |
|
39 | tenantId = claims.iss;
|
40 | if (!check.string(claims.iss)) {
|
41 | throw new Error('Request signature did not contain an issuer claim');
|
42 | }
|
43 | issuedAt = claims.iat;
|
44 | if (!check.number(claims.iat)) {
|
45 | throw new Error('Request signature did not contain an issued-at timestamp');
|
46 | }
|
47 |
|
48 |
|
49 | resolve(tenants.get(tenantId));
|
50 | }).then(function (tenant) {
|
51 |
|
52 | if (!tenant) {
|
53 | throw new Error('Request signature contained unknown issuer: ' + tenantId);
|
54 | }
|
55 |
|
56 |
|
57 | try {
|
58 | claims = jwt.decode(token, tenant.secret);
|
59 | } catch (err) {
|
60 | throw new Error('Request signature verification failed: ' + (err.message || err.toString()));
|
61 | }
|
62 |
|
63 | var now = Math.floor(Date.now() / 1000);
|
64 |
|
65 |
|
66 | if (nodeEnv === 'production' && claims.exp && now >= claims.exp) {
|
67 | throw new Error('Request signature expired');
|
68 | }
|
69 |
|
70 |
|
71 | var authTokenTtl = nodeEnv !== 'production'
|
72 | ? AUTH_TOKEN_TTL_DEV
|
73 | : Math.abs((opts.authTokenTtl >>> 0) || AUTH_TOKEN_TTL);
|
74 |
|
75 | authTokenTtl = authTokenTtl * 60;
|
76 |
|
77 |
|
78 | var expiresAt = issuedAt + authTokenTtl;
|
79 | if (now >= expiresAt) {
|
80 | throw new Error('Request signature expired');
|
81 | }
|
82 |
|
83 |
|
84 | var refreshed = _.extend({}, claims);
|
85 | refreshed.iat = now;
|
86 | refreshed.exp = now + authTokenTtl;
|
87 | token = jwt.encode(refreshed, tenant.secret);
|
88 |
|
89 |
|
90 | return {
|
91 | issuer: tenant,
|
92 | issued: refreshed.iat,
|
93 | userId: refreshed.prn,
|
94 | expiry: refreshed.exp,
|
95 | context: refreshed.context,
|
96 | token: token
|
97 | };
|
98 | });
|
99 | };
|
100 | };
|