From cc7929eac26258a8d0127c9036be76a1b945424d Mon Sep 17 00:00:00 2001 From: Michal Smaga Date: Fri, 1 Nov 2024 13:31:06 +0100 Subject: [PATCH] Update to subscription cookie (#3512) Task/Issue URL: https://app.asana.com/0/1108686900785972/1208264562025859/f **Description**: Initial PR -> https://github.com/duckduckgo/iOS/pull/3488 Update to how the subscription cookie operates: - constraint the cookie to `subscriptions.duckduckgo.com` - on sign out do not fully remove the cookie - just clear the value - gate the feature behind the `setAccessTokenCookieForSubscriptionDomains` privacy config feature flag --- Core/PixelEvent.swift | 8 +-- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- DuckDuckGo/AppDelegate.swift | 56 +++++++++++++++---- ...riptionCookieManageEventPixelMapping.swift | 10 ++-- 5 files changed, 55 insertions(+), 25 deletions(-) diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 0a021242ea..4849cb9921 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -710,8 +710,8 @@ extension Pixel { case privacyProKeychainAccessError case privacyProSubscriptionCookieMissingTokenOnSignIn case privacyProSubscriptionCookieMissingCookieOnSignOut - case privacyProSubscriptionCookieRefreshedWithUpdate - case privacyProSubscriptionCookieRefreshedWithDelete + case privacyProSubscriptionCookieRefreshedWithAccessToken + case privacyProSubscriptionCookieRefreshedWithEmptyValue case privacyProSubscriptionCookieFailedToSetSubscriptionCookie // MARK: Pixel Experiment @@ -1527,8 +1527,8 @@ extension Pixel.Event { case .privacyProKeychainAccessError: return "m_privacy-pro_keychain_access_error" case .privacyProSubscriptionCookieMissingTokenOnSignIn: return "m_privacy-pro_subscription-cookie-missing_token_on_sign_in" case .privacyProSubscriptionCookieMissingCookieOnSignOut: return "m_privacy-pro_subscription-cookie-missing_cookie_on_sign_out" - case .privacyProSubscriptionCookieRefreshedWithUpdate: return "m_privacy-pro_subscription-cookie-refreshed_with_update" - case .privacyProSubscriptionCookieRefreshedWithDelete: return "m_privacy-pro_subscription-cookie-refreshed_with_delete" + case .privacyProSubscriptionCookieRefreshedWithAccessToken: return "m_privacy-pro_subscription-cookie-refreshed_with_access_token" + case .privacyProSubscriptionCookieRefreshedWithEmptyValue: return "m_privacy-pro_subscription-cookie-refreshed_with_empty_value" case .privacyProSubscriptionCookieFailedToSetSubscriptionCookie: return "m_privacy-pro_subscription-cookie-failed_to_set_subscription_cookie" // MARK: Pixel Experiment diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 1e414812e4..8251c8968f 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -11010,7 +11010,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = exactVersion; - version = 201.0.0; + version = "201.0.0-1"; }; }; 9F8FE9472BAE50E50071E372 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 21aa8e0ac9..7541e05369 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "e5946eee6af859690cc1cc5e51daef3c8368981b", - "version" : "201.0.0" + "revision" : "9506581ae99273681073f9993fc6d881d3edaa7f", + "version" : "201.0.0-1" } }, { diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index d5f34b1a3c..b5448d7b67 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -89,6 +89,7 @@ import os.log private(set) var subscriptionFeatureAvailability: SubscriptionFeatureAvailability! private var subscriptionCookieManager: SubscriptionCookieManaging! + private var subscriptionCookieManagerFeatureFlagCancellable: AnyCancellable? var privacyProDataReporter: PrivacyProDataReporting! // MARK: - Feature specific app event handlers @@ -123,7 +124,7 @@ import os.log } } - // swiftlint:disable:next function_body_length cyclomatic_complexity + // swiftlint:disable:next function_body_length func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { #if targetEnvironment(simulator) @@ -313,17 +314,8 @@ import os.log subscriptionFeatureAvailability = DefaultSubscriptionFeatureAvailability( privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, purchasePlatform: .appStore) - - subscriptionCookieManager = SubscriptionCookieManager(subscriptionManager: AppDependencyProvider.shared.subscriptionManager, - currentCookieStore: { [weak self] in - guard self?.mainViewController?.tabManager.model.hasActiveTabs ?? false else { - // We shouldn't interact with WebKit's cookie store unless we have a WebView, - // eventually the subscription cookie will be refreshed on opening the first tab - return nil - } - - return WKWebsiteDataStore.current().httpCookieStore - }, eventMapping: SubscriptionCookieManageEventPixelMapping()) + + subscriptionCookieManager = makeSubscriptionCookieManager() homePageConfiguration = HomePageConfiguration(variantManager: AppDependencyProvider.shared.variantManager, remoteMessagingClient: remoteMessagingClient, @@ -416,6 +408,46 @@ import os.log return true } + private func makeSubscriptionCookieManager() -> SubscriptionCookieManaging { + let subscriptionCookieManager = SubscriptionCookieManager(subscriptionManager: AppDependencyProvider.shared.subscriptionManager, + currentCookieStore: { [weak self] in + guard self?.mainViewController?.tabManager.model.hasActiveTabs ?? false else { + // We shouldn't interact with WebKit's cookie store unless we have a WebView, + // eventually the subscription cookie will be refreshed on opening the first tab + return nil + } + + return WKWebsiteDataStore.current().httpCookieStore + }, eventMapping: SubscriptionCookieManageEventPixelMapping()) + + + let privacyConfigurationManager = ContentBlocking.shared.privacyConfigurationManager + + // Enable subscriptionCookieManager if feature flag is present + if privacyConfigurationManager.privacyConfig.isSubfeatureEnabled(PrivacyProSubfeature.setAccessTokenCookieForSubscriptionDomains) { + subscriptionCookieManager.enableSettingSubscriptionCookie() + } + + // Keep track of feature flag changes + subscriptionCookieManagerFeatureFlagCancellable = privacyConfigurationManager.updatesPublisher + .sink { [weak self, weak privacyConfigurationManager] in + guard let self, let privacyConfigurationManager else { return } + + let isEnabled = privacyConfigurationManager.privacyConfig.isSubfeatureEnabled(PrivacyProSubfeature.setAccessTokenCookieForSubscriptionDomains) + + Task { [weak self] in + if isEnabled { + self?.subscriptionCookieManager.enableSettingSubscriptionCookie() + await self?.subscriptionCookieManager.refreshSubscriptionCookie() + } else { + await self?.subscriptionCookieManager.disableSettingSubscriptionCookie() + } + } + } + + return subscriptionCookieManager + } + private func makeHistoryManager() -> HistoryManaging { let provider = AppDependencyProvider.shared diff --git a/DuckDuckGo/Subscription/SubscriptionCookieManageEventPixelMapping.swift b/DuckDuckGo/Subscription/SubscriptionCookieManageEventPixelMapping.swift index 19efdba9dd..0ec0ee2ef9 100644 --- a/DuckDuckGo/Subscription/SubscriptionCookieManageEventPixelMapping.swift +++ b/DuckDuckGo/Subscription/SubscriptionCookieManageEventPixelMapping.swift @@ -30,12 +30,10 @@ public final class SubscriptionCookieManageEventPixelMapping: EventMapping