Skip to content

Commit

Permalink
Revert "Refactor FXIOS-7214 [v121] Remove unused FxA files (#16922)" (#…
Browse files Browse the repository at this point in the history
…16928)

This reverts commit c7eca68.
  • Loading branch information
thatswinnie authored Oct 19, 2023
1 parent c7eca68 commit 6897520
Show file tree
Hide file tree
Showing 9 changed files with 293 additions and 2 deletions.
189 changes: 189 additions & 0 deletions Account/SyncAuthState.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

import Foundation
import Common
import MozillaAppServices
import Shared
import SwiftyJSON

public let FxAClientErrorDomain = "org.mozilla.fxa.error"
public let FxAClientUnknownError = NSError(
domain: FxAClientErrorDomain,
code: 999,
userInfo: [NSLocalizedDescriptionKey: "Invalid server response"])

public struct FxAccountRemoteError {
static let AttemptToOperateOnAnUnverifiedAccount: Int32 = 104
static let InvalidAuthenticationToken: Int32 = 110
static let EndpointIsNoLongerSupported: Int32 = 116
static let IncorrectLoginMethodForThisAccount: Int32 = 117
static let IncorrectKeyRetrievalMethodForThisAccount: Int32 = 118
static let IncorrectAPIVersionForThisAccount: Int32 = 119
static let UnknownDevice: Int32 = 123
static let DeviceSessionConflict: Int32 = 124
static let UnknownError: Int32 = 999
}

public enum FxAClientError: Error, CustomStringConvertible {
case remote(RemoteError)
case local(NSError)

public var description: String {
switch self {
case .remote(let err): return "FxA remote error: \(err)"
case .local(let err): return "FxA local error: \(err)"
}
}
}

public struct RemoteError {
let code: Int32
let errno: Int32
let error: String?
let message: String?
let info: String?

var isUpgradeRequired: Bool {
return errno == FxAccountRemoteError.EndpointIsNoLongerSupported
|| errno == FxAccountRemoteError.IncorrectLoginMethodForThisAccount
|| errno == FxAccountRemoteError.IncorrectKeyRetrievalMethodForThisAccount
|| errno == FxAccountRemoteError.IncorrectAPIVersionForThisAccount
}

var isInvalidAuthentication: Bool {
return code == 401
}

var isUnverified: Bool {
return errno == FxAccountRemoteError.AttemptToOperateOnAnUnverifiedAccount
}
}

private let CurrentSyncAuthStateCacheVersion = 1

public struct SyncAuthStateCache {
let token: TokenServerToken
let forKey: Data
let expiresAt: Timestamp
}

public protocol SyncAuthState {
func invalidate()
func token(_ now: Timestamp, canBeExpired: Bool) -> Deferred<Maybe<(token: TokenServerToken, forKey: Data)>>
var enginesEnablements: [String: Bool]? { get set }
var clientName: String? { get set }
}

public func syncAuthStateCachefromJSON(_ json: JSON) -> SyncAuthStateCache? {
if let version = json["version"].int {
if version != CurrentSyncAuthStateCacheVersion {
DefaultLogger.shared.log("Sync Auth State Cache is wrong version; dropping.",
level: .warning,
category: .sync)
return nil
}
if let token = TokenServerToken.fromJSON(json["token"]),
let forKey = json["forKey"].string?.hexDecodedData,
let expiresAt = json["expiresAt"].int64 {
return SyncAuthStateCache(token: token, forKey: forKey, expiresAt: Timestamp(expiresAt))
}
}
return nil
}

extension SyncAuthStateCache: JSONLiteralConvertible {
public func asJSON() -> JSON {
return JSON([
"version": CurrentSyncAuthStateCacheVersion,
"token": token.asJSON(),
"forKey": forKey.hexEncodedString,
"expiresAt": NSNumber(value: expiresAt),
] as NSDictionary)
}
}

