UNPKG

2.98 kBJavaScriptView Raw
1// Copyright © 2017, 2019 IBM Corp. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14'use strict';
15
16const debug = require('debug')('cloudant:plugins:iamauth');
17const request = require('request');
18const u = require('url');
19
20const BasePlugin = require('./base.js');
21const IAMTokenManager = require('../lib/tokens/IamTokenManager');
22
23/**
24 * IAM Authentication plugin.
25 */
26class IAMPlugin extends BasePlugin {
27 constructor(client, cfg) {
28 if (typeof cfg.iamApiKey === 'undefined') {
29 throw new Error('Missing IAM API key from configuration');
30 }
31
32 cfg = Object.assign({
33 autoRenew: true,
34 iamTokenUrl: 'https://iam.cloud.ibm.com/identity/token',
35 retryDelayMsecs: 1000
36 }, cfg);
37
38 super(client, cfg);
39
40 let sessionUrl = new u.URL(cfg.serverUrl);
41 sessionUrl.pathname = '/_iam_session';
42
43 this._jar = request.jar();
44
45 this._tokenManager = new IAMTokenManager(
46 client,
47 this._jar,
48 u.format(sessionUrl, {auth: false}),
49 cfg.iamTokenUrl,
50 cfg.iamApiKey,
51 cfg.iamClientId,
52 cfg.iamClientSecret
53 );
54
55 if (cfg.autoRenew) {
56 this._tokenManager.startAutoRenew();
57 }
58 }
59
60 onRequest(state, req, callback) {
61 var self = this;
62
63 req.jar = self._jar;
64
65 req.uri = req.uri || req.url;
66 delete req.url;
67 req.uri = u.format(new u.URL(req.uri), {auth: false});
68
69 self._tokenManager.renewIfRequired().then(() => {
70 callback(state);
71 }).catch((error) => {
72 debug(error);
73 if (state.attempt < state.maxAttempt) {
74 state.retry = true;
75 let iamResponse = error.response;
76 let retryAfterSecs;
77 if (iamResponse && iamResponse.headers) {
78 retryAfterSecs = iamResponse.headers['Retry-After'];
79 }
80 if (retryAfterSecs) {
81 state.retryDelayMsecs = retryAfterSecs * 1000;
82 } else {
83 state.retryDelayMsecs = self._cfg.retryDelayMsecs;
84 }
85 } else {
86 state.abortWithResponse = [ error ]; // return error to client
87 }
88 callback(state);
89 });
90 }
91
92 onResponse(state, response, callback) {
93 if (response.statusCode === 401) {
94 debug('Received 401 response. Asking for request retry.');
95 state.retry = true;
96 this._tokenManager.attemptTokenRenewal = true;
97 }
98 callback(state);
99 }
100
101 setIamApiKey(newIamApiKey) {
102 this._tokenManager.setIamApiKey(newIamApiKey);
103 }
104}
105
106IAMPlugin.id = 'iamauth';
107
108module.exports = IAMPlugin;