diff --git a/FirebaseAppCheck.podspec b/FirebaseAppCheck.podspec index b47e53b176c..a40eb5ef19d 100644 --- a/FirebaseAppCheck.podspec +++ b/FirebaseAppCheck.podspec @@ -45,7 +45,7 @@ Pod::Spec.new do |s| s.tvos.weak_framework = 'DeviceCheck' s.dependency 'AppCheckCore', '~> 10.19' - s.dependency 'FirebaseAppCheckInterop', '~> 10.17' + s.dependency 'FirebaseAppCheckInterop', '~> 10.28' s.dependency 'FirebaseCore', '~> 10.18' s.dependency 'PromisesObjC', '~> 2.1' s.dependency 'GoogleUtilities/Environment', '~> 7.13' diff --git a/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FIRAppCheckInterop.h b/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FIRAppCheckInterop.h index 11ade09985c..7d89f7f7495 100644 --- a/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FIRAppCheckInterop.h +++ b/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FIRAppCheckInterop.h @@ -23,7 +23,7 @@ NS_ASSUME_NONNULL_BEGIN NS_SWIFT_NAME(AppCheckTokenHandlerInterop) typedef void (^FIRAppCheckTokenHandlerInterop)(id tokenResult); -NS_SWIFT_NAME(AppCheckInterop) @protocol FIRAppCheckInterop +NS_SWIFT_NAME(AppCheckInterop) @protocol FIRAppCheckInterop /// Retrieve a cached or generate a new FAA Token. If forcingRefresh == YES always generates a new /// token and updates the cache. diff --git a/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FIRAppCheckProtocol.h b/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FIRAppCheckProtocol.h index 52d19fb7fc3..006dc6e1144 100644 --- a/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FIRAppCheckProtocol.h +++ b/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FIRAppCheckProtocol.h @@ -16,7 +16,7 @@ #import -@class FIRAppCheckToken; +@protocol FIRAppCheckTokenProtocol; NS_ASSUME_NONNULL_BEGIN @@ -38,8 +38,8 @@ NS_SWIFT_NAME(AppCheckProtocol) /// @param handler The completion handler. Includes the app check token if the request succeeds, /// or an error if the request fails. - (void)tokenForcingRefresh:(BOOL)forcingRefresh - completion: - (void (^)(FIRAppCheckToken *_Nullable token, NSError *_Nullable error))handler + completion:(void (^)(id _Nullable token, + NSError *_Nullable error))handler NS_SWIFT_NAME(token(forcingRefresh:completion:)); /// Requests a limited-use Firebase App Check token. This method should be used only if you need to @@ -50,7 +50,7 @@ NS_SWIFT_NAME(AppCheckProtocol) /// Protection](https://firebase.google.com/docs/app-check/custom-resource-backend#replay-protection). /// This method does not affect the token generation behavior of the /// ``tokenForcingRefresh()`` method. -- (void)limitedUseTokenWithCompletion:(void (^)(FIRAppCheckToken *_Nullable token, +- (void)limitedUseTokenWithCompletion:(void (^)(id _Nullable token, NSError *_Nullable error))handler; @end diff --git a/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FIRAppCheckTokenProtocol.h b/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FIRAppCheckTokenProtocol.h new file mode 100644 index 00000000000..99af6457648 --- /dev/null +++ b/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FIRAppCheckTokenProtocol.h @@ -0,0 +1,28 @@ +/* + * Copyright 2024 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. + */ +NS_ASSUME_NONNULL_BEGIN + +NS_SWIFT_NAME(AppCheckTokenProtocol) +@protocol FIRAppCheckTokenProtocol + +/// A Firebase App Check token. +@property(nonatomic, readonly) NSString *token; + +/// The App Check token's expiration date in the device's local time. +@property(nonatomic, readonly) NSDate *expirationDate; + +@end +NS_ASSUME_NONNULL_END diff --git a/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FirebaseAppCheckInterop.h b/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FirebaseAppCheckInterop.h index afe5f651d9c..f7004109906 100644 --- a/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FirebaseAppCheckInterop.h +++ b/FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/FirebaseAppCheckInterop.h @@ -16,4 +16,5 @@ #import "FIRAppCheckInterop.h" #import "FIRAppCheckProtocol.h" +#import "FIRAppCheckTokenProtocol.h" #import "FIRAppCheckTokenResultInterop.h" diff --git a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheck.h b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheck.h index 4005ee35b57..39e6b0f05e1 100644 --- a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheck.h +++ b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheck.h @@ -74,6 +74,36 @@ NS_SWIFT_NAME(AppCheck) /// set explicitly, the value will be persisted and used as a default value on next app launches. @property(nonatomic, assign) BOOL isTokenAutoRefreshEnabled; +/// Requests Firebase app check token. This method should *only* be used if you need to authorize +/// requests to a non-Firebase backend. Requests to Firebase backend are authorized automatically if +/// configured. +/// +/// If your non-Firebase backend exposes sensitive or expensive endpoints that have low traffic +/// volume, consider protecting it with [Replay +/// Protection](https://firebase.google.com/docs/app-check/custom-resource-backend#replay-protection). +/// In this case, use the ``limitedUseToken(completion:)`` instead to obtain a limited-use token. +/// @param forcingRefresh If `YES`, a new Firebase app check token is requested and the token +/// cache is ignored. If `NO`, the cached token is used if it exists and has not expired yet. In +/// most cases, `NO` should be used. `YES` should only be used if the server explicitly returns an +/// error, indicating a revoked token. +/// @param handler The completion handler. Includes the app check token if the request succeeds, +/// or an error if the request fails. +- (void)tokenForcingRefresh:(BOOL)forcingRefresh + completion: + (void (^)(FIRAppCheckToken *_Nullable token, NSError *_Nullable error))handler + NS_SWIFT_NAME(token(forcingRefresh:completion:)); + +/// Requests a limited-use Firebase App Check token. This method should be used only if you need to +/// authorize requests to a non-Firebase backend. +/// +/// Returns limited-use tokens that are intended for use with your non-Firebase backend endpoints +/// that are protected with [Replay +/// Protection](https://firebase.google.com/docs/app-check/custom-resource-backend#replay-protection). +/// This method does not affect the token generation behavior of the +/// ``tokenForcingRefresh()`` method. +- (void)limitedUseTokenWithCompletion:(void (^)(FIRAppCheckToken *_Nullable token, + NSError *_Nullable error))handler; + @end NS_ASSUME_NONNULL_END diff --git a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckToken.h b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckToken.h index 368b900ae06..50d3188e4b2 100644 --- a/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckToken.h +++ b/FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckToken.h @@ -16,11 +16,13 @@ #import +#import + NS_ASSUME_NONNULL_BEGIN /// An object representing a Firebase App Check token. NS_SWIFT_NAME(AppCheckToken) -@interface FIRAppCheckToken : NSObject +@interface FIRAppCheckToken : NSObject /// A Firebase App Check token. @property(nonatomic, readonly) NSString *token; diff --git a/FirebaseAppCheck/Tests/Interop/ObjC/FIRAppCheckInteropAPITests.m b/FirebaseAppCheck/Tests/Interop/ObjC/FIRAppCheckInteropAPITests.m new file mode 100644 index 00000000000..05318c79d2a --- /dev/null +++ b/FirebaseAppCheck/Tests/Interop/ObjC/FIRAppCheckInteropAPITests.m @@ -0,0 +1,53 @@ +// Copyright 2024 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. + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRAppCheckInteropAPITests : NSObject +@end + +@implementation FIRAppCheckInteropAPITests + +- (void)usage { + id appCheckInterop; + + [appCheckInterop getTokenForcingRefresh:NO + completion:^(id tokenResult) { + NSString *__unused token = tokenResult.token; + NSError *__unused _Nullable error = tokenResult.error; + }]; + + NSString *__unused tokenDidChangeNotificationName = + [appCheckInterop tokenDidChangeNotificationName]; + + NSString *__unused notificationTokenKey = [appCheckInterop notificationTokenKey]; + + NSString *__unused notificationAppNameKey = [appCheckInterop notificationAppNameKey]; + + if ([appCheckInterop respondsToSelector:@selector(getLimitedUseTokenWithCompletion:)]) { + [appCheckInterop + getLimitedUseTokenWithCompletion:^(id tokenResult) { + NSString *__unused token = tokenResult.token; + NSError *__unused _Nullable error = tokenResult.error; + }]; + } +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/FirebaseAppCheck/Tests/Interop/ObjC/FIRAppCheckProtocolAPITests.m b/FirebaseAppCheck/Tests/Interop/ObjC/FIRAppCheckProtocolAPITests.m new file mode 100644 index 00000000000..4aad9bcb3c2 --- /dev/null +++ b/FirebaseAppCheck/Tests/Interop/ObjC/FIRAppCheckProtocolAPITests.m @@ -0,0 +1,51 @@ +// Copyright 2024 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. + +#import + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface FIRAppCheckProtocolAPITests : NSObject +@end + +@implementation FIRAppCheckProtocolAPITests + +- (void)usage { + id appCheck; + + [appCheck tokenForcingRefresh:NO + completion:^(id _Nullable token, + NSError *_Nullable error) { + if (token) { + NSString *__unused tokenValue = token.token; + NSDate *__unused expirationDate = token.expirationDate; + } + }]; + + if ([appCheck respondsToSelector:@selector(limitedUseTokenWithCompletion:)]) { + [appCheck limitedUseTokenWithCompletion:^(id _Nullable token, + NSError *_Nullable error) { + if (token) { + NSString *__unused tokenValue = token.token; + NSDate *__unused expirationDate = token.expirationDate; + } + }]; + } +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/FirebaseAppCheck/Tests/Interop/Swift/AppCheckInteropAPITests.swift b/FirebaseAppCheck/Tests/Interop/Swift/AppCheckInteropAPITests.swift new file mode 100644 index 00000000000..6240f243f22 --- /dev/null +++ b/FirebaseAppCheck/Tests/Interop/Swift/AppCheckInteropAPITests.swift @@ -0,0 +1,64 @@ +// Copyright 2024 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. + +// MARK: This file is used to evaluate the AppCheckInterop API in Swift. + +import Foundation + +// MARK: Do not import `FirebaseAppCheck`, this file is for `FirebaseAppCheckInterop` only. + +import FirebaseAppCheckInterop + +final class AppCheckInteropAPITests { + let appCheckInterop: AppCheckInterop! = nil + + func usage() { + let _: Void = appCheckInterop.getToken(forcingRefresh: false) { result in + let _: FIRAppCheckTokenResultInterop = result + let _: String = result.token + if let error = result.error { + let _: String = error.localizedDescription + } + } + + let _: String = appCheckInterop.tokenDidChangeNotificationName() + + let _: String = appCheckInterop.notificationTokenKey() + + let _: String = appCheckInterop.notificationAppNameKey() + + guard let getLimitedUseToken: (@escaping AppCheckTokenHandlerInterop) -> Void = + appCheckInterop.getLimitedUseToken else { return } + let _: Void = getLimitedUseToken { result in + let _: FIRAppCheckTokenResultInterop = result + let _: String = result.token + if let error = result.error { + let _: String = error.localizedDescription + } + } + } + + @available(iOS 13, macOS 10.15, macCatalyst 13, tvOS 13, *) + func usage_async() async { + let result: FIRAppCheckTokenResultInterop = + await appCheckInterop.getToken(forcingRefresh: false) + let _: String = result.token + if let error = result.error { + let _: String = error.localizedDescription + } + + // The following fails to compile with "Command SwiftCompile failed with a nonzero exit code". + // let _ = await appCheckInterop.getLimitedUseToken?() + } +} diff --git a/FirebaseAppCheck/Tests/Interop/Swift/AppCheckProtocolAPITests.swift b/FirebaseAppCheck/Tests/Interop/Swift/AppCheckProtocolAPITests.swift new file mode 100644 index 00000000000..b013100dd70 --- /dev/null +++ b/FirebaseAppCheck/Tests/Interop/Swift/AppCheckProtocolAPITests.swift @@ -0,0 +1,58 @@ +// Copyright 2024 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. + +// MARK: This file is used to evaluate the AppCheckProtocol API in Swift. + +import Foundation + +// MARK: Do not import `FirebaseAppCheck`, this file is for `FirebaseAppCheckInterop` only. + +import FirebaseAppCheckInterop + +final class AppCheckProtocolAPITests { + let appCheck: AppCheckProtocol! = nil + + func usage() { + let _: Void = appCheck.token(forcingRefresh: false, completion: { token, error in + if let token: AppCheckTokenProtocol { + let _: String = token.token + let _: Date = token.expirationDate + } + if let error: Error { + let _: String = error.localizedDescription + } + }) + + let _: Void = appCheck.limitedUseToken { token, error in + if let token: AppCheckTokenProtocol { + let _: String = token.token + let _: Date = token.expirationDate + } + if let error: Error { + let _: String = error.localizedDescription + } + } + } + + @available(iOS 13, macOS 10.15, macCatalyst 13, tvOS 13, *) + func usage_async() async { + do { + let token: AppCheckTokenProtocol = try await appCheck.token(forcingRefresh: false) + let _: String = token.token + let _: Date = token.expirationDate + } catch { + let _: String = error.localizedDescription + } + } +} diff --git a/FirebaseAppCheckInterop.podspec b/FirebaseAppCheckInterop.podspec index 8301d166818..659137c7f7f 100644 --- a/FirebaseAppCheckInterop.podspec +++ b/FirebaseAppCheckInterop.podspec @@ -20,11 +20,39 @@ Pod::Spec.new do |s| :tag => 'CocoaPods-' + s.version.to_s } s.social_media_url = 'https://twitter.com/Firebase' - s.ios.deployment_target = '10.0' - s.osx.deployment_target = '10.13' - s.tvos.deployment_target = '12.0' - s.watchos.deployment_target = '6.0' + + ios_deployment_target = '10.0' + osx_deployment_target = '10.13' + tvos_deployment_target = '12.0' + watchos_deployment_target = '6.0' + + s.ios.deployment_target = ios_deployment_target + s.osx.deployment_target = osx_deployment_target + s.tvos.deployment_target = tvos_deployment_target + s.watchos.deployment_target = watchos_deployment_target s.source_files = 'FirebaseAppCheck/Interop/**/*.[hm]' s.public_header_files = 'FirebaseAppCheck/Interop/Public/FirebaseAppCheckInterop/*.h' + + s.test_spec 'objc-unit' do |unit_tests| + unit_tests.platforms = { + :ios => ios_deployment_target, + :osx => osx_deployment_target, + :tvos => tvos_deployment_target + } + unit_tests.source_files = [ + 'FirebaseAppCheck/Tests/Interop/ObjC/**/*.[hm]', + ] + end + + s.test_spec 'swift-unit' do |swift_unit_tests| + swift_unit_tests.platforms = { + :ios => ios_deployment_target, + :osx => osx_deployment_target, + :tvos => tvos_deployment_target + } + swift_unit_tests.source_files = [ + 'FirebaseAppCheck/Tests/Interop/Swift/**/*.swift', + ] + end end