Skip to content

[Auth] Remove AuthBackend singleton #14006

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

Merged
merged 13 commits into from
Nov 8, 2024
67 changes: 37 additions & 30 deletions FirebaseAuth/Sources/Swift/Auth/Auth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,12 @@ extension Auth: AuthInterop {
return
}
// Call back with current user token.
currentUser.internalGetToken(forceRefresh: forceRefresh) { token, error in
DispatchQueue.main.async {
callback(token, error)
currentUser
.internalGetToken(forceRefresh: forceRefresh, backend: strongSelf.backend) { token, error in
DispatchQueue.main.async {
callback(token, error)
}
}
}
}
}

Expand Down Expand Up @@ -292,7 +293,7 @@ extension Auth: AuthInterop {
requestConfiguration: self.requestConfiguration)
Task {
do {
let response = try await AuthBackend.call(with: request)
let response = try await self.backend.call(with: request)
Auth.wrapMainAsync(callback: completion, withParam: response.signinMethods, error: nil)
} catch {
Auth.wrapMainAsync(callback: completion, withParam: nil, error: error)
Expand Down Expand Up @@ -395,7 +396,7 @@ extension Auth: AuthInterop {
let response = try await injectRecaptcha(request: request,
action: AuthRecaptchaAction.signInWithPassword)
#else
let response = try await AuthBackend.call(with: request)
let response = try await backend.call(with: request)
#endif
return try await completeSignIn(
withAccessToken: response.idToken,
Expand Down Expand Up @@ -709,7 +710,7 @@ extension Auth: AuthInterop {
let request = SignUpNewUserRequest(requestConfiguration: self.requestConfiguration)
Task {
do {
let response = try await AuthBackend.call(with: request)
let response = try await self.backend.call(with: request)
let user = try await self.completeSignIn(
withAccessToken: response.idToken,
accessTokenExpirationDate: response.approximateExpirationDate,
Expand Down Expand Up @@ -771,7 +772,7 @@ extension Auth: AuthInterop {
requestConfiguration: self.requestConfiguration)
Task {
do {
let response = try await AuthBackend.call(with: request)
let response = try await self.backend.call(with: request)
let user = try await self.completeSignIn(
withAccessToken: response.idToken,
accessTokenExpirationDate: response.approximateExpirationDate,
Expand Down Expand Up @@ -881,7 +882,7 @@ extension Auth: AuthInterop {
if let inResponse {
response = inResponse
} else {
response = try await AuthBackend.call(with: request)
response = try await self.backend.call(with: request)
}
let user = try await self.completeSignIn(
withAccessToken: response.idToken,
Expand Down Expand Up @@ -993,7 +994,7 @@ extension Auth: AuthInterop {
requestConfiguration: self.requestConfiguration)
Task {
do {
let response = try await AuthBackend.call(with: request)
let response = try await self.backend.call(with: request)

let operation = ActionCodeInfo.actionCodeOperation(forRequestType: response.requestType)
guard let email = response.email else {
Expand Down Expand Up @@ -1433,7 +1434,7 @@ extension Auth: AuthInterop {
/// complete, or fails. Invoked asynchronously on the main thread in the future.
@objc open func revokeToken(withAuthorizationCode authorizationCode: String,
completion: ((Error?) -> Void)? = nil) {
currentUser?.internalGetToken { idToken, error in
currentUser?.internalGetToken(backend: backend) { idToken, error in
if let error {
Auth.wrapMainAsync(completion, error)
return
Expand Down Expand Up @@ -1613,7 +1614,9 @@ extension Auth: AuthInterop {

// MARK: Internal methods

init(app: FirebaseApp, keychainStorageProvider: AuthKeychainStorage = AuthKeychainStorageReal()) {
init(app: FirebaseApp,
keychainStorageProvider: AuthKeychainStorage = AuthKeychainStorageReal(),
backend: AuthBackend = AuthBackend(rpcIssuer: AuthBackendRPCIssuer())) {
Auth.setKeychainServiceNameForApp(app)
self.app = app
mainBundleUrlTypes = Bundle.main
Expand All @@ -1638,6 +1641,7 @@ extension Auth: AuthInterop {
auth: nil,
heartbeatLogger: app.heartbeatLogger,
appCheck: appCheck)
self.backend = backend
super.init()
requestConfiguration.auth = self

Expand Down Expand Up @@ -1911,17 +1915,18 @@ extension Auth: AuthInterop {
return
}
let uid = strongSelf.currentUser?.uid
strongSelf.currentUser?.internalGetToken(forceRefresh: true) { token, error in
if strongSelf.currentUser?.uid != uid {
return
}
if error != nil {
// Kicks off exponential back off logic to retry failed attempt. Starts with one minute
// delay (60 seconds) if this is the first failed attempt.
let rescheduleDelay = retry ? min(delay * 2, 16 * 60) : 60
strongSelf.scheduleAutoTokenRefresh(withDelay: rescheduleDelay, retry: true)
strongSelf.currentUser?
.internalGetToken(forceRefresh: true, backend: strongSelf.backend) { token, error in
if strongSelf.currentUser?.uid != uid {
return
}
if error != nil {
// Kicks off exponential back off logic to retry failed attempt. Starts with one minute
// delay (60 seconds) if this is the first failed attempt.
let rescheduleDelay = retry ? min(delay * 2, 16 * 60) : 60
strongSelf.scheduleAutoTokenRefresh(withDelay: rescheduleDelay, retry: true)
}
}
}
}
}

Expand Down Expand Up @@ -2075,7 +2080,7 @@ extension Auth: AuthInterop {
requestConfiguration: requestConfiguration)
request.autoCreate = !isReauthentication
credential.prepare(request)
let response = try await AuthBackend.call(with: request)
let response = try await backend.call(with: request)
if response.needConfirmation {
let email = response.email
let credential = OAuthCredential(withVerifyAssertionResponse: response)
Expand Down Expand Up @@ -2114,7 +2119,7 @@ extension Auth: AuthInterop {
phoneNumber: phoneNumber,
operation: operation,
requestConfiguration: requestConfiguration)
return try await AuthBackend.call(with: request)
return try await backend.call(with: request)
case let .verification(verificationID, code):
guard verificationID.count > 0 else {
throw AuthErrorUtils.missingVerificationIDError(message: nil)
Expand All @@ -2126,7 +2131,7 @@ extension Auth: AuthInterop {
verificationCode: code,
operation: operation,
requestConfiguration: requestConfiguration)
return try await AuthBackend.call(with: request)
return try await backend.call(with: request)
}
}
#endif
Expand All @@ -2152,7 +2157,7 @@ extension Auth: AuthInterop {
timestamp: credential.timestamp,
displayName: credential.displayName,
requestConfiguration: requestConfiguration)
let response = try await AuthBackend.call(with: request)
let response = try await backend.call(with: request)
let user = try await completeSignIn(withAccessToken: response.idToken,
accessTokenExpirationDate: response
.approximateExpirationDate,
Expand Down Expand Up @@ -2184,7 +2189,7 @@ extension Auth: AuthInterop {
let request = EmailLinkSignInRequest(email: email,
oobCode: actionCode,
requestConfiguration: requestConfiguration)
let response = try await AuthBackend.call(with: request)
let response = try await backend.call(with: request)
let user = try await completeSignIn(withAccessToken: response.idToken,
accessTokenExpirationDate: response
.approximateExpirationDate,
Expand Down Expand Up @@ -2242,7 +2247,7 @@ extension Auth: AuthInterop {
private func wrapAsyncRPCTask(_ request: any AuthRPCRequest, _ callback: ((Error?) -> Void)?) {
Task {
do {
let _ = try await AuthBackend.call(with: request)
let _ = try await self.backend.call(with: request)
Auth.wrapMainAsync(callback, nil)
} catch {
Auth.wrapMainAsync(callback, error)
Expand Down Expand Up @@ -2294,7 +2299,7 @@ extension Auth: AuthInterop {
action: action)
} else {
do {
return try await AuthBackend.call(with: request)
return try await backend.call(with: request)
} catch {
let nsError = error as NSError
if let underlyingError = nsError.userInfo[NSUnderlyingErrorKey] as? NSError,
Expand All @@ -2313,7 +2318,7 @@ extension Auth: AuthInterop {
}
}
}
return try await AuthBackend.call(with: request)
return try await backend.call(with: request)
}
#endif

Expand All @@ -2330,6 +2335,8 @@ extension Auth: AuthInterop {
/// Auth's backend.
var requestConfiguration: AuthRequestConfiguration

let backend: AuthBackend

#if os(iOS)

/// The manager for APNs tokens used by phone number auth.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ import Foundation
private func getHeadfulLiteUrl(eventID: String,
sessionID: String) async throws -> URL? {
let authDomain = try await AuthWebUtils
.fetchAuthDomain(withRequestConfiguration: auth.requestConfiguration)
.fetchAuthDomain(withRequestConfiguration: auth.requestConfiguration, backend: auth.backend)
let bundleID = Bundle.main.bundleIdentifier
let clientID = auth.app?.options.clientID
let appID = auth.app?.options.googleAppID
Expand Down
12 changes: 6 additions & 6 deletions FirebaseAuth/Sources/Swift/AuthProvider/PhoneAuthProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ import Foundation
.requestConfiguration)

do {
let response = try await AuthBackend.call(with: request)
let response = try await auth.backend.call(with: request)
return response.verificationID
} catch {
return try await handleVerifyErrorWithRetry(error: error,
Expand Down Expand Up @@ -245,7 +245,7 @@ import Foundation
requestConfiguration: auth.requestConfiguration
)

let response = try await AuthBackend.call(with: request)
let response = try await auth.backend.call(with: request)
return response.verificationID
}
guard let session else {
Expand All @@ -263,15 +263,15 @@ import Foundation
let request = StartMFAEnrollmentRequest(idToken: idToken,
enrollmentInfo: startMFARequestInfo,
requestConfiguration: auth.requestConfiguration)
let response = try await AuthBackend.call(with: request)
let response = try await auth.backend.call(with: request)
return response.phoneSessionInfo?.sessionInfo
} else {
let request = StartMFASignInRequest(MFAPendingCredential: session.mfaPendingCredential,
MFAEnrollmentID: session.multiFactorInfo?.uid,
signInInfo: startMFARequestInfo,
requestConfiguration: auth.requestConfiguration)

let response = try await AuthBackend.call(with: request)
let response = try await auth.backend.call(with: request)
return response.responseInfo?.sessionInfo
}
} catch {
Expand Down Expand Up @@ -328,7 +328,7 @@ import Foundation
isSandbox: token.type == AuthAPNSTokenType.sandbox,
requestConfiguration: auth.requestConfiguration)
do {
let verifyResponse = try await AuthBackend.call(with: request)
let verifyResponse = try await auth.backend.call(with: request)
guard let receipt = verifyResponse.receipt,
let timeout = verifyResponse.suggestedTimeOutDate?.timeIntervalSinceNow else {
fatalError("Internal Auth Error: invalid VerifyClientResponse")
Expand Down Expand Up @@ -436,7 +436,7 @@ import Foundation
/// - Parameter eventID: The event ID used for this purpose.
private func reCAPTCHAURL(withEventID eventID: String) async throws -> URL? {
let authDomain = try await AuthWebUtils
.fetchAuthDomain(withRequestConfiguration: auth.requestConfiguration)
.fetchAuthDomain(withRequestConfiguration: auth.requestConfiguration, backend: auth.backend)
let bundleID = Bundle.main.bundleIdentifier
let clientID = auth.app?.options.clientID
let appID = auth.app?.options.googleAppID
Expand Down
16 changes: 1 addition & 15 deletions FirebaseAuth/Sources/Swift/Backend/AuthBackend.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,7 @@ class AuthBackend: AuthBackendProtocol {
return "FirebaseAuth.iOS/\(FirebaseVersion()) \(GTMFetcherStandardUserAgentString(nil))"
}

static func call<T: AuthRPCRequest>(with request: T) async throws -> T.Response {
return try await shared.call(with: request)
}

static func setTestRPCIssuer(issuer: AuthBackendRPCIssuer) {
shared.rpcIssuer = issuer
}

static func resetRPCIssuer() {
shared.rpcIssuer = AuthBackendRPCIssuer()
}

private static let shared: AuthBackend = .init(rpcIssuer: AuthBackendRPCIssuer())

private var rpcIssuer: any AuthBackendRPCIssuerProtocol
private let rpcIssuer: any AuthBackendRPCIssuerProtocol

init(rpcIssuer: any AuthBackendRPCIssuerProtocol) {
self.rpcIssuer = rpcIssuer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ private var gAPIHost = "securetoken.googleapis.com"

/// Represents the parameters for the token endpoint.
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
struct SecureTokenRequest: AuthRPCRequest {
class SecureTokenRequest: AuthRPCRequest {
typealias Response = SecureTokenResponse

/// The type of grant requested.
Expand Down
6 changes: 3 additions & 3 deletions FirebaseAuth/Sources/Swift/MultiFactor/MultiFactor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ import Foundation
.requestConfiguration)
Task {
do {
let response = try await AuthBackend.call(with: request)
let response = try await auth.backend.call(with: request)
do {
let user = try await auth.completeSignIn(withAccessToken: response.idToken,
accessTokenExpirationDate: nil,
Expand Down Expand Up @@ -139,7 +139,7 @@ import Foundation

Task {
do {
let response = try await AuthBackend.call(with: request)
let response = try await auth.backend.call(with: request)
do {
let user = try await auth.completeSignIn(withAccessToken: response.idToken,
accessTokenExpirationDate: nil,
Expand Down Expand Up @@ -217,7 +217,7 @@ import Foundation
requestConfiguration: user.requestConfiguration)
Task {
do {
let response = try await AuthBackend.call(with: request)
let response = try await auth.backend.call(with: request)
do {
let user = try await auth.completeSignIn(withAccessToken: response.idToken,
accessTokenExpirationDate: nil,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ import Foundation
)
Task {
do {
let response = try await AuthBackend.call(with: request)
let response = try await self.auth.backend.call(with: request)
let user = try await self.auth.completeSignIn(withAccessToken: response.idToken,
accessTokenExpirationDate: nil,
refreshToken: response.refreshToken,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ import Foundation
@objc(generateSecretWithMultiFactorSession:completion:)
open class func generateSecret(with session: MultiFactorSession,
completion: @escaping (TOTPSecret?, Error?) -> Void) {
guard let currentUser = session.currentUser,
let requestConfiguration = currentUser.auth?.requestConfiguration else {
guard let currentUser = session.currentUser, let auth = currentUser.auth else {
let error = AuthErrorUtils.error(code: AuthErrorCode.internalError,
userInfo: [NSLocalizedDescriptionKey:
"Invalid ID token."])
Expand All @@ -42,10 +41,10 @@ import Foundation
let totpEnrollmentInfo = AuthProtoStartMFATOTPEnrollmentRequestInfo()
let request = StartMFAEnrollmentRequest(idToken: session.idToken,
totpEnrollmentInfo: totpEnrollmentInfo,
requestConfiguration: requestConfiguration)
requestConfiguration: auth.requestConfiguration)
Task {
do {
let response = try await AuthBackend.call(with: request)
let response = try await auth.backend.call(with: request)
if let totpSessionInfo = response.totpSessionInfo {
let secret = TOTPSecret(secretKey: totpSessionInfo.sharedSecretKey,
hashingAlgorithm: totpSessionInfo.hashingAlgorithm,
Expand Down
Loading
Loading