@api-client/core 0.5.20 → 0.5.23
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/build/browser.d.ts +7 -0
- package/build/browser.js +10 -0
- package/build/browser.js.map +1 -1
- package/build/index.d.ts +4 -0
- package/build/index.js +7 -0
- package/build/index.js.map +1 -1
- package/build/src/authorization/AuthorizationError.d.ts +23 -0
- package/build/src/authorization/AuthorizationError.js +33 -0
- package/build/src/authorization/AuthorizationError.js.map +1 -0
- package/build/src/authorization/CustomParameters.d.ts +24 -0
- package/build/src/authorization/CustomParameters.js +59 -0
- package/build/src/authorization/CustomParameters.js.map +1 -0
- package/build/src/authorization/OAuth2Authorization.d.ts +332 -0
- package/build/src/authorization/OAuth2Authorization.js +965 -0
- package/build/src/authorization/OAuth2Authorization.js.map +1 -0
- package/build/src/authorization/OidcAuthorization.d.ts +34 -0
- package/build/src/authorization/OidcAuthorization.js +139 -0
- package/build/src/authorization/OidcAuthorization.js.map +1 -0
- package/build/src/authorization/Utils.d.ts +51 -0
- package/build/src/authorization/Utils.js +122 -0
- package/build/src/authorization/Utils.js.map +1 -0
- package/build/src/authorization/lib/IframeAuthorization.d.ts +53 -0
- package/build/src/authorization/lib/IframeAuthorization.js +116 -0
- package/build/src/authorization/lib/IframeAuthorization.js.map +1 -0
- package/build/src/authorization/lib/KnownGrants.d.ts +6 -0
- package/build/src/authorization/lib/KnownGrants.js +7 -0
- package/build/src/authorization/lib/KnownGrants.js.map +1 -0
- package/build/src/authorization/lib/PopupAuthorization.d.ts +41 -0
- package/build/src/authorization/lib/PopupAuthorization.js +73 -0
- package/build/src/authorization/lib/PopupAuthorization.js.map +1 -0
- package/build/src/authorization/lib/Tokens.d.ts +55 -0
- package/build/src/authorization/lib/Tokens.js +117 -0
- package/build/src/authorization/lib/Tokens.js.map +1 -0
- package/build/src/authorization/types.d.ts +174 -0
- package/build/src/authorization/types.js +2 -0
- package/build/src/authorization/types.js.map +1 -0
- package/build/src/models/Authorization.d.ts +3 -5
- package/build/src/models/RequestAuthorization.js +4 -0
- package/build/src/models/RequestAuthorization.js.map +1 -1
- package/build/src/runtime/actions/runnable/DeleteCookieRunnable.d.ts +1 -1
- package/build/src/runtime/actions/runnable/SetCookieRunnable.d.ts +1 -1
- package/build/src/runtime/actions/runnable/SetVariableRunnable.d.ts +1 -1
- package/build/src/runtime/modules/RequestAuthorization.js +3 -7
- package/build/src/runtime/modules/RequestAuthorization.js.map +1 -1
- package/oauth-popup.html +29 -0
- package/package.json +3 -1
- package/src/authorization/AuthorizationError.ts +25 -0
- package/src/authorization/CustomParameters.ts +61 -0
- package/src/authorization/OAuth2Authorization.ts +1027 -0
- package/src/authorization/OidcAuthorization.ts +143 -0
- package/src/authorization/Utils.ts +126 -0
- package/src/authorization/lib/IframeAuthorization.ts +128 -0
- package/src/authorization/lib/KnownGrants.ts +6 -0
- package/src/authorization/lib/PopupAuthorization.ts +80 -0
- package/src/authorization/lib/Tokens.ts +124 -0
- package/src/authorization/types.ts +176 -0
- package/src/models/Authorization.ts +4 -5
- package/src/models/RequestAuthorization.ts +5 -1
- package/src/runtime/actions/runnable/DeleteCookieRunnable.ts +1 -1
- package/src/runtime/actions/runnable/SetCookieRunnable.ts +2 -2
- package/src/runtime/actions/runnable/SetVariableRunnable.ts +1 -1
- package/src/runtime/modules/RequestAuthorization.ts +3 -7
|
@@ -0,0 +1,965 @@
|
|
|
1
|
+
/* eslint-disable no-param-reassign */
|
|
2
|
+
/* eslint-disable class-methods-use-this */
|
|
3
|
+
import { sanityCheck, randomString, camel, generateCodeChallenge } from './Utils.js';
|
|
4
|
+
import { applyCustomSettingsQuery, applyCustomSettingsBody, applyCustomSettingsHeaders } from './CustomParameters.js';
|
|
5
|
+
import { AuthorizationError, CodeError } from './AuthorizationError.js';
|
|
6
|
+
import { IframeAuthorization } from './lib/IframeAuthorization.js';
|
|
7
|
+
import { PopupAuthorization } from './lib/PopupAuthorization.js';
|
|
8
|
+
import * as KnownGrants from './lib/KnownGrants.js';
|
|
9
|
+
export const resolveFunction = Symbol('resolveFunction');
|
|
10
|
+
export const rejectFunction = Symbol('rejectFunction');
|
|
11
|
+
export const settingsValue = Symbol('settingsValue');
|
|
12
|
+
export const optionsValue = Symbol('optionsValue');
|
|
13
|
+
export const prepareSettings = Symbol('prepareSettings');
|
|
14
|
+
export const prepareOptions = Symbol('prepareOptions');
|
|
15
|
+
export const authorize = Symbol('authorize');
|
|
16
|
+
export const stateValue = Symbol('stateValue');
|
|
17
|
+
export const authorizeImplicitCode = Symbol('authorizeImplicitCode');
|
|
18
|
+
export const authorizeClientCredentials = Symbol('authorizeClientCredentials');
|
|
19
|
+
export const authorizePassword = Symbol('authorizePassword');
|
|
20
|
+
export const authorizeCustomGrant = Symbol('authorizeCustomGrant');
|
|
21
|
+
export const authorizeDeviceCode = Symbol('authorizeDeviceCode');
|
|
22
|
+
export const authorizeJwt = Symbol('authorizeJwt');
|
|
23
|
+
export const popupValue = Symbol('popupValue');
|
|
24
|
+
export const popupUnloadHandler = Symbol('popupUnloadHandler');
|
|
25
|
+
export const tokenResponse = Symbol('tokenResponse');
|
|
26
|
+
export const messageHandler = Symbol('messageHandler');
|
|
27
|
+
export const iframeValue = Symbol('iframeValue');
|
|
28
|
+
export const processPopupRawData = Symbol('processPopupRawData');
|
|
29
|
+
export const handleTokenInfo = Symbol('handleTokenInfo');
|
|
30
|
+
export const computeTokenInfoScopes = Symbol('computeTokenInfoScopes');
|
|
31
|
+
export const computeExpires = Symbol('computeExpires');
|
|
32
|
+
export const codeValue = Symbol('codeValue');
|
|
33
|
+
export const frameTimeoutHandler = Symbol('frameTimeoutHandler');
|
|
34
|
+
export const reportOAuthError = Symbol('reportOAuthError');
|
|
35
|
+
export const authorizePopup = Symbol('authorizePopup');
|
|
36
|
+
export const authorizeTokenNonInteractive = Symbol('authorizeTokenNonInteractive');
|
|
37
|
+
export const createErrorParams = Symbol('createErrorParams');
|
|
38
|
+
export const handleTokenCodeError = Symbol('handleTokenCodeError');
|
|
39
|
+
export const codeVerifierValue = Symbol('codeVerifierValue');
|
|
40
|
+
export const tokenInfoFromParams = Symbol('tokenInfoFromParams');
|
|
41
|
+
export const grantResponseMapping = {
|
|
42
|
+
implicit: 'token',
|
|
43
|
+
authorization_code: 'code',
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* A library that performs OAuth 2 authorization.
|
|
47
|
+
*
|
|
48
|
+
* It is build for API components ecosystem and the configuration is defined in `@advanced-rest-client/events`
|
|
49
|
+
* so all components use the same configuration.
|
|
50
|
+
*/
|
|
51
|
+
export class OAuth2Authorization {
|
|
52
|
+
[settingsValue];
|
|
53
|
+
/**
|
|
54
|
+
* @returns The authorization settings used to initialize this class.
|
|
55
|
+
*/
|
|
56
|
+
get settings() {
|
|
57
|
+
return this[settingsValue];
|
|
58
|
+
}
|
|
59
|
+
[optionsValue];
|
|
60
|
+
/**
|
|
61
|
+
* @returns The processing options used to initialize this object.
|
|
62
|
+
*/
|
|
63
|
+
get options() {
|
|
64
|
+
return this[optionsValue];
|
|
65
|
+
}
|
|
66
|
+
[stateValue];
|
|
67
|
+
/**
|
|
68
|
+
* @returns The request state parameter. If the state is not passed with the configuration one is generated.
|
|
69
|
+
*/
|
|
70
|
+
get state() {
|
|
71
|
+
if (!this[stateValue]) {
|
|
72
|
+
this[stateValue] = this.settings.state || randomString();
|
|
73
|
+
}
|
|
74
|
+
return this[stateValue];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* @type The main resolve function
|
|
78
|
+
*/
|
|
79
|
+
[resolveFunction];
|
|
80
|
+
/**
|
|
81
|
+
* @type The main reject function
|
|
82
|
+
*/
|
|
83
|
+
[rejectFunction];
|
|
84
|
+
[codeVerifierValue];
|
|
85
|
+
[popupValue];
|
|
86
|
+
[iframeValue];
|
|
87
|
+
[tokenResponse];
|
|
88
|
+
[codeValue];
|
|
89
|
+
/**
|
|
90
|
+
* @param {OAuth2Settings} settings The authorization configuration.
|
|
91
|
+
* @param {OauthProcessingOptions=} options Additional processing options to configure the behavior of this library.
|
|
92
|
+
*/
|
|
93
|
+
constructor(settings, options = {}) {
|
|
94
|
+
if (!settings) {
|
|
95
|
+
throw new TypeError('Expected one argument.');
|
|
96
|
+
}
|
|
97
|
+
this[settingsValue] = this[prepareSettings](settings);
|
|
98
|
+
this[optionsValue] = this[prepareOptions](options);
|
|
99
|
+
this[messageHandler] = this[messageHandler].bind(this);
|
|
100
|
+
this[frameTimeoutHandler] = this[frameTimeoutHandler].bind(this);
|
|
101
|
+
this[popupUnloadHandler] = this[popupUnloadHandler].bind(this);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* @param settings
|
|
105
|
+
* @returns Processed settings
|
|
106
|
+
*/
|
|
107
|
+
[prepareSettings](settings) {
|
|
108
|
+
const copy = { ...settings };
|
|
109
|
+
Object.freeze(copy);
|
|
110
|
+
return copy;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* @param options
|
|
114
|
+
* @returns Processed options
|
|
115
|
+
*/
|
|
116
|
+
[prepareOptions](options) {
|
|
117
|
+
const copy = {
|
|
118
|
+
popupPullTimeout: 50,
|
|
119
|
+
messageTarget: window,
|
|
120
|
+
...options,
|
|
121
|
+
};
|
|
122
|
+
Object.freeze(copy);
|
|
123
|
+
return copy;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* A function that should be called before the authorization.
|
|
127
|
+
* It checks configuration integrity, and performs some sanity checks
|
|
128
|
+
* like proper values of the request URIs.
|
|
129
|
+
*/
|
|
130
|
+
checkConfig() {
|
|
131
|
+
// @todo(pawel): perform settings integrity tests.
|
|
132
|
+
sanityCheck(this.settings);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Performs the authorization.
|
|
136
|
+
* @returns Promise resolved to the token info.
|
|
137
|
+
*/
|
|
138
|
+
authorize() {
|
|
139
|
+
return new Promise((resolve, reject) => {
|
|
140
|
+
this[resolveFunction] = resolve;
|
|
141
|
+
this[rejectFunction] = reject;
|
|
142
|
+
this[authorize]();
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Reports authorization error back to the application.
|
|
147
|
+
*
|
|
148
|
+
* This operation clears the promise object.
|
|
149
|
+
*
|
|
150
|
+
* @param {string} message The message to report
|
|
151
|
+
* @param {string} code Error code
|
|
152
|
+
*/
|
|
153
|
+
[reportOAuthError](message, code) {
|
|
154
|
+
this.clearObservers();
|
|
155
|
+
if (!this[rejectFunction]) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const interactive = typeof this.settings.interactive === 'boolean' ? this.settings.interactive : true;
|
|
159
|
+
const e = new AuthorizationError(message, code, this.state, interactive);
|
|
160
|
+
this[rejectFunction](e);
|
|
161
|
+
this[rejectFunction] = undefined;
|
|
162
|
+
this[resolveFunction] = undefined;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Starts the authorization process.
|
|
166
|
+
*/
|
|
167
|
+
[authorize]() {
|
|
168
|
+
const { settings } = this;
|
|
169
|
+
switch (settings.grantType) {
|
|
170
|
+
case KnownGrants.implicit:
|
|
171
|
+
case KnownGrants.code:
|
|
172
|
+
this[authorizeImplicitCode]();
|
|
173
|
+
break;
|
|
174
|
+
case KnownGrants.clientCredentials:
|
|
175
|
+
this[authorizeClientCredentials]();
|
|
176
|
+
break;
|
|
177
|
+
case KnownGrants.password:
|
|
178
|
+
this[authorizePassword]();
|
|
179
|
+
break;
|
|
180
|
+
case KnownGrants.deviceCode:
|
|
181
|
+
this[authorizeDeviceCode]();
|
|
182
|
+
break;
|
|
183
|
+
case KnownGrants.jwtBearer:
|
|
184
|
+
this[authorizeJwt]();
|
|
185
|
+
break;
|
|
186
|
+
default:
|
|
187
|
+
this[authorizeCustomGrant]();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Starts the authorization flow for the `implicit` and `authorization_code` flows.
|
|
192
|
+
* If the `interactive` flag is configured it then it chooses between showing the UI (popup)
|
|
193
|
+
* or non-interactive iframe.
|
|
194
|
+
*/
|
|
195
|
+
async [authorizeImplicitCode]() {
|
|
196
|
+
const { settings } = this;
|
|
197
|
+
const url = await this.constructPopupUrl();
|
|
198
|
+
try {
|
|
199
|
+
if (!url) {
|
|
200
|
+
throw new Error(`Unable to construct the authorization URL.`);
|
|
201
|
+
}
|
|
202
|
+
if (settings.interactive === false) {
|
|
203
|
+
this[authorizeTokenNonInteractive](url);
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
this[authorizePopup](url);
|
|
207
|
+
}
|
|
208
|
+
this.options.messageTarget?.addEventListener('message', this[messageHandler]);
|
|
209
|
+
}
|
|
210
|
+
catch (e) {
|
|
211
|
+
this[rejectFunction](e);
|
|
212
|
+
this[rejectFunction] = undefined;
|
|
213
|
+
this[resolveFunction] = undefined;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Constructs the popup/iframe URL for the `implicit` or `authorization_code` grant types.
|
|
218
|
+
* @return Full URL for the endpoint.
|
|
219
|
+
*/
|
|
220
|
+
async constructPopupUrl() {
|
|
221
|
+
const url = await this.buildPopupUrlParams();
|
|
222
|
+
if (!url) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
return url.toString();
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* @returns The parameters to build popup URL.
|
|
229
|
+
*/
|
|
230
|
+
async buildPopupUrlParams() {
|
|
231
|
+
const { settings } = this;
|
|
232
|
+
const type = (settings.responseType || grantResponseMapping[settings.grantType]);
|
|
233
|
+
if (!type) {
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
const url = new URL(settings.authorizationUri);
|
|
237
|
+
url.searchParams.set('response_type', type);
|
|
238
|
+
url.searchParams.set('client_id', settings.clientId);
|
|
239
|
+
// Client secret cannot be ever exposed to the client (browser)!
|
|
240
|
+
// if (settings.clientSecret) {
|
|
241
|
+
// url.searchParams.set('client_secret', settings.clientSecret);
|
|
242
|
+
// }
|
|
243
|
+
url.searchParams.set('state', this.state);
|
|
244
|
+
if (settings.redirectUri) {
|
|
245
|
+
url.searchParams.set('redirect_uri', settings.redirectUri);
|
|
246
|
+
}
|
|
247
|
+
const { scopes } = settings;
|
|
248
|
+
if (Array.isArray(scopes) && scopes.length) {
|
|
249
|
+
url.searchParams.set('scope', scopes.join(' '));
|
|
250
|
+
}
|
|
251
|
+
if (settings.includeGrantedScopes) {
|
|
252
|
+
// this is Google specific
|
|
253
|
+
url.searchParams.set('include_granted_scopes', 'true');
|
|
254
|
+
}
|
|
255
|
+
if (settings.loginHint) {
|
|
256
|
+
// this is Google specific
|
|
257
|
+
url.searchParams.set('login_hint', settings.loginHint);
|
|
258
|
+
}
|
|
259
|
+
if (settings.interactive === false) {
|
|
260
|
+
// this is Google specific
|
|
261
|
+
url.searchParams.set('prompt', 'none');
|
|
262
|
+
}
|
|
263
|
+
if (settings.pkce && String(type).includes('code')) {
|
|
264
|
+
this[codeVerifierValue] = randomString();
|
|
265
|
+
const challenge = await generateCodeChallenge(this[codeVerifierValue]);
|
|
266
|
+
url.searchParams.set('code_challenge', challenge);
|
|
267
|
+
url.searchParams.set('code_challenge_method', 'S256');
|
|
268
|
+
}
|
|
269
|
+
// custom query parameters from the `api-authorization-method` component
|
|
270
|
+
if (settings.customData) {
|
|
271
|
+
const cs = settings.customData.auth;
|
|
272
|
+
if (cs) {
|
|
273
|
+
applyCustomSettingsQuery(url, cs);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return url;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Opens a popup to request authorization from the user.
|
|
280
|
+
* @param url The URL to open.
|
|
281
|
+
*/
|
|
282
|
+
[authorizePopup](url) {
|
|
283
|
+
const popup = new PopupAuthorization(this.options.popupPullTimeout);
|
|
284
|
+
try {
|
|
285
|
+
popup.load(url);
|
|
286
|
+
}
|
|
287
|
+
catch (e) {
|
|
288
|
+
const cause = e;
|
|
289
|
+
throw new AuthorizationError(cause.message, 'popup_blocked', this.state, this.settings.interactive || false);
|
|
290
|
+
}
|
|
291
|
+
popup.addEventListener('close', this[popupUnloadHandler]);
|
|
292
|
+
this[popupValue] = popup;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Tries to authorize the user in a non interactive way (iframe rather than a popup).
|
|
296
|
+
*
|
|
297
|
+
* This method always result in a success response. When there's an error or
|
|
298
|
+
* user is not logged in then the response won't contain auth token info.
|
|
299
|
+
*
|
|
300
|
+
* @param url Complete authorization url
|
|
301
|
+
*/
|
|
302
|
+
[authorizeTokenNonInteractive](url) {
|
|
303
|
+
const iframe = new IframeAuthorization(this.options.iframeTimeout);
|
|
304
|
+
iframe.addEventListener('timeout', this[frameTimeoutHandler]);
|
|
305
|
+
iframe.load(url);
|
|
306
|
+
this[iframeValue] = iframe;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Event handler for the the iframe timeout event.
|
|
310
|
+
* If there's the reject function then it is called with the error details.
|
|
311
|
+
*/
|
|
312
|
+
[frameTimeoutHandler]() {
|
|
313
|
+
if (!this[rejectFunction]) {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const e = new AuthorizationError('Non-interactive authorization failed.', 'iframe_load_error', this.state, false);
|
|
317
|
+
this[rejectFunction](e);
|
|
318
|
+
this[rejectFunction] = undefined;
|
|
319
|
+
this[resolveFunction] = undefined;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Clears all registered observers:
|
|
323
|
+
* - popup/iframe message listeners
|
|
324
|
+
* - popup info pull interval
|
|
325
|
+
*/
|
|
326
|
+
clearObservers() {
|
|
327
|
+
this.options.messageTarget?.removeEventListener('message', this[messageHandler]);
|
|
328
|
+
if (this[popupValue]) {
|
|
329
|
+
this[popupValue].cleanUp();
|
|
330
|
+
this[popupValue] = undefined;
|
|
331
|
+
}
|
|
332
|
+
if (this[iframeValue]) {
|
|
333
|
+
this[iframeValue].cancel();
|
|
334
|
+
this[iframeValue].cleanUp();
|
|
335
|
+
this[iframeValue] = undefined;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* This is called when the popup info pull interval detects that the window was closed.
|
|
340
|
+
* It checks whether the token info has been set by the redirect page and if not then it reports an error.
|
|
341
|
+
*/
|
|
342
|
+
[popupUnloadHandler]() {
|
|
343
|
+
if (this[tokenResponse] || (this.settings.grantType === 'authorization_code' && this[codeValue])) {
|
|
344
|
+
// everything seems to be ok.
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (!this[rejectFunction]) {
|
|
348
|
+
// someone already called it.
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
this[reportOAuthError]('No response has been recorded.', 'no_response');
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* A handler for the `message` event registered when performing authorization that involves the popup
|
|
355
|
+
* of the iframe.
|
|
356
|
+
*/
|
|
357
|
+
[messageHandler](e) {
|
|
358
|
+
const popup = this[popupValue];
|
|
359
|
+
const iframe = this[iframeValue];
|
|
360
|
+
if (!popup && !iframe) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
this[processPopupRawData](e.data);
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* @param {any} raw The data from the `MessageEvent`. Might not be the data returned by the auth popup/iframe.
|
|
367
|
+
*/
|
|
368
|
+
[processPopupRawData](raw) {
|
|
369
|
+
if (!raw) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
let params;
|
|
373
|
+
try {
|
|
374
|
+
params = new URLSearchParams(raw);
|
|
375
|
+
}
|
|
376
|
+
catch (e) {
|
|
377
|
+
// @ts-ignore
|
|
378
|
+
this[reportOAuthError]('Invalid response from the redirect page');
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
if (this.validateTokenResponse(params)) {
|
|
382
|
+
this.processTokenResponse(params);
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
// eslint-disable-next-line no-console
|
|
386
|
+
console.warn('Unprocessable authorization response', raw);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* @param {URLSearchParams} params The instance of search params with the response from the auth dialog.
|
|
391
|
+
* @returns {boolean} true when the params qualify as an authorization popup redirect response.
|
|
392
|
+
*/
|
|
393
|
+
validateTokenResponse(params) {
|
|
394
|
+
const oauthParams = [
|
|
395
|
+
'state',
|
|
396
|
+
'error',
|
|
397
|
+
'access_token',
|
|
398
|
+
'code',
|
|
399
|
+
];
|
|
400
|
+
return oauthParams.some(name => params.has(name));
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Processes the response returned by the popup or the iframe.
|
|
404
|
+
*/
|
|
405
|
+
async processTokenResponse(oauthParams) {
|
|
406
|
+
this.clearObservers();
|
|
407
|
+
const state = oauthParams.get('state');
|
|
408
|
+
if (!state) {
|
|
409
|
+
this[reportOAuthError]('Server did not return the state parameter.', 'no_state');
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
if (state !== this.state) {
|
|
413
|
+
// The authorization class (this) is created per token request so this can only have one state.
|
|
414
|
+
// When the app requests for more tokens at the same time is should create multiple instances of this.
|
|
415
|
+
this[reportOAuthError]('The state value returned by the authorization server is invalid.', 'invalid_state');
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
if (oauthParams.has('error')) {
|
|
419
|
+
const args = this.createTokenResponseError(oauthParams);
|
|
420
|
+
// @ts-ignore
|
|
421
|
+
this[reportOAuthError](...args);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const { grantType, responseType } = this.settings;
|
|
425
|
+
if (grantType === 'implicit' || responseType === 'id_token') {
|
|
426
|
+
this[handleTokenInfo](this[tokenInfoFromParams](oauthParams));
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (grantType === 'authorization_code') {
|
|
430
|
+
const code = oauthParams.get('code');
|
|
431
|
+
if (!code) {
|
|
432
|
+
this[reportOAuthError]('The authorization server did not returned the authorization code.', 'no_code');
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
this[codeValue] = code;
|
|
436
|
+
let tokenInfo;
|
|
437
|
+
try {
|
|
438
|
+
tokenInfo = await this.exchangeCode(code);
|
|
439
|
+
}
|
|
440
|
+
catch (e) {
|
|
441
|
+
this[handleTokenCodeError](e);
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
this[handleTokenInfo](tokenInfo);
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
this[reportOAuthError]('The authorization process has an invalid state. This should never happen.', 'unknown_state');
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Processes the response returned by the popup or the iframe.
|
|
451
|
+
* @returns Parameters for the [reportOAuthError]() function
|
|
452
|
+
*/
|
|
453
|
+
createTokenResponseError(oauthParams) {
|
|
454
|
+
const code = oauthParams.get('error');
|
|
455
|
+
const message = oauthParams.get('error_description');
|
|
456
|
+
return this[createErrorParams](code, message);
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* Creates arguments for the error function from error response
|
|
460
|
+
* @param code Returned from the authorization server error code
|
|
461
|
+
* @param description Returned from the authorization server error description
|
|
462
|
+
* @returns Parameters for the [reportOAuthError]() function
|
|
463
|
+
*/
|
|
464
|
+
[createErrorParams](code, description) {
|
|
465
|
+
let message;
|
|
466
|
+
if (description) {
|
|
467
|
+
message = description;
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
switch (code) {
|
|
471
|
+
case 'interaction_required':
|
|
472
|
+
message = 'The request requires user interaction.';
|
|
473
|
+
break;
|
|
474
|
+
case 'invalid_request':
|
|
475
|
+
message = 'The request is missing a required parameter.';
|
|
476
|
+
break;
|
|
477
|
+
case 'invalid_client':
|
|
478
|
+
message = 'Client authentication failed.';
|
|
479
|
+
break;
|
|
480
|
+
case 'invalid_grant':
|
|
481
|
+
message = 'The provided authorization grant or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.';
|
|
482
|
+
break;
|
|
483
|
+
case 'unauthorized_client':
|
|
484
|
+
message = 'The authenticated client is not authorized to use this authorization grant type.';
|
|
485
|
+
break;
|
|
486
|
+
case 'unsupported_grant_type':
|
|
487
|
+
message = 'The authorization grant type is not supported by the authorization server.';
|
|
488
|
+
break;
|
|
489
|
+
case 'invalid_scope':
|
|
490
|
+
message = 'The requested scope is invalid, unknown, malformed, or exceeds the scope granted by the resource owner.';
|
|
491
|
+
break;
|
|
492
|
+
default:
|
|
493
|
+
message = 'Unknown error';
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return [message, code];
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Creates a token info object from query parameters
|
|
500
|
+
*/
|
|
501
|
+
[tokenInfoFromParams](oauthParams) {
|
|
502
|
+
const accessToken = oauthParams.get('access_token');
|
|
503
|
+
const idToken = oauthParams.get('id_token');
|
|
504
|
+
const refreshToken = oauthParams.get('refresh_token');
|
|
505
|
+
const tokenType = oauthParams.get('token_type');
|
|
506
|
+
const expiresIn = Number(oauthParams.get('expires_in'));
|
|
507
|
+
const scope = this[computeTokenInfoScopes](oauthParams.get('scope'));
|
|
508
|
+
const tokenInfo = {
|
|
509
|
+
accessToken,
|
|
510
|
+
// FIXME: This should be set in the OIDC class.
|
|
511
|
+
// @ts-ignore
|
|
512
|
+
idToken,
|
|
513
|
+
refreshToken,
|
|
514
|
+
tokenType,
|
|
515
|
+
expiresIn,
|
|
516
|
+
state: oauthParams.get('state'),
|
|
517
|
+
scope,
|
|
518
|
+
expiresAt: 0,
|
|
519
|
+
expiresAssumed: false,
|
|
520
|
+
};
|
|
521
|
+
return this[computeExpires](tokenInfo);
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Processes token info object when it's ready.
|
|
525
|
+
*
|
|
526
|
+
* @param info Token info returned from the server.
|
|
527
|
+
*/
|
|
528
|
+
[handleTokenInfo](info) {
|
|
529
|
+
this[tokenResponse] = info;
|
|
530
|
+
if (this[resolveFunction]) {
|
|
531
|
+
this[resolveFunction](info);
|
|
532
|
+
}
|
|
533
|
+
this[rejectFunction] = undefined;
|
|
534
|
+
this[resolveFunction] = undefined;
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Computes token expiration time.
|
|
538
|
+
* It sets `expires_at` property on the token info object which is the time
|
|
539
|
+
* in the future when when the token expires.
|
|
540
|
+
*
|
|
541
|
+
* @param tokenInfo Token info object
|
|
542
|
+
* @returns A copy with updated properties.
|
|
543
|
+
*/
|
|
544
|
+
[computeExpires](tokenInfo) {
|
|
545
|
+
const copy = { ...tokenInfo };
|
|
546
|
+
let { expiresIn } = copy;
|
|
547
|
+
if (!expiresIn || Number.isNaN(expiresIn)) {
|
|
548
|
+
expiresIn = 3600;
|
|
549
|
+
copy.expiresAssumed = true;
|
|
550
|
+
}
|
|
551
|
+
copy.expiresIn = expiresIn;
|
|
552
|
+
const expiresAt = Date.now() + (expiresIn * 1000);
|
|
553
|
+
copy.expiresAt = expiresAt;
|
|
554
|
+
return copy;
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Computes the final list of granted scopes.
|
|
558
|
+
* It is a list of scopes received in the response or the list of requested scopes.
|
|
559
|
+
* Because the user may change the list of scopes during the authorization process
|
|
560
|
+
* the received list of scopes can be different than the one requested by the user.
|
|
561
|
+
*
|
|
562
|
+
* @param scope The `scope` parameter received with the response. It's null safe.
|
|
563
|
+
* @returns The list of scopes for the token.
|
|
564
|
+
*/
|
|
565
|
+
[computeTokenInfoScopes](scope) {
|
|
566
|
+
const requestedScopes = this.settings.scopes;
|
|
567
|
+
if (!scope && requestedScopes) {
|
|
568
|
+
return requestedScopes;
|
|
569
|
+
}
|
|
570
|
+
let listScopes = [];
|
|
571
|
+
if (scope) {
|
|
572
|
+
listScopes = scope.split(' ');
|
|
573
|
+
}
|
|
574
|
+
return listScopes;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Exchanges the authorization code for authorization token.
|
|
578
|
+
*
|
|
579
|
+
* @param code Returned code from the authorization endpoint.
|
|
580
|
+
* @returns The response from the server.
|
|
581
|
+
*/
|
|
582
|
+
async getCodeInfo(code) {
|
|
583
|
+
const body = this.getCodeRequestBody(code);
|
|
584
|
+
const url = this.settings.accessTokenUri;
|
|
585
|
+
return this.requestTokenInfo(url, body);
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Requests for token from the authorization server for `code`, `password`, `client_credentials` and custom grant types.
|
|
589
|
+
*
|
|
590
|
+
* @param url Base URI of the endpoint. Custom properties will be applied to the final URL.
|
|
591
|
+
* @param body Generated body for given type. Custom properties will be applied to the final body.
|
|
592
|
+
* @param optHeaders Optional headers to add to the request. Applied after custom data.
|
|
593
|
+
* @returns Promise resolved to the response string.
|
|
594
|
+
*/
|
|
595
|
+
async requestTokenInfo(url, body, optHeaders) {
|
|
596
|
+
const urlInstance = new URL(url);
|
|
597
|
+
const { settings, options } = this;
|
|
598
|
+
let headers = {
|
|
599
|
+
'content-type': 'application/x-www-form-urlencoded',
|
|
600
|
+
};
|
|
601
|
+
if (settings.customData) {
|
|
602
|
+
if (settings.customData.token) {
|
|
603
|
+
applyCustomSettingsQuery(urlInstance, settings.customData.token);
|
|
604
|
+
}
|
|
605
|
+
body = applyCustomSettingsBody(body, settings.customData);
|
|
606
|
+
headers = applyCustomSettingsHeaders(headers, settings.customData);
|
|
607
|
+
}
|
|
608
|
+
if (optHeaders) {
|
|
609
|
+
headers = { ...headers, ...optHeaders };
|
|
610
|
+
}
|
|
611
|
+
const init = {
|
|
612
|
+
headers,
|
|
613
|
+
body,
|
|
614
|
+
method: 'POST',
|
|
615
|
+
cache: 'no-cache',
|
|
616
|
+
};
|
|
617
|
+
let authTokenUrl = urlInstance.toString();
|
|
618
|
+
if (options.tokenProxy) {
|
|
619
|
+
const suffix = options.tokenProxyEncode ? encodeURIComponent(authTokenUrl) : authTokenUrl;
|
|
620
|
+
authTokenUrl = `${options.tokenProxy}${suffix}`;
|
|
621
|
+
}
|
|
622
|
+
const response = await fetch(authTokenUrl, init);
|
|
623
|
+
const { status } = response;
|
|
624
|
+
if (status === 404) {
|
|
625
|
+
throw new Error('Authorization URI is invalid. Received status 404.');
|
|
626
|
+
}
|
|
627
|
+
if (status >= 500) {
|
|
628
|
+
throw new Error(`Authorization server error. Response code is: ${status}`);
|
|
629
|
+
}
|
|
630
|
+
let responseBody;
|
|
631
|
+
try {
|
|
632
|
+
responseBody = await response.text();
|
|
633
|
+
}
|
|
634
|
+
catch (e) {
|
|
635
|
+
responseBody = 'No response has been recorded';
|
|
636
|
+
}
|
|
637
|
+
if (!responseBody) {
|
|
638
|
+
throw new Error('Code response body is empty.');
|
|
639
|
+
}
|
|
640
|
+
if (status >= 400 && status < 500) {
|
|
641
|
+
throw new Error(`Client error: ${responseBody}`);
|
|
642
|
+
}
|
|
643
|
+
const mime = response.headers.get('content-type') || '';
|
|
644
|
+
return this.processCodeResponse(responseBody, mime);
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Processes body of the code exchange to a map of key value pairs.
|
|
648
|
+
*/
|
|
649
|
+
processCodeResponse(body, mime = '') {
|
|
650
|
+
let tokenInfo = {};
|
|
651
|
+
if (mime.includes('json')) {
|
|
652
|
+
const info = JSON.parse(body);
|
|
653
|
+
Object.keys(info).forEach((key) => {
|
|
654
|
+
let name = key;
|
|
655
|
+
if (name.includes('_') || name.includes('-')) {
|
|
656
|
+
name = camel(name);
|
|
657
|
+
}
|
|
658
|
+
if (name) {
|
|
659
|
+
tokenInfo[name] = info[key];
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
tokenInfo = {};
|
|
665
|
+
const params = new URLSearchParams(body);
|
|
666
|
+
params.forEach((value, key) => {
|
|
667
|
+
let name = key;
|
|
668
|
+
if (key.includes('_') || key.includes('-')) {
|
|
669
|
+
name = camel(key);
|
|
670
|
+
}
|
|
671
|
+
if (name) {
|
|
672
|
+
tokenInfo[name] = value;
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
return tokenInfo;
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* @returns The token info when the request was a success.
|
|
680
|
+
*/
|
|
681
|
+
mapCodeResponse(info) {
|
|
682
|
+
if (info.error) {
|
|
683
|
+
throw new CodeError(info.errorDescription, info.error);
|
|
684
|
+
}
|
|
685
|
+
const expiresIn = Number(info.expiresIn);
|
|
686
|
+
const scope = this[computeTokenInfoScopes](info.scope);
|
|
687
|
+
const result = {
|
|
688
|
+
...info,
|
|
689
|
+
expiresIn,
|
|
690
|
+
scope,
|
|
691
|
+
expiresAt: 0,
|
|
692
|
+
expiresAssumed: false,
|
|
693
|
+
};
|
|
694
|
+
return this[computeExpires](result);
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Exchanges the authorization code for authorization token.
|
|
698
|
+
*
|
|
699
|
+
* @param code Returned code from the authorization endpoint.
|
|
700
|
+
* @returns The token info when the request was a success.
|
|
701
|
+
*/
|
|
702
|
+
async exchangeCode(code) {
|
|
703
|
+
const info = await this.getCodeInfo(code);
|
|
704
|
+
return this.mapCodeResponse(info);
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Returns a body value for the code exchange request.
|
|
708
|
+
* @param code Authorization code value returned by the authorization server.
|
|
709
|
+
* @return Request body.
|
|
710
|
+
*/
|
|
711
|
+
getCodeRequestBody(code) {
|
|
712
|
+
const { settings } = this;
|
|
713
|
+
const params = new URLSearchParams();
|
|
714
|
+
params.set('grant_type', 'authorization_code');
|
|
715
|
+
params.set('client_id', settings.clientId);
|
|
716
|
+
if (settings.redirectUri) {
|
|
717
|
+
params.set('redirect_uri', settings.redirectUri);
|
|
718
|
+
}
|
|
719
|
+
params.set('code', code);
|
|
720
|
+
if (settings.clientSecret) {
|
|
721
|
+
params.set('client_secret', settings.clientSecret);
|
|
722
|
+
}
|
|
723
|
+
else {
|
|
724
|
+
params.set('client_secret', '');
|
|
725
|
+
}
|
|
726
|
+
if (settings.pkce) {
|
|
727
|
+
params.set('code_verifier', this[codeVerifierValue]);
|
|
728
|
+
}
|
|
729
|
+
return params.toString();
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* A handler for the error that happened during code exchange.
|
|
733
|
+
*/
|
|
734
|
+
[handleTokenCodeError](e) {
|
|
735
|
+
if (e instanceof CodeError) {
|
|
736
|
+
// @ts-ignore
|
|
737
|
+
this[reportOAuthError](...this[createErrorParams](e.code, e.message));
|
|
738
|
+
}
|
|
739
|
+
else {
|
|
740
|
+
this[reportOAuthError](`Couldn't connect to the server. ${e.message}`, 'request_error');
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Requests a token for `client_credentials` request type.
|
|
745
|
+
*
|
|
746
|
+
* This method resolves the main promise set by the `authorize()` function.
|
|
747
|
+
*
|
|
748
|
+
* @return Promise resolved to a token info object.
|
|
749
|
+
*/
|
|
750
|
+
async [authorizeClientCredentials]() {
|
|
751
|
+
const { settings } = this;
|
|
752
|
+
const { accessTokenUri, deliveryMethod = 'body', deliveryName = 'authorization' } = settings;
|
|
753
|
+
const body = this.getClientCredentialsBody();
|
|
754
|
+
let headers = undefined;
|
|
755
|
+
const headerTransport = deliveryMethod === 'header';
|
|
756
|
+
if (headerTransport) {
|
|
757
|
+
headers = {
|
|
758
|
+
[deliveryName]: this.getClientCredentialsHeader(settings),
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
try {
|
|
762
|
+
const info = await this.requestTokenInfo(accessTokenUri, body, headers);
|
|
763
|
+
const tokenInfo = this.mapCodeResponse(info);
|
|
764
|
+
this[handleTokenInfo](tokenInfo);
|
|
765
|
+
}
|
|
766
|
+
catch (cause) {
|
|
767
|
+
this[handleTokenCodeError](cause);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Generates a payload message for client credentials.
|
|
772
|
+
*
|
|
773
|
+
* @return Message body as defined in OAuth2 spec.
|
|
774
|
+
*/
|
|
775
|
+
getClientCredentialsBody() {
|
|
776
|
+
const { settings } = this;
|
|
777
|
+
const headerTransport = settings.deliveryMethod === 'header';
|
|
778
|
+
const params = new URLSearchParams();
|
|
779
|
+
params.set('grant_type', 'client_credentials');
|
|
780
|
+
if (!headerTransport && settings.clientId) {
|
|
781
|
+
params.set('client_id', settings.clientId);
|
|
782
|
+
}
|
|
783
|
+
if (!headerTransport && settings.clientSecret) {
|
|
784
|
+
params.set('client_secret', settings.clientSecret);
|
|
785
|
+
}
|
|
786
|
+
if (Array.isArray(settings.scopes) && settings.scopes.length) {
|
|
787
|
+
params.set('scope', settings.scopes.join(' '));
|
|
788
|
+
}
|
|
789
|
+
return params.toString();
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Builds the authorization header for Client Credentials grant type.
|
|
793
|
+
* According to the spec the authorization header for this grant type
|
|
794
|
+
* is the Base64 of `clientId` + `:` + `clientSecret`.
|
|
795
|
+
*
|
|
796
|
+
* @param settings The OAuth 2 settings to use
|
|
797
|
+
*/
|
|
798
|
+
getClientCredentialsHeader(settings) {
|
|
799
|
+
const { clientId = '', clientSecret = '' } = settings;
|
|
800
|
+
const hash = btoa(`${clientId}:${clientSecret}`);
|
|
801
|
+
return `Basic ${hash}`;
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Requests a token for `client_credentials` request type.
|
|
805
|
+
*
|
|
806
|
+
* This method resolves the main promise set by the `authorize()` function.
|
|
807
|
+
*
|
|
808
|
+
* @return Promise resolved to a token info object.
|
|
809
|
+
*/
|
|
810
|
+
async [authorizePassword]() {
|
|
811
|
+
const { settings } = this;
|
|
812
|
+
const url = settings.accessTokenUri;
|
|
813
|
+
const body = this.getPasswordBody();
|
|
814
|
+
try {
|
|
815
|
+
const info = await this.requestTokenInfo(url, body);
|
|
816
|
+
const tokenInfo = this.mapCodeResponse(info);
|
|
817
|
+
this[handleTokenInfo](tokenInfo);
|
|
818
|
+
}
|
|
819
|
+
catch (cause) {
|
|
820
|
+
this[handleTokenCodeError](cause);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Generates a payload message for password authorization.
|
|
825
|
+
*
|
|
826
|
+
* @return Message body as defined in OAuth2 spec.
|
|
827
|
+
*/
|
|
828
|
+
getPasswordBody() {
|
|
829
|
+
const { settings } = this;
|
|
830
|
+
const params = new URLSearchParams();
|
|
831
|
+
params.set('grant_type', 'password');
|
|
832
|
+
params.set('username', settings.username || '');
|
|
833
|
+
params.set('password', settings.password || '');
|
|
834
|
+
if (settings.clientId) {
|
|
835
|
+
params.set('client_id', settings.clientId);
|
|
836
|
+
}
|
|
837
|
+
if (settings.clientSecret) {
|
|
838
|
+
params.set('client_secret', settings.clientSecret);
|
|
839
|
+
}
|
|
840
|
+
if (Array.isArray(settings.scopes) && settings.scopes.length) {
|
|
841
|
+
params.set('scope', settings.scopes.join(' '));
|
|
842
|
+
}
|
|
843
|
+
return params.toString();
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Performs authorization on custom grant type.
|
|
847
|
+
* This extension is described in OAuth 2.0 spec.
|
|
848
|
+
*
|
|
849
|
+
* This method resolves the main promise set by the `authorize()` function.
|
|
850
|
+
*
|
|
851
|
+
* @return Promise resolved when the request finish.
|
|
852
|
+
*/
|
|
853
|
+
async [authorizeCustomGrant]() {
|
|
854
|
+
const { settings } = this;
|
|
855
|
+
const url = settings.accessTokenUri;
|
|
856
|
+
const body = this.getCustomGrantBody();
|
|
857
|
+
try {
|
|
858
|
+
const info = await this.requestTokenInfo(url, body);
|
|
859
|
+
const tokenInfo = this.mapCodeResponse(info);
|
|
860
|
+
this[handleTokenInfo](tokenInfo);
|
|
861
|
+
}
|
|
862
|
+
catch (cause) {
|
|
863
|
+
this[handleTokenCodeError](cause);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Generates a payload message for the custom grant.
|
|
868
|
+
*
|
|
869
|
+
* @returns Message body as defined in OAuth2 spec.
|
|
870
|
+
*/
|
|
871
|
+
getCustomGrantBody() {
|
|
872
|
+
const { settings } = this;
|
|
873
|
+
const params = new URLSearchParams();
|
|
874
|
+
params.set('grant_type', settings.grantType);
|
|
875
|
+
if (settings.clientId) {
|
|
876
|
+
params.set('client_id', settings.clientId);
|
|
877
|
+
}
|
|
878
|
+
if (settings.clientSecret) {
|
|
879
|
+
params.set('client_secret', settings.clientSecret);
|
|
880
|
+
}
|
|
881
|
+
if (Array.isArray(settings.scopes) && settings.scopes.length) {
|
|
882
|
+
params.set('scope', settings.scopes.join(' '));
|
|
883
|
+
}
|
|
884
|
+
if (settings.redirectUri) {
|
|
885
|
+
params.set('redirect_uri', settings.redirectUri);
|
|
886
|
+
}
|
|
887
|
+
if (settings.username) {
|
|
888
|
+
params.set('username', settings.username);
|
|
889
|
+
}
|
|
890
|
+
if (settings.password) {
|
|
891
|
+
params.set('password', settings.password);
|
|
892
|
+
}
|
|
893
|
+
return params.toString();
|
|
894
|
+
}
|
|
895
|
+
/**
|
|
896
|
+
* Requests a token for the `urn:ietf:params:oauth:grant-type:device_code` response type.
|
|
897
|
+
*
|
|
898
|
+
* @returns Promise resolved to a token info object.
|
|
899
|
+
*/
|
|
900
|
+
async [authorizeDeviceCode]() {
|
|
901
|
+
const { settings } = this;
|
|
902
|
+
const url = settings.accessTokenUri;
|
|
903
|
+
const body = this.getDeviceCodeBody();
|
|
904
|
+
try {
|
|
905
|
+
const info = await this.requestTokenInfo(url, body);
|
|
906
|
+
const tokenInfo = this.mapCodeResponse(info);
|
|
907
|
+
this[handleTokenInfo](tokenInfo);
|
|
908
|
+
}
|
|
909
|
+
catch (cause) {
|
|
910
|
+
this[handleTokenCodeError](cause);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Generates a payload message for the `urn:ietf:params:oauth:grant-type:device_code` authorization.
|
|
915
|
+
*
|
|
916
|
+
* @returns Message body as defined in OAuth2 spec.
|
|
917
|
+
*/
|
|
918
|
+
getDeviceCodeBody() {
|
|
919
|
+
const { settings } = this;
|
|
920
|
+
const params = new URLSearchParams();
|
|
921
|
+
params.set('grant_type', KnownGrants.deviceCode);
|
|
922
|
+
params.set('device_code', settings.deviceCode || '');
|
|
923
|
+
if (settings.clientId) {
|
|
924
|
+
params.set('client_id', settings.clientId);
|
|
925
|
+
}
|
|
926
|
+
if (settings.clientSecret) {
|
|
927
|
+
params.set('client_secret', settings.clientSecret);
|
|
928
|
+
}
|
|
929
|
+
return params.toString();
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Requests a token for the `urn:ietf:params:oauth:grant-type:jwt-bearer` response type.
|
|
933
|
+
*
|
|
934
|
+
* @return Promise resolved to a token info object.
|
|
935
|
+
*/
|
|
936
|
+
async [authorizeJwt]() {
|
|
937
|
+
const { settings } = this;
|
|
938
|
+
const url = settings.accessTokenUri;
|
|
939
|
+
const body = this.getJwtBody();
|
|
940
|
+
try {
|
|
941
|
+
const info = await this.requestTokenInfo(url, body);
|
|
942
|
+
const tokenInfo = this.mapCodeResponse(info);
|
|
943
|
+
this[handleTokenInfo](tokenInfo);
|
|
944
|
+
}
|
|
945
|
+
catch (cause) {
|
|
946
|
+
this[handleTokenCodeError](cause);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* Generates a payload message for the `urn:ietf:params:oauth:grant-type:jwt-bearer` authorization.
|
|
951
|
+
*
|
|
952
|
+
* @return {string} Message body as defined in OAuth2 spec.
|
|
953
|
+
*/
|
|
954
|
+
getJwtBody() {
|
|
955
|
+
const { settings } = this;
|
|
956
|
+
const params = new URLSearchParams();
|
|
957
|
+
params.set('grant_type', KnownGrants.jwtBearer);
|
|
958
|
+
params.set('assertion', settings.assertion || '');
|
|
959
|
+
if (Array.isArray(settings.scopes) && settings.scopes.length) {
|
|
960
|
+
params.set('scope', settings.scopes.join(' '));
|
|
961
|
+
}
|
|
962
|
+
return params.toString();
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
//# sourceMappingURL=OAuth2Authorization.js.map
|