Files
firebase-ios-sdk/FirebaseFunctions/Sources/Internal/FunctionsContext.swift
T

98 lines
3.7 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2022 Google LLC
//
// 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.
@preconcurrency import FirebaseAppCheckInterop
@preconcurrency import FirebaseAuthInterop
@preconcurrency import FirebaseMessagingInterop
import Foundation
/// `FunctionsContext` is a helper object that holds metadata for a function call.
struct FunctionsContext {
let authToken: String?
let fcmToken: String?
let appCheckToken: String?
let limitedUseAppCheckToken: String?
}
struct FunctionsContextProvider: Sendable {
private let auth: AuthInterop?
private let messaging: MessagingInterop?
private let appCheck: AppCheckInterop?
init(auth: AuthInterop?, messaging: MessagingInterop?, appCheck: AppCheckInterop?) {
self.auth = auth
self.messaging = messaging
self.appCheck = appCheck
}
func context(options: HTTPSCallableOptions?) async throws -> FunctionsContext {
// Previously, this section used `async let`, but that was changed for a
// `Task`-based approach to work around a Swift 6.3 regression in Xcode 26.4.
// - Context: https://github.com/firebase/firebase-ios-sdk/issues/15974
let authToken = Task { try await auth?.getToken(forcingRefresh: false) }
let appCheckToken = Task { await getAppCheckToken(options: options) }
let limitedUseAppCheckToken = Task { await getLimitedUseAppCheckToken(options: options) }
return try await withTaskCancellationHandler {
defer {
authToken.cancel()
appCheckToken.cancel()
limitedUseAppCheckToken.cancel()
}
// Only `authToken` is throwing, but the formatter script removes the `try`
// from `try authToken` and puts it in front of the initializer call.
return try await FunctionsContext(
authToken: authToken.value,
fcmToken: messaging?.fcmToken,
appCheckToken: appCheckToken.value,
limitedUseAppCheckToken: limitedUseAppCheckToken.value
)
} onCancel: {
authToken.cancel()
appCheckToken.cancel()
limitedUseAppCheckToken.cancel()
}
}
private func getAppCheckToken(options: HTTPSCallableOptions?) async -> String? {
guard options?.requireLimitedUseAppCheckTokens != true, let appCheck else { return nil }
return await withCheckedContinuation { continuation in
appCheck.getToken(forcingRefresh: false) { result in
continuation.resume(returning: result.token)
}
}
}
private func getLimitedUseAppCheckToken(options: HTTPSCallableOptions?) async -> String? {
// At the moment, `await` doesn’t get along with Objective-C’s optional protocol methods.
await withCheckedContinuation { (continuation: CheckedContinuation<String?, Never>) in
guard
options?.requireLimitedUseAppCheckTokens == true,
let appCheck,
// `getLimitedUseToken(completion:)` is an optional protocol method. Optional binding
// is performed to make sure `continuation` is called even if the method’s not implemented.
let limitedUseTokenClosure = appCheck.getLimitedUseToken
else {
return continuation.resume(returning: nil)
}
limitedUseTokenClosure { tokenResult in
// The placeholder token should be used in the case of App Check error.
continuation.resume(returning: tokenResult.token)
}
}
}
}