@webex/plugin-authorization-node 2.59.2 → 2.59.3-next.1
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.js +6 -6
- package/README.md +53 -53
- package/babel.config.js +3 -3
- package/dist/authorization.js +44 -44
- package/dist/authorization.js.map +1 -1
- package/dist/config.js +2 -2
- package/dist/config.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/jest.config.js +3 -3
- package/package.json +16 -15
- package/process +1 -1
- package/src/authorization.js +184 -184
- package/src/config.js +7 -7
- package/src/index.js +19 -19
- package/test/integration/spec/authorization.js +166 -166
- package/test/unit/spec/authorization.js +69 -69
package/src/authorization.js
CHANGED
|
@@ -1,184 +1,184 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/* eslint camelcase: [0] */
|
|
6
|
-
|
|
7
|
-
import {oneFlight, whileInFlight} from '@webex/common';
|
|
8
|
-
import {grantErrors, WebexPlugin} from '@webex/webex-core';
|
|
9
|
-
|
|
10
|
-
import jwt from 'jsonwebtoken';
|
|
11
|
-
import uuid from 'uuid';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* NodeJS support for OAuth2
|
|
15
|
-
* @class
|
|
16
|
-
* @name AuthorizationNode
|
|
17
|
-
*/
|
|
18
|
-
const Authorization = WebexPlugin.extend({
|
|
19
|
-
derived: {
|
|
20
|
-
/**
|
|
21
|
-
* Alias of {@link AuthorizationNode#isAuthorizing}
|
|
22
|
-
* @instance
|
|
23
|
-
* @memberof AuthorizationNode
|
|
24
|
-
* @type {boolean}
|
|
25
|
-
*/
|
|
26
|
-
isAuthenticating: {
|
|
27
|
-
deps: ['isAuthorizing'],
|
|
28
|
-
fn() {
|
|
29
|
-
return this.isAuthorizing;
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
session: {
|
|
35
|
-
/**
|
|
36
|
-
* Indicates if an Authorization Code exchange is inflight
|
|
37
|
-
* @instance
|
|
38
|
-
* @memberof AuthorizationNode
|
|
39
|
-
* @type {boolean}
|
|
40
|
-
*/
|
|
41
|
-
isAuthorizing: {
|
|
42
|
-
default: false,
|
|
43
|
-
type: 'boolean',
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
namespace: 'Credentials',
|
|
48
|
-
|
|
49
|
-
logout(options = {}) {
|
|
50
|
-
this.webex.request({
|
|
51
|
-
method: 'POST',
|
|
52
|
-
uri: this.config.logoutUrl,
|
|
53
|
-
body: {
|
|
54
|
-
token: options.token,
|
|
55
|
-
cisService: this.config.service,
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
},
|
|
59
|
-
|
|
60
|
-
@whileInFlight('isAuthorizing')
|
|
61
|
-
@oneFlight
|
|
62
|
-
/**
|
|
63
|
-
* Exchanges an authorization code for an access token
|
|
64
|
-
* @instance
|
|
65
|
-
* @memberof AuthorizationNode
|
|
66
|
-
* @param {Object} options
|
|
67
|
-
* @param {Object} options.code
|
|
68
|
-
* @returns {Promise}
|
|
69
|
-
*/
|
|
70
|
-
requestAuthorizationCodeGrant(options = {}) {
|
|
71
|
-
this.logger.info('credentials: requesting authorization code grant');
|
|
72
|
-
|
|
73
|
-
if (!options.code) {
|
|
74
|
-
return Promise.reject(new Error('`options.code` is required'));
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return this.webex
|
|
78
|
-
.request({
|
|
79
|
-
method: 'POST',
|
|
80
|
-
uri: this.config.tokenUrl,
|
|
81
|
-
form: {
|
|
82
|
-
grant_type: 'authorization_code',
|
|
83
|
-
redirect_uri: this.config.redirect_uri,
|
|
84
|
-
code: options.code,
|
|
85
|
-
self_contained_token: true,
|
|
86
|
-
},
|
|
87
|
-
auth: {
|
|
88
|
-
user: this.config.client_id,
|
|
89
|
-
pass: this.config.client_secret,
|
|
90
|
-
sendImmediately: true,
|
|
91
|
-
},
|
|
92
|
-
shouldRefreshAccessToken: false,
|
|
93
|
-
})
|
|
94
|
-
.then((res) => {
|
|
95
|
-
this.webex.credentials.set({supertoken: res.body});
|
|
96
|
-
})
|
|
97
|
-
.catch((res) => {
|
|
98
|
-
if (res.statusCode !== 400) {
|
|
99
|
-
return Promise.reject(res);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const ErrorConstructor = grantErrors.select(res.body.error);
|
|
103
|
-
|
|
104
|
-
return Promise.reject(new ErrorConstructor(res._res || res));
|
|
105
|
-
});
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
@oneFlight
|
|
109
|
-
/**
|
|
110
|
-
* Requests a Webex access token for a user already authenticated into
|
|
111
|
-
* your product.
|
|
112
|
-
*
|
|
113
|
-
* Note: You'll need to supply a jwtRefreshCallback of the form
|
|
114
|
-
* `Promise<jwt> = jwtRefreshCallback(webex)` for automatic token refresh to
|
|
115
|
-
* work.
|
|
116
|
-
*
|
|
117
|
-
* @instance
|
|
118
|
-
* @memberof AuthorizationNode
|
|
119
|
-
* @param {Object} options
|
|
120
|
-
* @param {Object} options.jwt This is a jwt generated by your backend that
|
|
121
|
-
* identifies a user in your system
|
|
122
|
-
* @returns {Promise}
|
|
123
|
-
*/
|
|
124
|
-
requestAccessTokenFromJwt({jwt}) {
|
|
125
|
-
let hydraUri = this.webex.internal.services.get('hydra', true);
|
|
126
|
-
|
|
127
|
-
if (hydraUri && hydraUri.slice(-1) !== '/') {
|
|
128
|
-
// add a `/` to hydra's uri from the services catalog so that
|
|
129
|
-
// it matches the current env service format.
|
|
130
|
-
hydraUri += '/';
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
hydraUri = hydraUri || process.env.HYDRA_SERVICE_URL || 'https://api.ciscospark.com/v1/';
|
|
134
|
-
|
|
135
|
-
return this.webex
|
|
136
|
-
.request({
|
|
137
|
-
method: 'POST',
|
|
138
|
-
uri: `${hydraUri}jwt/login`,
|
|
139
|
-
headers: {
|
|
140
|
-
authorization: jwt,
|
|
141
|
-
},
|
|
142
|
-
})
|
|
143
|
-
.then(({body}) => ({
|
|
144
|
-
access_token: body.token,
|
|
145
|
-
token_type: 'Bearer',
|
|
146
|
-
expires_in: body.expiresIn,
|
|
147
|
-
}))
|
|
148
|
-
.then((token) => {
|
|
149
|
-
this.webex.credentials.set({
|
|
150
|
-
supertoken: token,
|
|
151
|
-
});
|
|
152
|
-
})
|
|
153
|
-
.then(() => this.webex.internal.services.initServiceCatalogs());
|
|
154
|
-
},
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Creates a jwt user token
|
|
158
|
-
* @param {object} options
|
|
159
|
-
* @param {String} options.issuer Guest Issuer ID
|
|
160
|
-
* @param {String} options.secretId Guest Secret ID
|
|
161
|
-
* @param {String} options.displayName Guest Display Name | optional
|
|
162
|
-
* @param {String} options.expiresIn
|
|
163
|
-
* @returns {Promise<object>}
|
|
164
|
-
*/
|
|
165
|
-
createJwt({issuer, secretId, displayName, expiresIn}) {
|
|
166
|
-
const secret = Buffer.from(secretId, 'base64');
|
|
167
|
-
const payload = {
|
|
168
|
-
"sub": `guest-user-${uuid()}`,
|
|
169
|
-
"iss": issuer,
|
|
170
|
-
"name": displayName || `Guest User - ${uuid()}`
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
try {
|
|
174
|
-
const jwtToken = jwt.sign(payload, secret,{ expiresIn });
|
|
175
|
-
|
|
176
|
-
return Promise.resolve({jwt: jwtToken});
|
|
177
|
-
} catch (e) {
|
|
178
|
-
return Promise.reject(e);
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
export default Authorization;
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/* eslint camelcase: [0] */
|
|
6
|
+
|
|
7
|
+
import {oneFlight, whileInFlight} from '@webex/common';
|
|
8
|
+
import {grantErrors, WebexPlugin} from '@webex/webex-core';
|
|
9
|
+
|
|
10
|
+
import jwt from 'jsonwebtoken';
|
|
11
|
+
import uuid from 'uuid';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* NodeJS support for OAuth2
|
|
15
|
+
* @class
|
|
16
|
+
* @name AuthorizationNode
|
|
17
|
+
*/
|
|
18
|
+
const Authorization = WebexPlugin.extend({
|
|
19
|
+
derived: {
|
|
20
|
+
/**
|
|
21
|
+
* Alias of {@link AuthorizationNode#isAuthorizing}
|
|
22
|
+
* @instance
|
|
23
|
+
* @memberof AuthorizationNode
|
|
24
|
+
* @type {boolean}
|
|
25
|
+
*/
|
|
26
|
+
isAuthenticating: {
|
|
27
|
+
deps: ['isAuthorizing'],
|
|
28
|
+
fn() {
|
|
29
|
+
return this.isAuthorizing;
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
session: {
|
|
35
|
+
/**
|
|
36
|
+
* Indicates if an Authorization Code exchange is inflight
|
|
37
|
+
* @instance
|
|
38
|
+
* @memberof AuthorizationNode
|
|
39
|
+
* @type {boolean}
|
|
40
|
+
*/
|
|
41
|
+
isAuthorizing: {
|
|
42
|
+
default: false,
|
|
43
|
+
type: 'boolean',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
namespace: 'Credentials',
|
|
48
|
+
|
|
49
|
+
logout(options = {}) {
|
|
50
|
+
this.webex.request({
|
|
51
|
+
method: 'POST',
|
|
52
|
+
uri: this.config.logoutUrl,
|
|
53
|
+
body: {
|
|
54
|
+
token: options.token,
|
|
55
|
+
cisService: this.config.service,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
@whileInFlight('isAuthorizing')
|
|
61
|
+
@oneFlight
|
|
62
|
+
/**
|
|
63
|
+
* Exchanges an authorization code for an access token
|
|
64
|
+
* @instance
|
|
65
|
+
* @memberof AuthorizationNode
|
|
66
|
+
* @param {Object} options
|
|
67
|
+
* @param {Object} options.code
|
|
68
|
+
* @returns {Promise}
|
|
69
|
+
*/
|
|
70
|
+
requestAuthorizationCodeGrant(options = {}) {
|
|
71
|
+
this.logger.info('credentials: requesting authorization code grant');
|
|
72
|
+
|
|
73
|
+
if (!options.code) {
|
|
74
|
+
return Promise.reject(new Error('`options.code` is required'));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return this.webex
|
|
78
|
+
.request({
|
|
79
|
+
method: 'POST',
|
|
80
|
+
uri: this.config.tokenUrl,
|
|
81
|
+
form: {
|
|
82
|
+
grant_type: 'authorization_code',
|
|
83
|
+
redirect_uri: this.config.redirect_uri,
|
|
84
|
+
code: options.code,
|
|
85
|
+
self_contained_token: true,
|
|
86
|
+
},
|
|
87
|
+
auth: {
|
|
88
|
+
user: this.config.client_id,
|
|
89
|
+
pass: this.config.client_secret,
|
|
90
|
+
sendImmediately: true,
|
|
91
|
+
},
|
|
92
|
+
shouldRefreshAccessToken: false,
|
|
93
|
+
})
|
|
94
|
+
.then((res) => {
|
|
95
|
+
this.webex.credentials.set({supertoken: res.body});
|
|
96
|
+
})
|
|
97
|
+
.catch((res) => {
|
|
98
|
+
if (res.statusCode !== 400) {
|
|
99
|
+
return Promise.reject(res);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const ErrorConstructor = grantErrors.select(res.body.error);
|
|
103
|
+
|
|
104
|
+
return Promise.reject(new ErrorConstructor(res._res || res));
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
@oneFlight
|
|
109
|
+
/**
|
|
110
|
+
* Requests a Webex access token for a user already authenticated into
|
|
111
|
+
* your product.
|
|
112
|
+
*
|
|
113
|
+
* Note: You'll need to supply a jwtRefreshCallback of the form
|
|
114
|
+
* `Promise<jwt> = jwtRefreshCallback(webex)` for automatic token refresh to
|
|
115
|
+
* work.
|
|
116
|
+
*
|
|
117
|
+
* @instance
|
|
118
|
+
* @memberof AuthorizationNode
|
|
119
|
+
* @param {Object} options
|
|
120
|
+
* @param {Object} options.jwt This is a jwt generated by your backend that
|
|
121
|
+
* identifies a user in your system
|
|
122
|
+
* @returns {Promise}
|
|
123
|
+
*/
|
|
124
|
+
requestAccessTokenFromJwt({jwt}) {
|
|
125
|
+
let hydraUri = this.webex.internal.services.get('hydra', true);
|
|
126
|
+
|
|
127
|
+
if (hydraUri && hydraUri.slice(-1) !== '/') {
|
|
128
|
+
// add a `/` to hydra's uri from the services catalog so that
|
|
129
|
+
// it matches the current env service format.
|
|
130
|
+
hydraUri += '/';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
hydraUri = hydraUri || process.env.HYDRA_SERVICE_URL || 'https://api.ciscospark.com/v1/';
|
|
134
|
+
|
|
135
|
+
return this.webex
|
|
136
|
+
.request({
|
|
137
|
+
method: 'POST',
|
|
138
|
+
uri: `${hydraUri}jwt/login`,
|
|
139
|
+
headers: {
|
|
140
|
+
authorization: jwt,
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
.then(({body}) => ({
|
|
144
|
+
access_token: body.token,
|
|
145
|
+
token_type: 'Bearer',
|
|
146
|
+
expires_in: body.expiresIn,
|
|
147
|
+
}))
|
|
148
|
+
.then((token) => {
|
|
149
|
+
this.webex.credentials.set({
|
|
150
|
+
supertoken: token,
|
|
151
|
+
});
|
|
152
|
+
})
|
|
153
|
+
.then(() => this.webex.internal.services.initServiceCatalogs());
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Creates a jwt user token
|
|
158
|
+
* @param {object} options
|
|
159
|
+
* @param {String} options.issuer Guest Issuer ID
|
|
160
|
+
* @param {String} options.secretId Guest Secret ID
|
|
161
|
+
* @param {String} options.displayName Guest Display Name | optional
|
|
162
|
+
* @param {String} options.expiresIn
|
|
163
|
+
* @returns {Promise<object>}
|
|
164
|
+
*/
|
|
165
|
+
createJwt({issuer, secretId, displayName, expiresIn}) {
|
|
166
|
+
const secret = Buffer.from(secretId, 'base64');
|
|
167
|
+
const payload = {
|
|
168
|
+
"sub": `guest-user-${uuid()}`,
|
|
169
|
+
"iss": issuer,
|
|
170
|
+
"name": displayName || `Guest User - ${uuid()}`
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const jwtToken = jwt.sign(payload, secret,{ expiresIn });
|
|
175
|
+
|
|
176
|
+
return Promise.resolve({jwt: jwtToken});
|
|
177
|
+
} catch (e) {
|
|
178
|
+
return Promise.reject(e);
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
export default Authorization;
|
package/src/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export default {
|
|
6
|
-
credentials: {},
|
|
7
|
-
};
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
credentials: {},
|
|
7
|
+
};
|
package/src/index.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import '@webex/internal-plugin-device';
|
|
6
|
-
import {registerPlugin} from '@webex/webex-core';
|
|
7
|
-
|
|
8
|
-
import Authorization from './authorization';
|
|
9
|
-
import config from './config';
|
|
10
|
-
|
|
11
|
-
const proxies = ['isAuthorizing', 'isAuthenticating'];
|
|
12
|
-
|
|
13
|
-
registerPlugin('authorization', Authorization, {
|
|
14
|
-
config,
|
|
15
|
-
proxies,
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
export {default} from './authorization';
|
|
19
|
-
export {default as config} from './config';
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import '@webex/internal-plugin-device';
|
|
6
|
+
import {registerPlugin} from '@webex/webex-core';
|
|
7
|
+
|
|
8
|
+
import Authorization from './authorization';
|
|
9
|
+
import config from './config';
|
|
10
|
+
|
|
11
|
+
const proxies = ['isAuthorizing', 'isAuthenticating'];
|
|
12
|
+
|
|
13
|
+
registerPlugin('authorization', Authorization, {
|
|
14
|
+
config,
|
|
15
|
+
proxies,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export {default} from './authorization';
|
|
19
|
+
export {default as config} from './config';
|