UNPKG

14.7 kBJavaScriptView Raw
1
2
3'use strict';
4
5var helper = require('./test-helper');
6var config = helper.config();
7
8var debug = require('debug')('plugin:oauth:test');
9var http = require('http');
10var request = require('supertest');
11var chai = require('chai');
12var should = chai.should();
13var pem = require('pem');
14var jwt = require('jsonwebtoken');
15var util = require('util');
16
17chai.config.includeStack = true;
18chai.config.showDiff = true;
19
20describe('oauth', function() {
21
22 var privateKey, publicKey;
23
24 before(function(done) {
25 var options = {
26 selfSigned: true,
27 days: 1
28 };
29 pem.createCertificate(options, function(err, keys) {
30 if (err) { return done(err); }
31
32 privateKey = keys.serviceKey;
33 publicKey = keys.certificate;
34
35 config.oauth.public_key = publicKey;
36 config.oauth.allowNoAuthorization = false;
37 config.oauth.allowInvalidAuthorization = false;
38
39 done();
40 });
41 });
42
43 var servers, proxy;
44
45 before(function(done) {
46 config.edgemicro.plugins.sequence = ['oauth'];
47 helper.startServers(config, function(err, s) {
48 if (err) { return done(err); }
49 servers = s;
50 proxy = servers.proxy;
51 done();
52 });
53 });
54
55 after(function() {
56 servers.close();
57 });
58
59
60 describe('no authorization', function() {
61
62 it('should fail when allowNoAuthorization is false', function(done) {
63
64 config.oauth.allowNoAuthorization = false;
65 config.oauth.allowInvalidAuthorization = false;
66 request(proxy)
67 .get('/')
68 .expect(401)
69 .end(function(err, res) {
70 if (err) { return done(err); }
71 should.not.exist(res.body.fromTarget);
72 done();
73 });
74 });
75
76 it('should succeed when allowNoAuthorization is true', function(done) {
77
78 config.oauth.allowNoAuthorization = true;
79 config.oauth.allowInvalidAuthorization = false;
80 request(proxy)
81 .get('/')
82 .expect(200)
83 .end(function(err, res) {
84 if (err) { return done(err); }
85 should.exist(res.body.fromTarget);
86 should.not.exist(res.headers.authorization);
87
88 should.not.exist(res.body.headers['authorization']);
89 should.not.exist(res.body.headers['x-authorization-claims']);
90 done();
91 });
92 });
93 });
94
95 describe('bad bearer token', function() {
96
97 it('should fail with missing bearer token', function(done) {
98
99 config.oauth.allowNoAuthorization = false;
100 config.oauth.allowInvalidAuthorization = false;
101 request(proxy)
102 .get('/')
103 .set('Authorization', 'Bearer')
104 .expect(400)
105 .end(function(err, res) {
106 if (err) { return done(err); }
107 should.not.exist(res.body.fromTarget);
108 done();
109 });
110 });
111
112 it('should fail when allowInvalidAuthorization is false', function(done) {
113
114 config.oauth.allowNoAuthorization = false;
115 config.oauth.allowInvalidAuthorization = false;
116 request(proxy)
117 .get('/')
118 .set('Authorization', 'Bearer BadBearerToken')
119 .expect(401)
120 .end(function(err, res) {
121 if (err) { return done(err); }
122 should.not.exist(res.body.fromTarget);
123 done();
124 });
125 });
126
127 it('should succeed when allowInvalidAuthorization is true', function(done) {
128
129 config.oauth.allowNoAuthorization = false;
130 config.oauth.allowInvalidAuthorization = true;
131 request(proxy)
132 .get('/')
133 .set('Authorization', 'Bearer BadBearerToken')
134 .expect(200)
135 .end(function(err, res) {
136 if (err) { return done(err); }
137 should.exist(res.body.fromTarget);
138 should.not.exist(res.headers.authorization);
139
140 should.not.exist(res.body.headers['authorization']);
141 should.not.exist(res.body.headers['x-authorization-claims']);
142 done();
143 });
144 });
145 });
146
147 describe('good bearer token', function() {
148
149 it('should succeed if valid proxy path', function(done) {
150
151 var options = { algorithm: 'RS256' };
152 var payload = {
153 application_name: 'app',
154 client_id: 'client',
155 scopes: ['scope1'],
156 api_product_list: [ 'Test' ],
157 test: 'test'
158 };
159 var token = jwt.sign(payload, privateKey, options);
160
161 config.oauth.allowNoAuthorization = false;
162 config.oauth.allowInvalidAuthorization = false;
163 request(proxy)
164 .get('/')
165 .set('Authorization', 'Bearer ' + token)
166 .expect(200)
167 .end(function(err, res) {
168 if (err) { return done(err); }
169 should.exist(res.body.fromTarget);
170 should.not.exist(res.headers.authorization);
171
172 should.not.exist(res.body.headers['authorization']);
173 should.exist(res.body.headers['x-authorization-claims']);
174 var claims = JSON.parse(new Buffer(res.body.headers['x-authorization-claims'], 'base64').toString());
175 claims.should.have.keys('scopes', 'test');
176
177 done();
178 });
179 });
180
181 it('should fail if missing required proxy path', function(done) {
182
183 var options = { algorithm: 'RS256' };
184 var payload = { test: 'test' };
185 var token = jwt.sign(payload, privateKey, options);
186
187 config.oauth.allowNoAuthorization = false;
188 config.oauth.allowInvalidAuthorization = false;
189 request(proxy)
190 .get('/')
191 .set('Authorization', 'Bearer ' + token)
192 .expect(403)
193 .end(function(err, res) {
194 if (err) { return done(err); }
195 should.not.exist(res.body.fromTarget);
196 done();
197 });
198 });
199
200 it('should fail if expired', function(done) {
201
202 var options = { algorithm: 'RS256', expiresInSeconds: 1 };
203 var payload = { test: 'test' };
204 var token = jwt.sign(payload, privateKey, options);
205
206 setTimeout(function() {
207
208 config.oauth.allowNoAuthorization = false;
209 config.oauth.allowInvalidAuthorization = false;
210
211 request(proxy)
212 .get('/')
213 .set('Authorization', 'Bearer ' + token)
214 .expect(403)
215 .end(function(err, res) {
216 if (err) { return done(err); }
217 should.not.exist(res.body.fromTarget);
218 done();
219 });
220 }, 1000);
221 });
222 });
223
224 describe('configured auth header', function() {
225
226 before(function() {
227 config.oauth.allowNoAuthorization = false;
228 config.oauth.allowInvalidAuthorization = false;
229 config.oauth['authorization-header'] = 'x-proxy-auth';
230 });
231
232 after(function() {
233 delete(config.oauth['authorization-header']);
234 });
235
236 it('should succeed if in configured header', function(done) {
237
238 var options = { algorithm: 'RS256' };
239 var payload = {
240 application_name: 'app',
241 client_id: 'client',
242 scopes: ['scope1'],
243 api_product_list: [ 'Test' ],
244 test: 'test'
245 };
246 var token = jwt.sign(payload, privateKey, options);
247
248 request(proxy)
249 .get('/')
250 .set('Authorization', 'Whatever')
251 .set('x-proxy-auth', 'Bearer ' + token)
252 .expect(200)
253 .end(function(err, res) {
254 if (err) { return done(err); }
255 should.exist(res.body.fromTarget);
256 should.not.exist(res.headers.authorization);
257
258 should.exist(res.body.headers['authorization']);
259 should.exist(res.body.headers['x-authorization-claims']);
260 var claims = JSON.parse(new Buffer(res.body.headers['x-authorization-claims'], 'base64').toString());
261 claims.should.have.keys('scopes', 'test');
262
263 done();
264 });
265 });
266
267 it('should fail if in default header', function(done) {
268
269 var options = { algorithm: 'RS256' };
270 var payload = {
271 application_name: 'app',
272 client_id: 'client',
273 scopes: ['scope1'],
274 api_product_list: [ 'Test' ],
275 test: 'test'
276 };
277 var token = jwt.sign(payload, privateKey, options);
278
279 request(proxy)
280 .get('/')
281 .set('Authorization', 'Bearer ' + token)
282 .expect(401)
283 .end(function(err, res) {
284 if (err) { return done(err); }
285 should.not.exist(res.body.fromTarget);
286 done();
287 });
288
289 });
290 });
291
292 describe('apikey', function() {
293
294 var verifier;
295
296 var apiKey = '6gClRCKp0UCOZ8o9Q5S7X88nI5hgizGQ';
297 var jwtTokenForApiKey =
298 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhcHBsaWNhdGlvbl9uYW1lIjoiSkRzIEFwcCIsImNsaWVudF9pZCI6IjZnQ2xSQ0twMFV' +
299 'DT1o4bzlRNVM3WDg4bkk1aGdpekdRIiwic2NvcGVzIjpbXSwiYXBpX3Byb2R1Y3RfbGlzdCI6WyJ0cmF2ZWwtYXBwIl0sImlhdCI6MTQ0NTU' +
300 '1MjYxMH0.glQTK-Nh0YFYbWK-pr8nbJuIvt51p6zmLe53CbZ3JLEcG0QjHYcKmUPLPgDOGYfyjnYMUVMMfIDDZlhemy84fKQMGRptCgUfmga' +
301 '9roLNPKPujhxFXb9GhkQ94KXxm8GChuvjYxn8K7K_nAhnzn4wB84rczvm91ytOwFPCeS_t6KbLS3uMrj6Nj1gITGeZVlm2QLAvUlJ5Abua0t' +
302 'OItHj7_nvzHHwClgN9Is1UZ7LW5f747kVWp10t1JbAmubyTP-01TSbaniDGfBCmi0JYOizUFZiMjdSVcZP-tqWvHAsLdQ2T9k4sKG1I1pKcK' +
303 'vh3dmS3j8PWhTHIV3FM1x7L5hig';
304 var apiPublicKey =
305 '-----BEGIN CERTIFICATE-----\n' +
306 'MIICpDCCAYwCCQDd9JO8DIvnqTANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwls\n' +
307 'b2NhbGhvc3QwHhcNMTUwNjI1MDMxNTQ5WhcNMTUwNjI2MDMxNTQ5WjAUMRIwEAYD\n' +
308 'VQQDEwlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDY\n' +
309 'O0JHvd1m/H/VyGmQpuqY/CvZGbx/wRWrLG/YErYl1w30e615hJOBpT1neavXONGw\n' +
310 '2kuiqjRon8WcWvjrmKSDitul1MhUqddEBC+JhMfZpgCr9axcrFgRxm4JcfrhWoqE\n' +
311 'LXAYLo/VXzNCCkGz4SLcp/azpnnPBeTm5m4AJBMW0YrznBQrVMJHGXXvd1b9q5Hp\n' +
312 'Ejui9VdIEknhNJ88bjP3Lq7j0efCbkj0IAYNigdbC+eIMbRHVHNyEaioUAU9pYAp\n' +
313 '2v1tomnwlQbEhD3vVnWJprKWgLGtYf9pmwrffwtDu73gyO/qr+aVNSNlchQ4fOag\n' +
314 '3xeYKmAUHOl6QpiIocjzAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAG9B1kST84u0\n' +
315 'yR5/CNqHO6wx2SiYKOfrhkvKbxsql/u59qxYd6jHPyE4adLkp/PPWz6DN4ucRT4J\n' +
316 '2UULFD2biRvPK9Ua3J7IKLUMOuVnwU7JFCiAf51e0A9Weqh6L0+ATTkZMddUfKTA\n' +
317 'uLdVOBz43EPCizKAjUP39+RFuUhBJazNrXyibE4fP/r6pJ7EB4a57HnvTlNkPZXA\n' +
318 'z24Ihg42rqDXgFmNPE6X9p/pSOzA87iGLrOznbFQzTHJg/wLqmPEPVrjFwXWfzoI\n' +
319 'CAysSvrNWbfqSIoxxWPpkK+O10m2DqYaQMu3J73p9kgD0gV9KBSW2rMFDg6JnI4G\n' +
320 'BDvtgIzP58M=\n' +
321 '-----END CERTIFICATE-----';
322
323 before(function(done) {
324 verifier = http.createServer(function(req, res) {
325 var key = req.headers['x-dna-api-key'];
326 if (key === apiKey) {
327 res.statusCode = 200;
328 res.end(jwtTokenForApiKey);
329 } else {
330 res.statusCode = 401;
331 res.end();
332 }
333 });
334 verifier.listen(function(err) {
335 if (err) { return done(err); }
336 config.oauth.verify_api_key_url = 'http://127.0.0.1:' + verifier.address().port;
337 config.oauth.public_key = apiPublicKey;
338 config.oauth.product_to_proxy = {};
339 config.oauth.product_to_proxy['travel-app'] = ['microgateway_test'];
340 done();
341 });
342 });
343
344 after(function() {
345 verifier.close();
346 });
347
348 it('in header', function(done) {
349 request(proxy)
350 .get('/')
351 .set('x-api-key', apiKey)
352 .expect(200)
353 .end(function(err, res) {
354 debug(res.body);
355 if (err) { return done(err); }
356 done();
357 });
358 });
359
360 it('in custom header', function(done) {
361 config.oauth['api-key-header'] = 'custom-api-key-header';
362 request(proxy)
363 .get('/')
364 .set('custom-api-key-header', apiKey)
365 .expect(200)
366 .end(function(err, res) {
367 debug(res.body);
368 delete config.oauth['api-key-header'];
369 if (err) { return done(err); }
370 done();
371 });
372 });
373
374 it('in url param', function(done) {
375 request(proxy)
376 .get('/?x-api-key=' + apiKey)
377 .expect(200)
378 .end(function(err, res) {
379 debug(res.body);
380 if (err) { return done(err); }
381 done();
382 });
383 });
384
385 it('in custom url param', function(done) {
386 config.oauth['api-key-header'] = 'custom-api-key-header';
387 request(proxy)
388 .get('/?custom-api-key-header=' + apiKey)
389 .expect(200)
390 .end(function(err, res) {
391 debug(res.body);
392 delete config.oauth['api-key-header'];
393 if (err) { return done(err); }
394 done();
395 });
396 });
397
398 it('in header (invalid)', function(done) {
399 request(proxy)
400 .get('/')
401 .set('x-api-key', (apiKey + 'x'))
402 .expect(403)
403 .end(function(err, res) {
404 debug(res.body);
405 if (err) { return done(err); }
406 done();
407 });
408 });
409
410 it('in url (invalid)', function(done) {
411 request(proxy)
412 .get('/?x-api-key=' + (apiKey + 'x'))
413 .expect(403)
414 .end(function(err, res) {
415 debug(res.body);
416 if (err) { return done(err); }
417 done();
418 });
419 });
420
421 it('expiration', function(done) {
422 config.oauth.public_key = publicKey;
423
424 var payload = {};
425 var options = { algorithm: 'RS256' };
426
427 var savedToken = jwtTokenForApiKey;
428 var decoded = jwt.decode(jwtTokenForApiKey);
429 Object.keys(decoded).forEach(function(key) { payload[key] = decoded[key] });
430 options.expiresIn = -1; // expired already
431 jwtTokenForApiKey = jwt.sign(payload, privateKey, options);
432
433 request(proxy)
434 .get('/')
435 .set('x-api-key', apiKey)
436 .set('cache-control', 'no-cache')
437 .expect(403) // expired
438 .end(function(err, res) {
439 debug('expired: ' + util.inspect(res.body));
440 if (err) { return done(err); }
441
442 options.expiresIn = 30; // expires 30s from now
443 jwtTokenForApiKey = jwt.sign(payload, privateKey, options);
444
445 request(proxy) // should succeed now
446 .get('/')
447 .set('x-api-key', apiKey)
448 .set('cache-control', 'no-cache')
449 .expect(200)
450 .end(function(err, res) {
451 debug('restored: ' + util.inspect(res.body));
452 jwtTokenForApiKey = savedToken; // restore token
453 if (err) { return done(err); }
454 done();
455 });
456 });
457 });
458
459 it('keep auth header', function(done) {
460 config.oauth.keepAuthHeader = true;
461 config.oauth.allowNoAuthorization = true;
462 config.oauth.allowInvalidAuthorization = false;
463
464 request(proxy)
465 .get('/')
466 .expect(200)
467 .end(function(err, res) {
468 if (err) { return done(err); }
469 should.exist(res.body.fromTarget);
470 should.exist(res.headers.authorization);
471
472 should.not.exist(res.body.headers['authorization']);
473 should.not.exist(res.body.headers['x-authorization-claims']);
474 done();
475 });
476
477 });
478
479 });
480
481});