open class FirefoxAccountSyncAuthState: SyncAuthState {
private var logger: Logger
fileprivate let cache: KeychainCache<SyncAuthStateCache>
public var enginesEnablements: [String: Bool]?
public var clientName: String?

init(cache: KeychainCache<SyncAuthStateCache>,
logger: Logger = DefaultLogger.shared) {
self.cache = cache
self.logger = logger
}

// If a token gives you a 401, invalidate it and request a new one.
open func invalidate() {
logger.log("Invalidating cached token server token.",
level: .info,
category: .sync)
self.cache.value = nil
}

open func token(_ now: Timestamp, canBeExpired: Bool) -> Deferred<Maybe<(token: TokenServerToken, forKey: Data)>> {
if let value = cache.value {
// Give ourselves some room to do work.
let isExpired = value.expiresAt < now + 5 * OneMinuteInMilliseconds
if canBeExpired {
if isExpired {
logger.log("Returning cached expired token.",
level: .info,
category: .sync)
} else {
logger.log("Returning cached token, which should be valid.",
level: .info,
category: .sync)
}
return deferMaybe((token: value.token, forKey: value.forKey))
}

if !isExpired {
logger.log("Returning cached token, which should be valid.",
level: .info,
category: .sync)
return deferMaybe((token: value.token, forKey: value.forKey))
}
}

let deferred = Deferred<Maybe<(token: TokenServerToken, forKey: Data)>>()

RustFirefoxAccounts.shared.accountManager.uponQueue(.main) { accountManager in
accountManager.getTokenServerEndpointURL { result in
guard case .success(let tokenServerEndpointURL) = result else {
deferred.fill(Maybe(failure: FxAClientError.local(NSError())))
return
}

let client = TokenServerClient(url: tokenServerEndpointURL)
accountManager.getAccessToken(scope: OAuthScope.oldSync) { res in
switch res {
case .failure(let err):
deferred.fill(Maybe(failure: err as MaybeErrorType))
case .success(let accessToken):
self.logger.log("Fetching token server token.",
level: .debug,
category: .sync)
client.token(token: accessToken.token, kid: accessToken.key!.kid).upon { result in
guard let token = result.successValue else {
deferred.fill(Maybe(failure: result.failureValue!))
return
}
let kSync = Bytes.base64urlSafeDecodedData(accessToken.key!.k)!
let newCache = SyncAuthStateCache(token: token, forKey: kSync, expiresAt: now + 1000 * token.durationInSeconds)
self.logger.log("Fetched token server token! Token expires at \(newCache.expiresAt).",
level: .debug,
category: .sync)
self.cache.value = newCache
deferred.fill(Maybe(success: (token: token, forKey: kSync)))
}
}
}
}
}
return deferred
}
}
8 changes: 8 additions & 0 deletions Client.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@
2FCAE2781ABB531100877008 /* Visit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FCAE25C1ABB531100877008 /* Visit.swift */; };
2FCAE2841ABB533A00877008 /* MockFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FCAE2791ABB533A00877008 /* MockFiles.swift */; };
2FDB10931A9FBEC5006CF312 /* PrefsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FDB10921A9FBEC5006CF312 /* PrefsTests.swift */; };
2FDBCF611ABFC9DE00AFF7F0 /* SyncAuthState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FDBCF601ABFC9DE00AFF7F0 /* SyncAuthState.swift */; };
2FDE87FE1ABB3817005317B1 /* LegacyRemoteTabsPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FDE87FD1ABB3817005317B1 /* LegacyRemoteTabsPanel.swift */; };
318FB6EB1DB5600D0004E40F /* SQLiteHistoryFactories.swift in Sources */ = {isa = PBXBuildFile; fileRef = 318FB6EA1DB5600D0004E40F /* SQLiteHistoryFactories.swift */; };
31ADB5DA1E58CEC300E87909 /* ClipboardBarDisplayHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31ADB5D91E58CEC300E87909 /* ClipboardBarDisplayHandler.swift */; };
Expand Down Expand Up @@ -590,6 +591,7 @@
8A3EF7FD2A2FCFAC00796E3A /* AppReviewPromptSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3EF7FC2A2FCFAC00796E3A /* AppReviewPromptSetting.swift */; };
8A3EF7FF2A2FCFBB00796E3A /* ChangeToChinaSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3EF7FE2A2FCFBB00796E3A /* ChangeToChinaSetting.swift */; };
8A3EF8012A2FCFC900796E3A /* FasterInactiveTabs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3EF8002A2FCFC900796E3A /* FasterInactiveTabs.swift */; };
8A3EF8052A2FCFE500796E3A /* ForgetSyncAuthStateDebugSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3EF8042A2FCFE500796E3A /* ForgetSyncAuthStateDebugSetting.swift */; };
8A3EF8072A2FCFF700796E3A /* SentryIDSetting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3EF8062A2FCFF700796E3A /* SentryIDSetting.swift */; };
8A3EF8092A2FD02B00796E3A /* ExperimentsSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3EF8082A2FD02B00796E3A /* ExperimentsSettings.swift */; };
8A3EF80D2A2FD04D00796E3A /* ResetWallpaperOnboardingPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A3EF80C2A2FD04D00796E3A /* ResetWallpaperOnboardingPage.swift */; };
Expand Down Expand Up @@ -2379,6 +2381,7 @@
2FCAE33D1ABB5F1800877008 /* Storage-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Storage-Bridging-Header.h"; sourceTree = "<group>"; };
2FCF4713ABA14D85F70567AC /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/Today.strings; sourceTree = "<group>"; };
2FDB10921A9FBEC5006CF312 /* PrefsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrefsTests.swift; sourceTree = "<group>"; };
2FDBCF601ABFC9DE00AFF7F0 /* SyncAuthState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncAuthState.swift; sourceTree = "<group>"; };
2FDE46BCA5E27EF7DC02CE20 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/FindInPage.strings; sourceTree = "<group>"; };
2FDE87FD1ABB3817005317B1 /* LegacyRemoteTabsPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacyRemoteTabsPanel.swift; sourceTree = "<group>"; };
2FEBABAE1AB3659000DB5728 /* ResultTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResultTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4971,6 +4974,7 @@
8A3EF7FC2A2FCFAC00796E3A /* AppReviewPromptSetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReviewPromptSetting.swift; sourceTree = "<group>"; };
8A3EF7FE2A2FCFBB00796E3A /* ChangeToChinaSetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeToChinaSetting.swift; sourceTree = "<group>"; };
8A3EF8002A2FCFC900796E3A /* FasterInactiveTabs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FasterInactiveTabs.swift; sourceTree = "<group>"; };
8A3EF8042A2FCFE500796E3A /* ForgetSyncAuthStateDebugSetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForgetSyncAuthStateDebugSetting.swift; sourceTree = "<group>"; };
8A3EF8062A2FCFF700796E3A /* SentryIDSetting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryIDSetting.swift; sourceTree = "<group>"; };
8A3EF8082A2FD02B00796E3A /* ExperimentsSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExperimentsSettings.swift; sourceTree = "<group>"; };
8A3EF80C2A2FD04D00796E3A /* ResetWallpaperOnboardingPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetWallpaperOnboardingPage.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -7586,6 +7590,7 @@
2F14E1391ABB890800FF98DB /* Account-Bridging-Header.h */,
3905B4D41E8E7A6B0027D953 /* FxAPushMessageHandler.swift */,
2FA436271ABB8436008031D1 /* HawkHelper.swift */,
2FDBCF601ABFC9DE00AFF7F0 /* SyncAuthState.swift */,
2FA436281ABB8436008031D1 /* TokenServerClient.swift */,
2FA435FD1ABB83B4008031D1 /* Supporting Files */,
);
Expand Down Expand Up @@ -8349,6 +8354,7 @@
8A3EF8002A2FCFC900796E3A /* FasterInactiveTabs.swift */,
BCFF93F12AAF9688005B5B71 /* FirefoxSuggestSettings.swift */,
8A3EF7FA2A2FCF9D00796E3A /* ForceCrashSetting.swift */,
8A3EF8042A2FCFE500796E3A /* ForgetSyncAuthStateDebugSetting.swift */,
8A3EF7EF2A2FCF3100796E3A /* HiddenSettings.swift */,
8A3EF8142A2FD08800796E3A /* OpenFiftyTabsDebugOption.swift */,
8A3EF8122A2FD07A00796E3A /* ResetContextualHints.swift */,
Expand Down Expand Up @@ -12133,6 +12139,7 @@
EB07F860240D696000924860 /* PushNotificationSetup.swift in Sources */,
C8E2E80D23D20FB3005AACE6 /* RustFirefoxAccounts.swift in Sources */,
C8E2E80C23D20FB3005AACE6 /* Avatar.swift in Sources */,
2FDBCF611ABFC9DE00AFF7F0 /* SyncAuthState.swift in Sources */,
96666D0229969F7D00A4029F /* GeneralizedImageFetcher.swift in Sources */,
45355B272A269EAC00B1EA8E /* PushConfiguration.swift in Sources */,
2FA436351ABB8436008031D1 /* TokenServerClient.swift in Sources */,
Expand Down Expand Up @@ -12458,6 +12465,7 @@
C2D1A10D2A66C70000205DCC /* BookmarksCoordinator.swift in Sources */,
396E38F11EE0C8EC00CC180F /* FxAPushMessageHandler.swift in Sources */,
8A76B01629F6EB3900A82607 /* ScreenshotService.swift in Sources */,
8A3EF8052A2FCFE500796E3A /* ForgetSyncAuthStateDebugSetting.swift in Sources */,
E4CD9F6D1A77DD2800318571 /* ReaderModeStyleViewController.swift in Sources */,
E13E9AB52AAB0FB5001A0E9D /* FakespotViewModel.swift in Sources */,
8A5D1CBD2A30DC4E005AD35C /* AccountStatusSetting.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ class AppSettingsTableViewController: SettingsTableViewController,
ExportBrowserDataSetting(settings: self),
DeleteExportedDataSetting(settings: self),
ForceCrashSetting(settings: self),
ForgetSyncAuthStateDebugSetting(settings: self),
SwitchFakespotProduction(settings: self, settingsDelegate: self),
ChangeToChinaSetting(settings: self),
AppReviewPromptSetting(settings: self, settingsDelegate: self),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

import Foundation

class ForgetSyncAuthStateDebugSetting: HiddenSetting {
override var title: NSAttributedString? {
return NSAttributedString(
string: "Forget Sync auth state",
attributes: [NSAttributedString.Key.foregroundColor: theme.colors.textPrimary])
}

override func onClick(_ navigationController: UINavigationController?) {
settings.profile.rustFxA.syncAuthState.invalidate()
settings.tableView.reloadData()
}
}
15 changes: 15 additions & 0 deletions Client/Telemetry/TelemetryWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ class TelemetryWrapper: TelemetryWrapperProtocol, FeatureFlaggable {
// Save the profile so we can record settings from it when the notification below fires.
self.profile = profile

setSyncDeviceId()
SponsoredTileTelemetry.setupContextId()

// Register an observer to record settings and other metrics that are more appropriate to
Expand All @@ -195,6 +196,20 @@ class TelemetryWrapper: TelemetryWrapperProtocol, FeatureFlaggable {
)
}

// Sets hashed fxa sync device id for glean deletion ping
func setSyncDeviceId() {
// Grab our token so we can use the hashed_fxa_uid and clientGUID for deletion-request ping
guard let accountManager = RustFirefoxAccounts.shared.accountManager.peek(),
let state = accountManager.deviceConstellation()?.state(),
let clientGUID = state.localDevice?.id
else { return }

RustFirefoxAccounts.shared.syncAuthState.token(Date.now(), canBeExpired: true) >>== { (token, _) in
let deviceId = (clientGUID + token.hashedFxAUID).sha256.hexEncodedString
GleanMetrics.Deletion.syncDeviceId.set(deviceId)
}
}

@objc
func recordFinishedLaunchingPreferenceMetrics(notification: NSNotification) {
guard let profile = self.profile else { return }
Expand Down
21 changes: 21 additions & 0 deletions RustFxA/RustFirefoxAccounts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ open class RustFirefoxAccounts {
public var accountManager = Deferred<FxAccountManager>()
private static var isInitializingAccountManager = false
public var avatar: Avatar?
public let syncAuthState: SyncAuthState
fileprivate static var prefs: Prefs?
public let pushNotifications = PushNotificationSetup()
private let logger: Logger
Expand Down Expand Up @@ -81,6 +82,19 @@ open class RustFirefoxAccounts {
return RustFirefoxAccounts.shared.accountManager
}

private static let prefKeySyncAuthStateUniqueID = "PrefKeySyncAuthStateUniqueID"
private static func syncAuthStateUniqueId(prefs: Prefs?) -> String {
let id: String
let key = RustFirefoxAccounts.prefKeySyncAuthStateUniqueID
if let _id = prefs?.stringForKey(key) {
id = _id
} else {
id = UUID().uuidString
prefs?.setString(id, forKey: key)
}
return id
}

@discardableResult
public static func reconfig(prefs: Prefs) -> Deferred<FxAccountManager> {
if isInitializingAccountManager {
Expand Down Expand Up @@ -138,7 +152,12 @@ open class RustFirefoxAccounts {
// before any Application Services component gets used.
Viaduct.shared.useReqwestBackend()

let prefs = RustFirefoxAccounts.prefs
self.logger = logger
let cache = KeychainCache.fromBranch("rustAccounts.syncAuthState",
withLabel: RustFirefoxAccounts.syncAuthStateUniqueId(prefs: prefs),
factory: syncAuthStateCachefromJSON)
syncAuthState = FirefoxAccountSyncAuthState(cache: cache)

// Called when account is logged in for the first time, on every app start when the account is found (even if offline).
NotificationCenter.default.addObserver(forName: .accountAuthenticated, object: nil, queue: .main) { [weak self] notification in
Expand Down Expand Up @@ -207,8 +226,10 @@ open class RustFirefoxAccounts {
guard let accountManager = accountManager.peek() else { return }
accountManager.logout { _ in }
let prefs = RustFirefoxAccounts.prefs
prefs?.removeObjectForKey(RustFirefoxAccounts.prefKeySyncAuthStateUniqueID)
prefs?.removeObjectForKey(prefKeyCachedUserProfile)
prefs?.removeObjectForKey(PendingAccountDisconnectedKey)
self.syncAuthState.invalidate()
cachedUserProfile = nil
MZKeychainWrapper.sharedClientAppContainerKeychain.removeObject(forKey: KeychainKey.apnsToken, withAccessibility: .afterFirstUnlock)
}
Expand Down
Loading

0 comments on commit 6897520

Please sign in to comment.