From 8d6686c22644bfad45eaace5b26a7be8ab978b40 Mon Sep 17 00:00:00 2001 From: siyuniu-ms <123212536+siyuniu-ms@users.noreply.github.com> Date: Thu, 11 Jul 2024 17:29:29 -0700 Subject: [PATCH] [main] add withCredentials config (#2373) --- channels/1ds-post-js/src/DataModels.ts | 17 ++++++++++++ channels/1ds-post-js/src/HttpManager.ts | 9 +++++-- channels/1ds-post-js/src/PostChannel.ts | 1 + .../test/Unit/src/HttpManagerTest.ts | 17 ++++++++++++ .../test/Unit/src/PostChannelTest.ts | 26 +++++++++++++++++++ channels/offline-channel-js/src/Sender.ts | 10 +++++-- .../ISenderPostManager.ts | 17 ++++++++++++ .../src/JavaScriptSDK/SenderPostManager.ts | 7 ++++- 8 files changed, 99 insertions(+), 5 deletions(-) diff --git a/channels/1ds-post-js/src/DataModels.ts b/channels/1ds-post-js/src/DataModels.ts index 3a40f714..6ec624df 100644 --- a/channels/1ds-post-js/src/DataModels.ts +++ b/channels/1ds-post-js/src/DataModels.ts @@ -245,6 +245,23 @@ export interface IChannelConfiguration { * @since 4.1.0 */ excludeCsMetaData?: boolean; + + /** + * [Optional] Specify whether cross-site Access-Control fetch requests should include credentials such as cookies, + * authentication headers, or TLS client certificates. + * + * Possible values: + * - "omit": never send credentials in the request or include credentials in the response. + * - "include": always include credentials, even cross-origin. + * - "same-origin": only send and include credentials for same-origin requests. + * + * If not set, the default value will be "include". + * + * For more information, refer to: + * - [Fetch API - Using Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#including_credentials) + * @since 3.3.1 + */ + fetchCredentials?: RequestCredentials; } /** diff --git a/channels/1ds-post-js/src/HttpManager.ts b/channels/1ds-post-js/src/HttpManager.ts index 917ee0f1..45bd902d 100644 --- a/channels/1ds-post-js/src/HttpManager.ts +++ b/channels/1ds-post-js/src/HttpManager.ts @@ -177,6 +177,7 @@ export class HttpManager { let _timeoutWrapper: ITimeoutOverrideWrapper; let _excludeCsMetaData: boolean; let _sendPostMgr: SenderPostManager; + let _fetchCredentials: RequestCredentials; dynamicProto(HttpManager, this, (_self) => { _initDefaults(); @@ -238,7 +239,9 @@ export class HttpManager { if (!isNullOrUndefined(channelConfig.useSendBeacon)) { _useBeacons = !!channelConfig.useSendBeacon; } - + if (channelConfig.fetchCredentials){ + _fetchCredentials= channelConfig.fetchCredentials; + } let sendPostConfig = _getSendPostMgrConfig(); // only init it once if (!_sendPostMgr) { @@ -411,7 +414,7 @@ export class HttpManager { } _self["_getDbgPlgTargets"] = () => { - return [_sendInterfaces[EventSendType.Batched], _killSwitch, _serializer, _sendInterfaces]; + return [_sendInterfaces[EventSendType.Batched], _killSwitch, _serializer, _sendInterfaces, _getSendPostMgrConfig()]; }; function _getSendPostMgrConfig(): _ISendPostMgrConfig { @@ -422,10 +425,12 @@ export class HttpManager { xhrOnComplete: _xhrOnComplete, beaconOnRetry: _onBeaconRetry } as _ISenderOnComplete; + let config = { enableSendPromise: false, isOneDs: true, disableCredentials: !_sendCredentials, + fetchCredentials: _fetchCredentials, disableXhr: false, disableBeacon: !_useBeacons, disableBeaconSync: !_useBeacons, diff --git a/channels/1ds-post-js/src/PostChannel.ts b/channels/1ds-post-js/src/PostChannel.ts index edc04a7d..f932b5a9 100644 --- a/channels/1ds-post-js/src/PostChannel.ts +++ b/channels/1ds-post-js/src/PostChannel.ts @@ -75,6 +75,7 @@ const defaultPostChannelConfig: IConfigDefaults = objDeep stringifyObjects: undefValue, enableCompoundKey: undefValue, disableOptimizeObj: false, + fetchCredentials: undefValue, // disableCacheHeader: undefValue, // See Task #7178858 - Collector requires a change to support this transports: undefValue, unloadTransports: undefValue, diff --git a/channels/1ds-post-js/test/Unit/src/HttpManagerTest.ts b/channels/1ds-post-js/test/Unit/src/HttpManagerTest.ts index 8f60ebe6..5358cbd1 100644 --- a/channels/1ds-post-js/test/Unit/src/HttpManagerTest.ts +++ b/channels/1ds-post-js/test/Unit/src/HttpManagerTest.ts @@ -1448,6 +1448,23 @@ export class HttpManagerTest extends AITestClass { } }); + this.testCase({ + name: "Fetch Credentials config could be set from post channel and pass into sendPostManager", + useFakeTimers: true, + test: () => { + var manager: HttpManager = new HttpManager(500, 2, 1, { + requeue: _requeueNotification, + send: _sendNotification, + sent: _sentNotification, + drop: _dropNotification + }); + this.core.config.extensionConfig![this.postManager.identifier].fetchCredentials = "omit"; + this.core.config.endpointUrl = "testEndpoint"; + this.core.config.extensionConfig![this.postManager.identifier].alwaysUseXhrOverride = false; + manager.initialize(this.core.config, this.core, this.postManager); + QUnit.assert.equal(manager["_getDbgPlgTargets"]()[4].fetchCredentials, "omit", "Fetch credentials should be set to omit"); + } + }); this.testCase({ name: "Validate synchronous event with zero response - Default Send Type (Synchronous)", useFakeTimers: true, diff --git a/channels/1ds-post-js/test/Unit/src/PostChannelTest.ts b/channels/1ds-post-js/test/Unit/src/PostChannelTest.ts index c16af912..cb20ac9d 100644 --- a/channels/1ds-post-js/test/Unit/src/PostChannelTest.ts +++ b/channels/1ds-post-js/test/Unit/src/PostChannelTest.ts @@ -167,6 +167,7 @@ export class PostChannelTest extends AITestClass { xhrTimeout: undefValue, disableXhrSync: undefValue, alwaysUseXhrOverride: false, + fetchCredentials: undefValue, maxEventRetryAttempts: 6, maxUnloadEventRetryAttempts: 2, addNoResponse: undefValue, @@ -197,6 +198,31 @@ export class PostChannelTest extends AITestClass { } }); + + this.testCase({ + name: "Fetch Credentials config default to be null, and support dynamic change", + useFakeTimers: true, + test: () => { + let config = this.config; + let core = this.core; + let postChannel = this.postChannel; + core.initialize(config, [postChannel]); + let actaulConfig = postChannel["_getDbgPlgTargets"]()[1]; + QUnit.assert.deepEqual(actaulConfig["fetchCredentials"], undefined, "fetchCredentials was undefined if not set"); + let httpManager = postChannel["_getDbgPlgTargets"]()[0]; + QUnit.assert.deepEqual(httpManager["_getDbgPlgTargets"]()[4].fetchCredentials, undefined, "fetchCredentials was undefined if not set"); + if (core.config.extensionConfig){ + core.config.extensionConfig[postChannel.identifier].fetchCredentials = "omit"; + } + this.clock.tick(1); + actaulConfig = postChannel["_getDbgPlgTargets"]()[1]; + QUnit.assert.deepEqual(actaulConfig["fetchCredentials"], "omit", "post channel fetchCredentials was set to omit"); + httpManager = postChannel["_getDbgPlgTargets"]()[0]; + console.log("get", JSON.stringify(httpManager["_getDbgPlgTargets"]()[4])); + QUnit.assert.deepEqual(httpManager["_getDbgPlgTargets"]()[4].fetchCredentials, "omit", "http manager fetchCredentials was set to omit"); + } + }); + this.testCase({ name: "Post Channel: dynamic config changes", useFakeTimers: true, diff --git a/channels/offline-channel-js/src/Sender.ts b/channels/offline-channel-js/src/Sender.ts index 742cc0f4..25f5dd72 100644 --- a/channels/offline-channel-js/src/Sender.ts +++ b/channels/offline-channel-js/src/Sender.ts @@ -42,6 +42,7 @@ export class Sender { let _isOneDs: boolean; let _sendPostMgr: SenderPostManager; let _disableCredentials: boolean; + let _fetchCredentials: RequestCredentials; dynamicProto(Sender, this, (_self, _base) => { @@ -83,14 +84,18 @@ export class Sender { utlSetStoragePrefix(config.storagePrefix); } let ctx = createProcessTelemetryContext(null, config, core); - + let offlineCfg = ctx.getExtCfg(DefaultOfflineIdentifier) as IOfflineChannelConfiguration; _onlineChannelId = channelId || BreezeChannelIdentifier; let senderConfig = ctx.getExtCfg(_onlineChannelId, {}) as any; let offlineSenderCfg = offlineCfg.senderCfg || {} as IOfflineSenderConfig; - + _fetchCredentials = null; if (_onlineChannelId == PostChannelId) { _isOneDs = true; + let channelConfig = ctx.getExtCfg(PostChannelId); + if (channelConfig && channelConfig["fetchCredentials"]) { + _fetchCredentials = channelConfig["fetchCredentials"]; + } } _alwaysUseCustomSend = offlineSenderCfg.alwaysUseXhrOverride; @@ -163,6 +168,7 @@ export class Sender { enableSendPromise: _enableSendPromise, isOneDs: _isOneDs, disableCredentials: _disableCredentials, + fetchCredentials: _fetchCredentials, senderOnCompleteCallBack: _getOnCompleteFuncs() } as _ISendPostMgrConfig; diff --git a/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/ISenderPostManager.ts b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/ISenderPostManager.ts index a67da03f..0e5ec0fc 100644 --- a/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/ISenderPostManager.ts +++ b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/ISenderPostManager.ts @@ -118,6 +118,23 @@ export interface _ISendPostMgrConfig { */ addNoResponse?: boolean; + /** + * [Optional] Specify whether cross-site Access-Control fetch requests should include credentials such as cookies, + * authentication headers, or TLS client certificates. + * + * Possible values: + * - "omit": never send credentials in the request or include credentials in the response. + * - "include": always include credentials, even cross-origin. + * - "same-origin": only send and include credentials for same-origin requests. + * + * If not set, the default value will be "include". + * + * For more information, refer to: + * - [Fetch API - Using Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#including_credentials) + * @since version after 3.3.1 + */ + fetchCredentials?: RequestCredentials; + } /** diff --git a/shared/AppInsightsCore/src/JavaScriptSDK/SenderPostManager.ts b/shared/AppInsightsCore/src/JavaScriptSDK/SenderPostManager.ts index a3b4465d..0f950857 100644 --- a/shared/AppInsightsCore/src/JavaScriptSDK/SenderPostManager.ts +++ b/shared/AppInsightsCore/src/JavaScriptSDK/SenderPostManager.ts @@ -46,6 +46,7 @@ export class SenderPostManager { let _isOneDs: boolean; let _onCompleteFuncs: _ISenderOnComplete; let _disableCredentials: boolean; + let _fetchCredentials: RequestCredentials; let _fallbackInst: IXHROverride; let _disableXhr: boolean; let _disableBeacon: boolean; @@ -83,6 +84,7 @@ export class SenderPostManager { try { _onCompleteFuncs = config.senderOnCompleteCallBack || {}; _disableCredentials = !!config.disableCredentials; + _fetchCredentials = config.fetchCredentials; _isOneDs = !!config.isOneDs; _enableSendPromise = !!config.enableSendPromise; _disableXhr = !! config.disableXhr; @@ -379,7 +381,9 @@ export class SenderPostManager { } - if (_sendCredentials && _isOneDs) { + if (_fetchCredentials) { // if user passed in this value via post channel (1ds), then use it + init.credentials = _fetchCredentials; + } else if (_sendCredentials && _isOneDs) { // for 1ds, Don't send credentials when URL is file:// init.credentials = "include"; } @@ -607,6 +611,7 @@ export class SenderPostManager { _isOneDs = null; _onCompleteFuncs = null; _disableCredentials = null; + _fetchCredentials = null; _fallbackInst = null; _disableXhr = false; _disableBeacon = false;