@mimik/oauth-helper 1.4.5 → 1.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc CHANGED
@@ -1,17 +1,34 @@
1
- // Use this file as a starting point for your project's .eslintrc.
2
- // Copy this file, and add rule overrides as needed.
3
1
  {
2
+ "plugins": [
3
+ "@mimik/document-env",
4
+ "@mimik/dependencies"
5
+ ],
4
6
  "env": {
5
7
  "node": true
6
8
  },
9
+ "parserOptions": {
10
+ "ecmaVersion": 2020
11
+ },
7
12
  "extends": "airbnb",
8
13
  "rules": {
14
+ "import/no-extraneous-dependencies": ["error", {"devDependencies": true}],
9
15
  "brace-style": [1, "stroustrup", {"allowSingleLine": true}],
10
16
  "no-confusing-arrow": [0], // arrow isnt confusing
11
17
  "max-len": [1, 180, { "ignoreComments": true }],
12
18
  "linebreak-style": 0,
13
19
  "quotes": [1, "single"],
14
- "semi": [1, "always"]
20
+ "semi": [1, "always"],
21
+ "no-process-env": ["error"],
22
+ "@mimik/document-env/validate-document-env": 2,
23
+ "@mimik/dependencies/case-sensitive": 2,
24
+ "@mimik/dependencies/no-cycles": 2,
25
+ "@mimik/dependencies/no-unresolved": 2,
26
+ "@mimik/dependencies/require-json-ext": 2
27
+ },
28
+ "settings":{
29
+ "react": {
30
+ "version": "latest"
31
+ }
15
32
  },
16
33
  "globals": {
17
34
  "module": true,
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ npm run commit-ready
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ npm run test
package/.nycrc ADDED
@@ -0,0 +1,4 @@
1
+ {
2
+ "exclude": ["gulpfile.js"],
3
+ "reporter": ["lcov", "text"]
4
+ }
package/Gulpfile.js CHANGED
@@ -5,29 +5,37 @@ const gulp = require('gulp');
5
5
  const eslint = require('gulp-eslint');
6
6
  const git = require('gulp-git');
7
7
  const jsdoc2md = require('jsdoc-to-markdown');
8
+ const mocha = require('gulp-spawn-mocha');
8
9
 
9
10
  const files = [
10
11
  'index.js',
11
12
  'Gulpfile.js',
12
- 'test/*.spec.js',
13
+ 'test/**.js',
13
14
  'lib/**.js',
14
15
  ];
15
16
 
16
- const docs = (done) => {
17
+ const createDocs = (done) => {
17
18
  jsdoc2md.render({ files: 'index.js' })
18
- .then(output => fs.writeFileSync('README.md', output))
19
- .catch(err => log.error('docs creation failed:', err.message));
20
- return done();
19
+ .then((output) => fs.writeFileSync('README.md', output))
20
+ .catch((err) => log.error('docs creation failed:', err.message))
21
+ .finally(() => done());
21
22
  };
22
23
 
23
24
  const lint = () => gulp.src(files)
24
25
  .pipe(eslint({}))
25
- .pipe(eslint.format())
26
- .pipe(eslint.failOnError());
26
+ .pipe(eslint.format());
27
27
 
28
28
  const add = () => gulp.src('README.md')
29
29
  .pipe(git.add({ quiet: true }));
30
30
 
31
- gulp.task('docs', docs);
31
+ const test = () => gulp.src(['test/*.spec.js'])
32
+ .pipe(mocha({
33
+ reporter: 'mochawesome',
34
+ exit: true,
35
+ }));
36
+
37
+ const docs = gulp.series(createDocs, add);
38
+
32
39
  gulp.task('lint', lint);
33
- gulp.task('add', add);
40
+ gulp.task('docs', docs);
41
+ gulp.task('test', test);
package/README.md CHANGED
@@ -26,6 +26,7 @@ const oauthHelper = require('@mimik/oauth-helper');
26
26
  * [~rpAuth(type, options)](#module_oauth-helper..rpAuth) ⇒ <code>Promise</code>
27
27
  * [~authProfile(method, id, correlationId)](#module_oauth-helper..authProfile) ⇒ <code>Promise</code>
28
28
  * _callback_
29
+ * [~apiKeySecurityHelper(request, definitions, apiKey, next)](#module_oauth-helper..apiKeySecurityHelper)
29
30
  * [~apiTokenSecurityHelper(request, definitions, scopes, next)](#module_oauth-helper..apiTokenSecurityHelper)
30
31
  * [~apiTokenAdminSecurityHelper(request, definitions, scopes, next)](#module_oauth-helper..apiTokenAdminSecurityHelper)
31
32
 
@@ -41,6 +42,15 @@ Make an authorized request.
41
42
 
42
43
  - <code>Promise</code> Will throw the same error than [request-promise](https://www.npmjs.com/package/request-promise).
43
44
 
45
+ The property `token` may be added to options, in order to set up how the token is retrieved from the token manager. The structure is:
46
+ ```
47
+ {
48
+ "retry": "object to specify how the retry to the token manager will be done. similar to rp-retry retry property",
49
+ "customerName": "name of the customer for which the token is entended for, see mST API",
50
+ "cluster": "to set the token to be a cluster token"
51
+ }
52
+ ````
53
+
44
54
  **Requires**: <code>module:@mimik/sumologic-winston-logger</code>
45
55
  **Fulfil**: <code>object</code> - Response of the [request-promise](https://www.npmjs.com/package/request-promise) request.
46
56
 
@@ -59,7 +69,7 @@ Make an authProfile request to mID.
59
69
  **Category**: async
60
70
  **Throws**:
61
71
 
62
- - <code>Promise</code> Will throw an error is the request fails.
72
+ - <code>Promise</code> Will throw a rich error is the request fails or an error if the id is not identified
63
73
 
64
74
  **Requires**: <code>module:@mimik/sumologic-winston-logger</code>
65
75
  **Fulfil**: <code>object</code> The response of the request made to `mID`.
@@ -70,6 +80,21 @@ Make an authProfile request to mID.
70
80
  | id | <code>string</code> | `UserId` to associated witht the request. |
71
81
  | correlationId | <code>UUID.&lt;string&gt;</code> | CorrelationId to associated with the request. |
72
82
 
83
+ <a name="module_oauth-helper..apiKeySecurityHelper"></a>
84
+
85
+ ### oauth-helper~apiKeySecurityHelper(request, definitions, apiKey, next)
86
+ Validate the request by checking the apiKey. To be used for `apikey`;
87
+
88
+ **Kind**: inner method of [<code>oauth-helper</code>](#module_oauth-helper)
89
+ **Category**: callback
90
+
91
+ | Param | Type | Description |
92
+ | --- | --- | --- |
93
+ | request | <code>object</code> | The request with a token in the header. |
94
+ | definitions | <code>object</code> | Swagger security definitions to compare with. |
95
+ | apiKey | <code>Array.&lt;string&gt;</code> | APIky extracted from the defined location. |
96
+ | next | [<code>requestCallback</code>](#requestCallback) | The callback that handles the response. The validation adds the following properties to the request: - apiKey Only the header location is allowed. |
97
+
73
98
  <a name="module_oauth-helper..apiTokenSecurityHelper"></a>
74
99
 
75
100
  ### oauth-helper~apiTokenSecurityHelper(request, definitions, scopes, next)
@@ -83,7 +108,7 @@ Validate the request by inspecting the token. To be used for `system` and `user`
83
108
  | request | <code>object</code> | The request with a token in the header. |
84
109
  | definitions | <code>object</code> | Swagger security definitions to compare with. |
85
110
  | scopes | <code>Array.&lt;string&gt;</code> | List of swagger scopes associated whith the requested endpoint. |
86
- | next | [<code>requestCallback</code>](#requestCallback) | The callback that handles the response. The validation adds the following properties to the request: - in case of 'system' token: 'clientId', 'tokenType', 'customer', 'onBehlaf'. - in case of 'user' token: userId, appId, onBehalfId, 'onBehalf'. |
111
+ | next | [<code>requestCallback</code>](#requestCallback) | The callback that handles the response. The validation adds the following properties to the request: - in case of 'system' token: 'clientId', 'tokenType', 'customer', 'onBehlaf'. If the token is a cluster token the property 'cluster' is set to true. - in case of 'user' token: userId, appId, onBehalfId, 'onBehalf'. |
87
112
 
88
113
  <a name="module_oauth-helper..apiTokenAdminSecurityHelper"></a>
89
114
 
package/index.js CHANGED
@@ -1,7 +1,13 @@
1
+ const Promise = require('bluebird');
1
2
  const jwt = require('jsonwebtoken');
2
- const rp = require('request-promise');
3
3
  const _ = require('lodash');
4
+
5
+ const { rpRetry } = require('@mimik/request-retry');
4
6
  const logger = require('@mimik/sumologic-winston-logger');
7
+ const { getRichError } = require('@mimik/response-helper');
8
+ const { TOKEN_PARAMS } = require('@mimik/swagger-helper');
9
+
10
+ Promise.config({ cancellation: true });
5
11
  /**
6
12
  * @module oauth-helper
7
13
  * @example
@@ -12,9 +18,14 @@ const TOKEN_REFRESH_TOLERANCE = 900; // in seconds
12
18
  const ON_BEHALF = 'onBehalf';
13
19
  const SUB_ADMIN = 'subAdmin';
14
20
  const ADMIN = 'admin';
21
+ const USER = 'user';
15
22
  const NO_GENERIC = 'noGeneric';
16
- const APPLICATION = 'application';
23
+ // const APPLICATION = 'application';
17
24
  const IMPLICIT = 'implicit';
25
+ const CLIENT = '@clients';
26
+ const AUTHORIZATION = 'authorization';
27
+ const HEADER = 'header';
28
+ const CLUSTER = 'cluster';
18
29
 
19
30
  const tokens = {};
20
31
 
@@ -29,45 +40,47 @@ const createToken = (value) => {
29
40
  value,
30
41
  exp: jwt.decode(value).exp,
31
42
  };
32
-
33
43
  return token;
34
44
  };
35
45
 
36
46
  module.exports = (config) => {
37
- const jwtOptions = (flow) => {
38
- if (flow === IMPLICIT) { // user flow
47
+ const { implicit, server, generic } = config.security;
48
+ const jwtOptions = (fl) => {
49
+ if (fl === IMPLICIT) { // user flow
39
50
  return {
40
- audience: (config.security.implicit && config.security.implicit.audience) || config.security.server.audience,
41
- issuer: (config.security.implicit && config.security.implicit.issuer) || config.security.server.issuer,
51
+ audience: (implicit && implicit.audience) || server.audience,
52
+ issuer: (implicit && implicit.issuer) || server.issuer,
42
53
  };
43
54
  }
44
- if (flow === APPLICATION) { // server to server flow
45
- return {
46
- audience: (config.security.generic.audience === NO_GENERIC) ? config.security.server.audience : config.security.generic.audience,
47
- issuer: config.security.server.issuer,
48
- // subject: `${config.serverSettings.id}@clients`,
49
- };
50
- }
51
- throw new Error(`flow ${flow} not implemented`);
55
+ // server to server or admin flow (application)
56
+ return {
57
+ audience: (generic.audience === NO_GENERIC) ? server.audience : generic.audience,
58
+ issuer: server.issuer,
59
+ // subject: `${config.serverSettings.id}@clients`,
60
+ };
52
61
  };
53
- const keys = (flow) => {
54
- if (flow === IMPLICIT) {
55
- return (config.security.implicit && config.security.implicit.key)
56
- || ((config.security.generic.key === NO_GENERIC) ? config.security.server.accessKey : config.security.generic.key);
62
+ const keys = (fl) => {
63
+ if (fl === IMPLICIT) { // user flow
64
+ return (implicit && implicit.key)
65
+ || ((generic.key === NO_GENERIC) ? server.accessKey : generic.key);
57
66
  }
58
- if (flow === APPLICATION) {
59
- return (config.security.generic.key === NO_GENERIC) ? config.security.server.accessKey : config.security.generic.key;
60
- }
61
- throw new Error(`flow ${flow} not implemented`);
67
+ // server to server or admin flow (application)
68
+ return (generic.key === NO_GENERIC) ? server.accessKey : generic.key;
62
69
  };
63
70
  const checkHeaders = (headers) => {
64
71
  if (!headers) {
65
72
  throw new Error('missing header');
66
73
  }
67
- if (!headers.authorization) {
68
- throw new Error('missing authorization header');
74
+ const authNames = Object.keys(headers).filter((key) => key.toLowerCase() === AUTHORIZATION);
75
+ const authNamesLength = authNames.length;
76
+
77
+ if (authNamesLength === 0) {
78
+ throw new Error(`missing ${AUTHORIZATION} header`);
79
+ }
80
+ if (authNamesLength !== 1) {
81
+ throw new Error(`duplicated ${AUTHORIZATION} header`);
69
82
  }
70
- const auth = headers.authorization.split(' ');
83
+ const auth = headers[authNames[0]].split(' ');
71
84
 
72
85
  if ((auth[0] !== 'bearer' && auth[0] !== 'Bearer') || auth.length !== 2) {
73
86
  throw new Error(`authorization type incorrect: ${auth[0]}`);
@@ -84,53 +97,60 @@ module.exports = (config) => {
84
97
  const intersects = _.intersection(currentScopes, defScopes);
85
98
 
86
99
  if (intersects.length === 0) {
87
- throw new Error(`incorrect scopes: ${currentScopes}`);
100
+ throw new Error(`incorrect scopes: ${tokenScopes}`);
88
101
  }
89
102
 
90
- if (_.find(intersects, scope => scope.split(':')[0] === ON_BEHALF)) {
103
+ if (_.find(intersects, (scope) => scope.split(':')[0] === ON_BEHALF)) {
91
104
  return true;
92
105
  }
93
106
  }
94
107
  return false;
95
108
  };
96
109
 
97
- const getToken = (type, correlationId) => {
110
+ const getToken = (type, origin, options, correlationId) => {
98
111
  const tokenOptions = {
99
112
  method: 'POST',
100
- uri: config.security.server.issuer,
101
- json: true,
113
+ url: server.issuer,
102
114
  };
115
+
116
+ if (correlationId) tokenOptions.headers = { 'x-correlation-id': correlationId };
103
117
  const getCredential = {
104
- client_id: config.security.server.id,
105
- client_secret: config.security.server.secret,
118
+ client_id: server.id,
119
+ client_secret: server.secret,
106
120
  audience: config.dependencies[type].audience,
107
121
  grant_type: 'client_credentials',
108
122
  };
109
123
 
124
+ if (options) {
125
+ if (options.customerName) getCredential.customer_name = options.customerName;
126
+ if (options.retry) tokenOptions.retry = options.retry;
127
+ if (options.cluster) getCredential.type = CLUSTER;
128
+ }
110
129
  if (!tokens[type]) tokens[type] = {};
111
- if (valid(tokens[type].accessToken)) return Promise.resolve(tokens[type].accessToken.value);
112
- if (valid(tokens[type].refreshToken)) {
113
- logger.silly('valid refresh_token, refresh_token', { type }, correlationId);
130
+ if (!tokens[type][origin]) tokens[type][origin] = {};
131
+ if (valid(tokens[type][origin].accessToken)) return Promise.resolve(tokens[type][origin].accessToken.value);
132
+ if (valid(tokens[type][origin].refreshToken)) {
133
+ logger.silly('valid refresh_token, refresh_token', { type, origin }, correlationId);
114
134
  tokenOptions.body = {
115
- refresh_token: tokens[type].refreshToken.value,
135
+ refresh_token: tokens[type][origin].refreshToken.value,
116
136
  grant_type: 'refresh_token',
117
137
  };
118
138
  }
119
139
  else {
120
- logger.silly('invalid refresh_token, client_credentials', { type }, correlationId);
140
+ logger.silly('invalid refresh_token, client_credentials', { type, origin }, correlationId);
121
141
  tokenOptions.body = getCredential;
122
142
  }
123
- return rp(tokenOptions).catch((err) => {
143
+ return rpRetry(tokenOptions).catch((err) => {
124
144
  if (err.statusCode !== 400 || tokenOptions.body.grant_type === 'client_credentials') {
125
145
  throw err;
126
146
  }
127
147
  logger.silly('moving from refresh_token to client_credential', { error: err.message }, correlationId);
128
148
  tokenOptions.body = getCredential;
129
- return rp(tokenOptions);
149
+ return rpRetry(tokenOptions);
130
150
  }).then((response) => {
131
- tokens[type].accessToken = createToken(response.data.access_token);
132
- tokens[type].refreshToken = createToken(response.data.refresh_token);
133
- return tokens[type].accessToken.value;
151
+ tokens[type][origin].accessToken = createToken(response.data.access_token);
152
+ tokens[type][origin].refreshToken = createToken(response.data.refresh_token);
153
+ return tokens[type][origin].accessToken.value;
134
154
  });
135
155
  };
136
156
 
@@ -145,29 +165,42 @@ module.exports = (config) => {
145
165
  * @return {Promise}.
146
166
  * @fulfil {object} - Response of the [request-promise](https://www.npmjs.com/package/request-promise) request.
147
167
  * @throws {Promise} Will throw the same error than [request-promise](https://www.npmjs.com/package/request-promise).
168
+ *
169
+ * The property `token` may be added to options, in order to set up how the token is retrieved from the token manager. The structure is:
170
+ * ```
171
+ * {
172
+ * "retry": "object to specify how the retry to the token manager will be done. similar to rp-retry retry property",
173
+ * "customerName": "name of the customer for which the token is entended for, see mST API",
174
+ * "cluster": "to set the token to be a cluster token"
175
+ * }
176
+ * ````
148
177
  */
149
178
  const rpAuth = (type, options) => {
150
- if (!type) return Promise.reject(new Error(`rpAuth type non existent for ${options.method || 'GET'} on ${options.url || options.uri}`));
179
+ const enteredUrl = options.uri || options.url; // uri takes precedence over url in request-promise, conserving the order
180
+ let url;
181
+
182
+ if (!type) return Promise.reject(new Error(`rpAuth type non existent for ${options.method || 'GET'} on ${enteredUrl}`));
151
183
  if (!config.dependencies[type]) return Promise.reject(new Error(`type ${type} used in rpAuth with no dependencies`));
184
+ if (!options.url && !options.uri) return Promise.reject(new Error('uri or url non existent'));
185
+ try { url = new URL(enteredUrl); }
186
+ catch (err) { return Promise.reject(new Error(`invalid url address: ${enteredUrl}: ${err.message}`)); }
152
187
  const correlationId = (options.headers && options.headers['x-correlation-id']);
153
188
 
154
- return getToken(type, correlationId)
189
+ return getToken(type, url.origin, options.token, correlationId)
155
190
  .then((token) => {
156
191
  const opts = options;
157
192
 
158
193
  if (!opts.headers) opts.headers = {};
159
194
  opts.headers.authorization = `Bearer ${token}`;
160
- opts.simple = true; // make sure that we use the simple mode
161
- return rp(opts)
195
+ return rpRetry(opts)
162
196
  .catch((err) => {
163
197
  if (err.statusCode === 403) {
164
198
  logger.warn('got a unauthorized request, retrying', { error: err.message, type }, correlationId);
165
199
  tokens[type].accessToken = null;
166
- return getToken(type, correlationId)
200
+ return getToken(type, options.uri || options.url, options.token, correlationId)
167
201
  .then((newToken) => {
168
- if (!opts.headers) opts.headers = {};
169
202
  opts.headers.authorization = `Bearer ${newToken}`;
170
- return rp(opts);
203
+ return rpRetry(opts);
171
204
  });
172
205
  }
173
206
  throw err;
@@ -175,6 +208,33 @@ module.exports = (config) => {
175
208
  });
176
209
  };
177
210
 
211
+ /**
212
+ * Validate the request by checking the apiKey. To be used for `apikey`;
213
+ *
214
+ * @function apiKeySecurityHelper
215
+ * @category callback
216
+ * @param {object} request - The request with a token in the header.
217
+ * @param {object} definitions - Swagger security definitions to compare with.
218
+ * @param {string[]} apiKey - APIky extracted from the defined location.
219
+ * @param {requestCallback} next - The callback that handles the response.
220
+ *
221
+ * The validation adds the following properties to the request:
222
+ * - apiKey
223
+ * Only the header location is allowed.
224
+ */
225
+ const apiKeySecurityHelper = (request, definitions, apiKey, next) => {
226
+ if (definitions.in !== HEADER) {
227
+ next(new Error(`invalid location: only ${HEADER} is accepted`));
228
+ return;
229
+ }
230
+ if (config.security.apiKeys.includes(apiKey)) {
231
+ request.apiKey = apiKey;
232
+ next(null);
233
+ return;
234
+ }
235
+ next(new Error('invalid API key'));
236
+ };
237
+
178
238
  /**
179
239
  * Validate the request by inspecting the token. To be used for `system` and `user` token.
180
240
  *
@@ -186,11 +246,12 @@ module.exports = (config) => {
186
246
  * @param {requestCallback} next - The callback that handles the response.
187
247
  *
188
248
  * The validation adds the following properties to the request:
189
- * - in case of 'system' token: 'clientId', 'tokenType', 'customer', 'onBehlaf'.
249
+ * - in case of 'system' token: 'clientId', 'tokenType', 'customer', 'onBehlaf'. If the token is a cluster token the property 'cluster' is set to true.
190
250
  * - in case of 'user' token: userId, appId, onBehalfId, 'onBehalf'.
191
251
  */
192
252
  const apiTokenSecurityHelper = (request, definitions, scopes, next) => {
193
253
  let authToken;
254
+ const { flow } = definitions;
194
255
 
195
256
  try { authToken = checkHeaders(request.headers); }
196
257
  catch (err) {
@@ -207,10 +268,19 @@ module.exports = (config) => {
207
268
  next(new Error('invalid token: wrong type'));
208
269
  return;
209
270
  }
210
- try { jwt.verify(authToken, keys(definitions.flow), jwtOptions(definitions.flow)); }
271
+ try { jwt.verify(authToken, keys(flow), jwtOptions(flow)); }
211
272
  catch (err) {
212
- next(err);
213
- return;
273
+ if (flow !== IMPLICIT && generic.previousKey) { // backward compatibility
274
+ try { jwt.verify(authToken, generic.previousKey, jwtOptions(flow)); }
275
+ catch (secondErr) {
276
+ next(secondErr);
277
+ return;
278
+ }
279
+ }
280
+ else {
281
+ next(err);
282
+ return;
283
+ }
214
284
  }
215
285
  let onBehalfOption = false;
216
286
 
@@ -219,25 +289,24 @@ module.exports = (config) => {
219
289
  next(errScopes);
220
290
  return;
221
291
  }
222
- if (onBehalfOption) request.onBehalf = true;
292
+ if (onBehalfOption) request[TOKEN_PARAMS.onBehalf] = true;
223
293
  if (definitions.flow === IMPLICIT) {
224
- if (token.sub) request.userId = token.sub;
225
- if (token.azp) request.appId = token.azp;
294
+ if (token.sub) request[TOKEN_PARAMS.userId] = token.sub;
295
+ if (token.azp) request[TOKEN_PARAMS.appId] = token.azp;
226
296
  if (token.may_act && token.may_act.sub) {
227
- request.onBehalfId = token.sub;
228
- request.userId = token.may_act.sub;
297
+ request[TOKEN_PARAMS.onBehalfId] = token.sub;
298
+ request[TOKEN_PARAMS.userId] = token.may_act.sub;
229
299
  }
300
+ request.tokenType = USER;
230
301
  next(null);
231
302
  return;
232
303
  }
233
- if (definitions.flow === APPLICATION) {
234
- if (token.sub) request.clientId = token.sub;
235
- if (token.subType) request.tokenType = token.subType;
236
- if (token.cust) request.customer = token.cust;
237
- next(null);
238
- return;
239
- }
240
- next(new Error(`flow ${definitions.flow} not implemented`));
304
+ // server to server or admin flow (application)
305
+ if (token.sub) request[TOKEN_PARAMS.clientId] = token.sub;
306
+ if (token.subType) request[TOKEN_PARAMS.tokenType] = token.subType;
307
+ if (token.cust) request[TOKEN_PARAMS.customer] = token.cust;
308
+ if (token.type === CLUSTER) request[TOKEN_PARAMS.cluster] = true;
309
+ next(null);
241
310
  };
242
311
 
243
312
  /**
@@ -254,6 +323,7 @@ module.exports = (config) => {
254
323
  */
255
324
  const apiTokenAdminSecurityHelper = (request, definitions, scopes, next) => {
256
325
  let authToken;
326
+ const { flow } = definitions;
257
327
 
258
328
  try { authToken = checkHeaders(request.headers); }
259
329
  catch (err) {
@@ -272,14 +342,20 @@ module.exports = (config) => {
272
342
  return;
273
343
  }
274
344
  try {
275
- jwt.verify(authToken, config.security.generic.key, {
276
- audience: config.security.generic.audience,
277
- issuer: config.security.server.issuer,
278
- });
345
+ jwt.verify(authToken, keys(flow), jwtOptions(flow));
279
346
  }
280
347
  catch (err) {
281
- next(err);
282
- return;
348
+ if (generic.previousKey) { // backward compatibility
349
+ try { jwt.verify(authToken, generic.previousKey, jwtOptions(flow)); }
350
+ catch (secondErr) {
351
+ next(secondErr);
352
+ return;
353
+ }
354
+ }
355
+ else {
356
+ next(err);
357
+ return;
358
+ }
283
359
  }
284
360
  try { checkScopes(token.scope, scopes); }
285
361
  catch (errScopes) {
@@ -292,13 +368,13 @@ module.exports = (config) => {
292
368
  return;
293
369
  }
294
370
  }
295
- else if (token.sub !== `${config.security.admin.externalId}@clients`) {
371
+ else if (token.sub !== `${config.security.admin.externalId}${CLIENT}`) {
296
372
  next(new Error(`jwt subject invalid: ${token.sub}`));
297
373
  return;
298
374
  }
299
- if (token.subType) request.tokenType = token.subType;
300
- if (token.sub) request.clientId = token.sub;
301
- if (token.cust) request.customer = token.cust;
375
+ if (token.subType) request[TOKEN_PARAMS.tokenType] = token.subType;
376
+ if (token.sub) request[TOKEN_PARAMS.clientId] = token.sub;
377
+ if (token.cust) request[TOKEN_PARAMS.customer] = token.cust;
302
378
  next(null);
303
379
  };
304
380
 
@@ -313,25 +389,31 @@ module.exports = (config) => {
313
389
  * @param {UUID<string>} correlationId - CorrelationId to associated with the request.
314
390
  * @return {Promise}.
315
391
  * @fulfil {object} The response of the request made to `mID`.
316
- * @throws {Promise} Will throw an error is the request fails.
392
+ * @throws {Promise} Will throw a rich error is the request fails or an error if the id is not identified
317
393
  */
318
- const authProfile = (method, id, correlationId) => rpAuth('mID', {
319
- method,
320
- uri: `${config.dependencies.mID.url}/users/${id}`,
321
- headers: {
322
- 'x-correlation-id': correlationId,
323
- },
324
- json: true,
325
- }).catch((err) => {
326
- if (err.statusCode === 400 && method === 'DELETE') {
327
- logger.warn('profile without authprofile', { id, error: err.message }, correlationId);
328
- return;
329
- }
330
- throw new Error(err.message);
331
- });
394
+ const authProfile = (method, id, correlationId) => {
395
+ if (!id) return Promise.reject(new Error('Id has to be defined'));
396
+ return rpAuth('mID', {
397
+ method,
398
+ uri: `${config.dependencies.mID.url}/users/${id}`,
399
+ headers: {
400
+ 'x-correlation-id': correlationId,
401
+ },
402
+ json: true,
403
+ }).catch((err) => {
404
+ const error = getRichError(err.statusCode, 'could perform operation on identity server', { method, id }, err);
405
+
406
+ if (error.statusCode === 400 && method === 'DELETE') {
407
+ logger.warn('profile without authprofile', { id, error }, correlationId);
408
+ return;
409
+ }
410
+ throw error;
411
+ });
412
+ };
332
413
 
333
414
  return {
334
415
  rpAuth,
416
+ apiKeySecurityHelper,
335
417
  apiTokenSecurityHelper,
336
418
  apiTokenAdminSecurityHelper,
337
419
  authProfile,
@@ -0,0 +1,10 @@
1
+ const rp = require('request-promise');
2
+
3
+ const options = {
4
+ method: 'GET',
5
+ url: 'http://google.com',
6
+ uri: 'http//google.com',
7
+ json: true,
8
+ };
9
+
10
+ rp(options).then(() => console.log('ok')).catch((err) => console.log(err));