Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NetP subscription integration #1710

Merged
merged 31 commits into from
Dec 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ac49552
WIP
samsymons Sep 30, 2023
32e9542
Remove the placeholder subscription items.
samsymons Oct 3, 2023
87751fc
Avoid letting users use the easter egg in subscription mode.
samsymons Oct 4, 2023
359e34e
Add a dedicated class for handling subscription events.
samsymons Oct 4, 2023
2c40160
Check if both access and auth tokens are present.
samsymons Oct 4, 2023
11b0b63
Replace local BSK version with branch.
samsymons Oct 4, 2023
8ea09ef
Change the build name.
samsymons Oct 4, 2023
d925b83
Merge branch 'develop' into sam/add-netp-subscription-auth-support
samsymons Oct 8, 2023
0d58e9f
Update branch version.
samsymons Oct 8, 2023
6925cf5
Fix SwiftLint violation.
samsymons Oct 8, 2023
94c6e39
Update compile-time checks.
samsymons Oct 8, 2023
0fa0010
Fix compilation again.
samsymons Oct 8, 2023
ca7d94c
Merge branch 'develop' into sam/add-netp-subscription-auth-support
samsymons Nov 27, 2023
6ae04ea
Fix compilation errors.
samsymons Nov 27, 2023
5ee8115
Fix Privacy Pro build.
samsymons Nov 27, 2023
d4aa6e9
Undo an xcconfig change.
samsymons Nov 27, 2023
0b1bf00
Simplify some token fetching logic.
samsymons Nov 27, 2023
accfbba
Fix App Store build compilation.
samsymons Nov 27, 2023
02accd0
Merge branch 'main' into sam/add-netp-subscription-auth-support
samsymons Dec 21, 2023
0a565ad
Update Package.resolved.
samsymons Dec 21, 2023
5e58575
Remove recovered references post-merge.
samsymons Dec 21, 2023
ff35a3d
Fix the project file after some merge issues.
samsymons Dec 21, 2023
b024422
Fix a compiler error with the Privacy Pro target.
samsymons Dec 21, 2023
c517b1c
Fix SwiftLint.
samsymons Dec 21, 2023
cb505ab
Reset the Privacy Pro scheme back to the main branch state.
samsymons Dec 21, 2023
52f025c
Merge branch 'main' into sam/add-netp-subscription-auth-support
samsymons Dec 24, 2023
04a16f3
Resolve post-merge issues.
samsymons Dec 24, 2023
c2ee464
Fix Privacy Pro build.
samsymons Dec 24, 2023
66c11b9
Fix the Privacy Pro build, attempt #2.
samsymons Dec 24, 2023
a4a6d0e
Rename token to accessToken.
samsymons Dec 25, 2023
dcb60d0
Set BSK to 100.0.0.
samsymons Dec 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,8 @@
4B2D067C2A13340900DE1F49 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4BEC322A11B509001D9AC5 /* Logging.swift */; };
4B2D067F2A1334D700DE1F49 /* NetworkProtectionUI in Frameworks */ = {isa = PBXBuildFile; productRef = 4B2D067E2A1334D700DE1F49 /* NetworkProtectionUI */; };
4B2E7D6326FF9D6500D2DB17 /* PrintingUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2E7D6226FF9D6500D2DB17 /* PrintingUserScript.swift */; };
4B2F565C2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2F565B2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift */; };
4B2F565D2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B2F565B2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift */; };
4B379C1527BD91E3008A968E /* QuartzIdleStateProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B379C1427BD91E3008A968E /* QuartzIdleStateProvider.swift */; };
4B379C1E27BDB7FF008A968E /* DeviceAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B379C1D27BDB7FF008A968E /* DeviceAuthenticator.swift */; };
4B379C2227BDBA29008A968E /* LocalAuthenticationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B379C2127BDBA29008A968E /* LocalAuthenticationService.swift */; };
Expand Down Expand Up @@ -3393,6 +3395,7 @@
4B2D06642A132F3A00DE1F49 /* NetworkProtectionAppExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NetworkProtectionAppExtension.entitlements; sourceTree = "<group>"; };
4B2D06692A13318400DE1F49 /* DuckDuckGo VPN App Store.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "DuckDuckGo VPN App Store.app"; sourceTree = BUILT_PRODUCTS_DIR; };
4B2E7D6226FF9D6500D2DB17 /* PrintingUserScript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrintingUserScript.swift; sourceTree = "<group>"; };
4B2F565B2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionSubscriptionEventHandler.swift; sourceTree = "<group>"; };
4B379C1427BD91E3008A968E /* QuartzIdleStateProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuartzIdleStateProvider.swift; sourceTree = "<group>"; };
4B379C1D27BDB7FF008A968E /* DeviceAuthenticator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceAuthenticator.swift; sourceTree = "<group>"; };
4B379C2127BDBA29008A968E /* LocalAuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAuthenticationService.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5213,6 +5216,7 @@
children = (
4BCF15D52ABB83D70083F6DF /* NetworkProtectionRemoteMessaging */,
7BA7CC4D2AD11F6F0042E5CE /* NetworkProtectionIPCTunnelController.swift */,
4B2F565B2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift */,
);
path = DeveloperIDTarget;
sourceTree = "<group>";
Expand Down Expand Up @@ -10830,6 +10834,7 @@
4B957B052AC7AE700062CA31 /* FindInPageModel.swift in Sources */,
4B957B062AC7AE700062CA31 /* PseudoFolder.swift in Sources */,
4B957B072AC7AE700062CA31 /* Visit.swift in Sources */,
4B2F565D2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift in Sources */,
4B957B082AC7AE700062CA31 /* PixelDataStore.swift in Sources */,
4B957B092AC7AE700062CA31 /* WaitlistStorage.swift in Sources */,
4B957B0A2AC7AE700062CA31 /* Pixel.swift in Sources */,
Expand Down Expand Up @@ -11243,6 +11248,7 @@
1DFAB51D2A8982A600A0F7F6 /* SetExtension.swift in Sources */,
315AA07028CA5CC800200030 /* YoutubePlayerNavigationHandler.swift in Sources */,
37AFCE9227DB8CAD00471A10 /* PreferencesAboutView.swift in Sources */,
4B2F565C2B38F93E001214C0 /* NetworkProtectionSubscriptionEventHandler.swift in Sources */,
9826B0A02747DF3D0092F683 /* ContentBlocking.swift in Sources */,
4B379C2227BDBA29008A968E /* LocalAuthenticationService.swift in Sources */,
37CEFCA92A6737A2001EF741 /* CredentialsCleanupErrorHandling.swift in Sources */,
Expand Down Expand Up @@ -13044,7 +13050,7 @@
repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 99.0.2;
version = 100.0.0;
};
};
AA06B6B52672AF8100F541C5 /* XCRemoteSwiftPackageReference "Sparkle" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/BrowserServicesKit",
"state" : {
"revision" : "18043cbc24e5bb79aa1f44e01a367e0bccd2fa6e",
"version" : "99.0.2"
"revision" : "c3a482a4ca22d706207d08a68db8f23f0c262040",
"version" : "100.0.0"
}
},
{
Expand Down
8 changes: 8 additions & 0 deletions DuckDuckGo/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate, FileDownloadManagerDel
private var emailCancellables = Set<AnyCancellable>()
let bookmarksManager = LocalBookmarkManager.shared

#if NETWORK_PROTECTION && SUBSCRIPTION
private let networkProtectionSubscriptionEventHandler = NetworkProtectionSubscriptionEventHandler()
#endif

#if DBP && SUBSCRIPTION
private let dataBrokerProtectionSubscriptionEventHandler = DataBrokerProtectionSubscriptionEventHandler()
#endif
Expand Down Expand Up @@ -247,6 +251,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate, FileDownloadManagerDel

UserDefaultsWrapper<Any>.clearRemovedKeys()

#if NETWORK_PROTECTION && SUBSCRIPTION
networkProtectionSubscriptionEventHandler.registerForSubscriptionAccountManagerEvents()
#endif

#if NETWORK_PROTECTION
NetworkProtectionAppEvents().applicationDidFinishLaunching()
UNUserNotificationCenter.current().delegate = self
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/Bookmarks/Services/BookmarkStoreMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public final class BookmarkStoreMock: BookmarkStore {
}

var moveObjectUUIDCalled = false
func move(objectUUIDs: [String], toIndex: Int?, withinParentFolder: DuckDuckGo_Privacy_Browser.ParentFolderType, completion: @escaping (Error?) -> Void) {
func move(objectUUIDs: [String], toIndex: Int?, withinParentFolder: ParentFolderType, completion: @escaping (Error?) -> Void) {
moveObjectUUIDCalled = true
}

Expand Down
4 changes: 4 additions & 0 deletions DuckDuckGo/MainWindow/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import Carbon.HIToolbox
import Combine
import Common

#if NETWORK_PROTECTION
import NetworkProtection
#endif

final class MainViewController: NSViewController {

private let tabBarContainerView = NSView()
Expand Down
12 changes: 0 additions & 12 deletions DuckDuckGo/NavigationBar/View/MoreOptionsMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -356,18 +356,6 @@ final class MoreOptionsMenu: NSMenu {

#endif // DBP

#if SUBSCRIPTION
let item1 = NSMenuItem(title: "Placeholder A", action: #selector(openPreferences(_:)), keyEquivalent: "")
.targetting(self)
.withImage(.image(for: .vpnIcon))
items.append(item1)

let item2 = NSMenuItem(title: "Placeholder B", action: #selector(openPreferences(_:)), keyEquivalent: "")
.targetting(self)
.withImage(.image(for: .vpnIcon))
items.append(item2)
#endif
samsymons marked this conversation as resolved.
Show resolved Hide resolved

return items
}

Expand Down
11 changes: 11 additions & 0 deletions DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import NetworkProtectionUI
#endif

#if SUBSCRIPTION
import Subscription
import SubscriptionUI
#endif

Expand Down Expand Up @@ -331,6 +332,16 @@ final class NavigationBarViewController: NSViewController {
return
}

#if SUBSCRIPTION
let accountManager = AccountManager()
let networkProtectionTokenStorage = NetworkProtectionKeychainTokenStore()

if accountManager.accessToken != nil && (try? networkProtectionTokenStorage.fetchToken()) == nil {
print("[NetP Subscription] Got access token but not auth token, meaning token exchange failed")
return
samsymons marked this conversation as resolved.
Show resolved Hide resolved
}
#endif

// 1. If the user is on the waitlist but hasn't been invited or accepted terms and conditions, show the waitlist screen.
// 2. If the user has no waitlist state but has an auth token, show the NetP popover.
// 3. If the user has no state of any kind, show the waitlist screen.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ extension EventMapping where Event == NetworkProtectionError {
.wireGuardDnsResolution,
.wireGuardSetNetworkSettings,
.startWireGuardBackend,
// Needs Privacy triage for macOS Geoswitching pixels
.failedToRetrieveAuthToken,
.failedToFetchLocationList,
.failedToParseLocationListResponse:
domainEvent = .networkProtectionUnhandledError(function: #function, line: #line, error: event)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// NetworkProtectionSubscriptionEventHandler.swift
//
// Copyright © 2023 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#if NETWORK_PROTECTION && SUBSCRIPTION

import Foundation
import Subscription
import NetworkProtection

final class NetworkProtectionSubscriptionEventHandler {
samsymons marked this conversation as resolved.
Show resolved Hide resolved

private let accountManager: AccountManaging
private let networkProtectionRedemptionCoordinator: NetworkProtectionCodeRedeeming
private let networkProtectionTokenStorage: NetworkProtectionTokenStore
private let networkProtectionFeatureDisabler: NetworkProtectionFeatureDisabling

init(accountManager: AccountManaging = AccountManager(),
networkProtectionRedemptionCoordinator: NetworkProtectionCodeRedeeming = NetworkProtectionCodeRedemptionCoordinator(),
networkProtectionTokenStorage: NetworkProtectionTokenStore = NetworkProtectionKeychainTokenStore(),
networkProtectionFeatureDisabler: NetworkProtectionFeatureDisabling = NetworkProtectionFeatureDisabler()) {
self.accountManager = accountManager
self.networkProtectionRedemptionCoordinator = networkProtectionRedemptionCoordinator
self.networkProtectionTokenStorage = networkProtectionTokenStorage
self.networkProtectionFeatureDisabler = networkProtectionFeatureDisabler
}

func registerForSubscriptionAccountManagerEvents() {
NotificationCenter.default.addObserver(self, selector: #selector(handleAccountDidSignIn), name: .accountDidSignIn, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleAccountDidSignOut), name: .accountDidSignOut, object: nil)
}

@objc private func handleAccountDidSignIn() {
guard let token = accountManager.accessToken else {
assertionFailure("[NetP Subscription] AccountManager signed in but token could not be retrieved")
return
}

Task {
do {
try await networkProtectionRedemptionCoordinator.exchange(accessToken: token)
print("[NetP Subscription] Exchanged access token for auth token successfully")
} catch {
print("[NetP Subscription] Failed to exchange access token for auth token: \(error)")
}
}
}

@objc private func handleAccountDidSignOut() {
print("[NetP Subscription] Deleted NetP auth token after signing out from Privacy Pro")

Task {
await networkProtectionFeatureDisabler.disable(keepAuthToken: false, uninstallSystemExtension: false)
}
}

}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ final class MacPacketTunnelProvider: PacketTunnelProvider {
domainEvent = .networkProtectionNoAuthTokenFoundError
case .unhandledError(function: let function, line: let line, error: let error):
domainEvent = .networkProtectionUnhandledError(function: function, line: line, error: error)
case .failedToFetchLocationList,
case .failedToRetrieveAuthToken,
.failedToFetchLocationList,
.failedToParseLocationListResponse:
// Needs Privacy triage for macOS Geoswitching pixels
return
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/Preferences/View/PreferencesAboutView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ extension Preferences {
Text(UserText.privacySimplified).font(.privacySimplified)

Text(UserText.versionLabel(version: model.appVersion.versionNumber, build: model.appVersion.buildNumber)).onTapGesture(count: 12) {
#if NETWORK_PROTECTION
#if NETWORK_PROTECTION && !SUBSCRIPTION
model.displayNetPInvite()
#endif
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ let nonSandboxedExtraInputFiles: Set<InputFile> = [
.init("NetworkProtectionAppEvents.swift", .source),
.init("NetworkProtectionIPCTunnelController.swift", .source),
.init("NetworkProtectionNavBarPopoverManager.swift", .source),
.init("NetworkProtectionSubscriptionEventHandler.swift", .source),
.init("KeychainType+ClientDefault.swift", .source),
.init("DBPHomeViewController.swift", .source),
.init("DataBrokerProtectionManager.swift", .source),
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/DataBrokerProtection/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ let package = Package(
targets: ["DataBrokerProtection"])
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
.package(path: "../PixelKit"),
.package(path: "../SwiftUIExtensions"),
.package(path: "../XPCHelper")
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/LoginItems/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let package = Package(
),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/NetworkProtectionMac/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ let package = Package(
.library(name: "NetworkProtectionUI", targets: ["NetworkProtectionUI"])
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
.package(path: "../XPCHelper"),
.package(path: "../SwiftUIExtensions")
],
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/PixelKit/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ let package = Package(
)
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/Subscription/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ let package = Package(
targets: ["Subscription"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/SwiftUIExtensions/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let package = Package(
),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/SyncUI/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let package = Package(
],
dependencies: [
.package(path: "../SwiftUIExtensions"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/SystemExtensionManager/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ let package = Package(
),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/XPCHelper/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ let package = Package(
.library(name: "XPCHelper", targets: ["XPCHelper"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "99.0.2"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "100.0.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
11 changes: 10 additions & 1 deletion UnitTests/Waitlist/Mocks/MockNetworkProtectionCodeRedeemer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ final class MockNetworkProtectionCodeRedeemer: NetworkProtectionCodeRedeeming {
case error
}

var redeemedCode: String?
var throwError: Bool = false

var redeemedCode: String?
func redeem(_ code: String) async throws {
if throwError {
throw MockNetworkProtectionCodeRedeemerError.error
Expand All @@ -38,6 +38,15 @@ final class MockNetworkProtectionCodeRedeemer: NetworkProtectionCodeRedeeming {
}
}

var redeemedAccessToken: String?
func exchange(accessToken: String) async throws {
if throwError {
throw MockNetworkProtectionCodeRedeemerError.error
} else {
redeemedAccessToken = accessToken
}
}

}

#endif
Loading