Skip to content

Commit 1803b00

Browse files
authored
App Check: Token API for 3P use (#8266)
* Initial API implementation * Update CHANGELOG * Update test imports * Make API async/await friendly by default * Make API async friendly by default 2 * Fix CI * Remove async/await example to fix CI * Edit comment spacing * Keychain error handling * API rename and tests * Add comments to tests * Add public errors to umbrella header * Refactor remaining test logic * Remove newline * Review * Fix import style * Fix import style 2
1 parent 5ac44b8 commit 1803b00

20 files changed

+975
-257
lines changed

FirebaseAppCheck/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
# Unreleased
2+
- [added] Token API for 3P use. (#8266)
13
# 8.2.0 -- M98
24
- [added] Apple's App Attest attestation provider support. (#8133)
35
- [changed] Token auto-refresh optimizations. (#8232)
46
# v8.0.0 -- M95
5-
- [added] Firebase abuse reduction support SDK. (#7928, #7937, #7948)
7+
- [added] Firebase abuse reduction support SDK. (#7928, #7937, #7948)

FirebaseAppCheck/Sources/AppAttestProvider/Errors/FIRAppAttestRejectionError.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@
1414
* limitations under the License.
1515
*/
1616

17+
#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckErrors.h"
18+
1719
#import "FirebaseAppCheck/Sources/AppAttestProvider/Errors/FIRAppAttestRejectionError.h"
1820

1921
#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
2022

2123
@implementation FIRAppAttestRejectionError
2224

2325
- (instancetype)init {
24-
return [self initWithDomain:kFIRAppCheckErrorDomain
25-
code:FIRAppCheckErrorCodeUnknown
26-
userInfo:nil];
26+
return [self initWithDomain:FIRAppCheckErrorDomain code:FIRAppCheckErrorCodeUnknown userInfo:nil];
2727
}
2828

2929
@end

FirebaseAppCheck/Sources/AppAttestProvider/Storage/FIRAppAttestArtifactStorage.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#import <Foundation/Foundation.h>
1818

1919
@class FBLPromise<ValueType>;
20+
@class GULKeychainStorage;
2021

2122
NS_ASSUME_NONNULL_BEGIN
2223

@@ -46,7 +47,7 @@ NS_ASSUME_NONNULL_BEGIN
4647

4748
- (instancetype)init NS_UNAVAILABLE;
4849

49-
/// A default initializer.
50+
/// Default convenience initializer.
5051
/// @param appName A Firebase App name (`FirebaseApp.name`). The app name will be used as a part of
5152
/// the key to store the token for the storage instance.
5253
/// @param appID A Firebase App identifier (`FirebaseOptions.googleAppID`). The app ID will be used
@@ -55,6 +56,19 @@ NS_ASSUME_NONNULL_BEGIN
5556
- (instancetype)initWithAppName:(NSString *)appName
5657
appID:(NSString *)appID
5758
accessGroup:(nullable NSString *)accessGroup;
59+
60+
/// Designated initializer.
61+
/// @param appName A Firebase App name (`FirebaseApp.name`). The app name will be used as a part of
62+
/// the key to store the token for the storage instance.
63+
/// @param appID A Firebase App identifier (`FirebaseOptions.googleAppID`). The app ID will be used
64+
/// as a part of the key to store the token for the storage instance.
65+
/// @param keychainStorage An instance of `GULKeychainStorage` used as an underlying secure storage.
66+
/// @param accessGroup The Keychain Access Group.
67+
- (instancetype)initWithAppName:(NSString *)appName
68+
appID:(NSString *)appID
69+
keychainStorage:(GULKeychainStorage *)keychainStorage
70+
accessGroup:(nullable NSString *)accessGroup NS_DESIGNATED_INITIALIZER;
71+
5872
@end
5973

6074
NS_ASSUME_NONNULL_END

FirebaseAppCheck/Sources/AppAttestProvider/Storage/FIRAppAttestArtifactStorage.m

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,11 @@
2222
#import "FBLPromises.h"
2323
#endif
2424

25-
#import "FirebaseAppCheck/Sources/AppAttestProvider/Storage/FIRAppAttestStoredArtifact.h"
26-
2725
#import <GoogleUtilities/GULKeychainStorage.h>
2826

27+
#import "FirebaseAppCheck/Sources/AppAttestProvider/Storage/FIRAppAttestStoredArtifact.h"
28+
#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
29+
2930
NS_ASSUME_NONNULL_BEGIN
3031

3132
static NSString *const kKeychainService = @"com.firebase.app_check.app_attest_artifact_storage";
@@ -78,27 +79,42 @@ - (instancetype)initWithAppName:(NSString *)appName
7879
} else {
7980
return nil;
8081
}
82+
})
83+
.recover(^NSError *(NSError *error) {
84+
return [FIRAppCheckErrorUtil keychainErrorWithError:error];
8185
});
8286
}
8387

8488
- (FBLPromise<NSData *> *)setArtifact:(nullable NSData *)artifact forKey:(nonnull NSString *)keyID {
8589
if (artifact) {
86-
FIRAppAttestStoredArtifact *storedArtifact =
87-
[[FIRAppAttestStoredArtifact alloc] initWithKeyID:keyID artifact:artifact];
88-
return [self.keychainStorage setObject:storedArtifact
89-
forKey:[self artifactKey]
90-
accessGroup:self.accessGroup]
91-
.then(^id _Nullable(NSNull *_Nullable value) {
92-
return artifact;
93-
});
90+
return [self storeArtifact:artifact forKey:keyID].recover(^NSError *(NSError *error) {
91+
return [FIRAppCheckErrorUtil keychainErrorWithError:error];
92+
});
9493
} else {
9594
return [self.keychainStorage removeObjectForKey:[self artifactKey] accessGroup:self.accessGroup]
9695
.then(^id _Nullable(NSNull *_Nullable value) {
9796
return nil;
97+
})
98+
.recover(^NSError *(NSError *error) {
99+
return [FIRAppCheckErrorUtil keychainErrorWithError:error];
98100
});
99101
}
100102
}
101103

104+
#pragma mark - Helpers
105+
106+
- (FBLPromise<NSData *> *)storeArtifact:(nullable NSData *)artifact
107+
forKey:(nonnull NSString *)keyID {
108+
FIRAppAttestStoredArtifact *storedArtifact =
109+
[[FIRAppAttestStoredArtifact alloc] initWithKeyID:keyID artifact:artifact];
110+
return [self.keychainStorage setObject:storedArtifact
111+
forKey:[self artifactKey]
112+
accessGroup:self.accessGroup]
113+
.then(^id _Nullable(NSNull *_Nullable value) {
114+
return artifact;
115+
});
116+
}
117+
102118
- (NSString *)artifactKey {
103119
return
104120
[NSString stringWithFormat:@"app_check_app_attest_artifact.%@.%@", self.appName, self.appID];

FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,20 @@
2020

2121
NS_ASSUME_NONNULL_BEGIN
2222

23-
FOUNDATION_EXTERN NSErrorDomain const kFIRAppCheckErrorDomain NS_SWIFT_NAME(AppCheckErrorDomain);
24-
2523
void FIRAppCheckSetErrorToPointer(NSError *error, NSError **pointer);
2624

2725
@interface FIRAppCheckErrorUtil : NSObject
2826

29-
// Internal errors.
27+
+ (NSError *)publicDomainErrorWithError:(NSError *)error;
28+
29+
// MARK: - Internal errors
3030

3131
+ (NSError *)cachedTokenNotFound;
32+
3233
+ (NSError *)cachedTokenExpired;
3334

35+
+ (NSError *)keychainErrorWithError:(NSError *)error;
36+
3437
+ (FIRAppCheckHTTPError *)APIErrorWithHTTPResponse:(NSHTTPURLResponse *)HTTPResponse
3538
data:(nullable NSData *)data;
3639

@@ -50,20 +53,4 @@ void FIRAppCheckSetErrorToPointer(NSError *error, NSError **pointer);
5053

5154
@end
5255

53-
typedef NS_ERROR_ENUM(kFIRAppCheckErrorDomain, FIRAppCheckErrorCode){
54-
/// An unknown or non-actionable error.
55-
FIRAppCheckErrorCodeUnknown = 0,
56-
57-
/// A network connection error.
58-
FIRAppCheckErrorCodeServerUnreachable = 1,
59-
60-
/// Invalid configuration error.
61-
FIRAppCheckErrorCodeInvalidConfiguration = 2,
62-
63-
/// System keychain access error.
64-
FIRAppCheckErrorCodeKeychain = 3,
65-
66-
/// Selected app attestation provider is not supported on the current platform or OS version.
67-
FIRAppCheckErrorCodeUnsupported = 4} NS_SWIFT_NAME(AppCheckErrorCode);
68-
6956
NS_ASSUME_NONNULL_END

FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.m

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,24 @@
1515
*/
1616

1717
#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
18-
#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckHTTPError.h"
1918

20-
NSString *const kFIRAppCheckErrorDomain = @"com.firebase.appCheck";
19+
#import <GoogleUtilities/GULKeychainUtils.h>
20+
21+
#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckHTTPError.h"
22+
#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckErrors.h"
2123

2224
@implementation FIRAppCheckErrorUtil
2325

26+
+ (NSError *)publicDomainErrorWithError:(NSError *)error {
27+
if ([error.domain isEqualToString:FIRAppCheckErrorDomain]) {
28+
return error;
29+
}
30+
31+
return [self unknownErrorWithError:error];
32+
}
33+
34+
#pragma mark - Internal errors
35+
2436
+ (NSError *)cachedTokenNotFound {
2537
NSString *failureReason = [NSString stringWithFormat:@"Cached token not found."];
2638
return [self appCheckErrorWithCode:FIRAppCheckErrorCodeUnknown
@@ -35,14 +47,25 @@ + (NSError *)cachedTokenExpired {
3547
underlyingError:nil];
3648
}
3749

50+
+ (NSError *)keychainErrorWithError:(NSError *)error {
51+
if ([error.domain isEqualToString:kGULKeychainUtilsErrorDomain]) {
52+
NSString *failureReason = [NSString stringWithFormat:@"Keychain access error."];
53+
return [self appCheckErrorWithCode:FIRAppCheckErrorCodeKeychain
54+
failureReason:failureReason
55+
underlyingError:error];
56+
}
57+
58+
return [self unknownErrorWithError:error];
59+
}
60+
3861
+ (FIRAppCheckHTTPError *)APIErrorWithHTTPResponse:(NSHTTPURLResponse *)HTTPResponse
3962
data:(nullable NSData *)data {
4063
return [[FIRAppCheckHTTPError alloc] initWithHTTPResponse:HTTPResponse data:data];
4164
}
4265

4366
+ (NSError *)APIErrorWithNetworkError:(NSError *)networkError {
4467
NSString *failureReason = [NSString stringWithFormat:@"API request error."];
45-
return [self appCheckErrorWithCode:FIRAppCheckErrorCodeUnknown
68+
return [self appCheckErrorWithCode:FIRAppCheckErrorCodeServerUnreachable
4669
failureReason:failureReason
4770
underlyingError:networkError];
4871
}
@@ -83,7 +106,7 @@ + (NSError *)unsupportedAttestationProvider:(NSString *)providerName {
83106
}
84107

85108
+ (NSError *)appAttestKeyIDNotFound {
86-
NSString *failureReason = @"App attest key ID not found.";
109+
NSString *failureReason = [NSString stringWithFormat:@"App attest key ID not found."];
87110
return [self appCheckErrorWithCode:FIRAppCheckErrorCodeUnknown
88111
failureReason:failureReason
89112
underlyingError:nil];
@@ -95,14 +118,23 @@ + (NSError *)errorWithFailureReason:(NSString *)failureReason {
95118
underlyingError:nil];
96119
}
97120

121+
#pragma mark - Helpers
122+
123+
+ (NSError *)unknownErrorWithError:(NSError *)error {
124+
NSString *failureReason = error.userInfo[NSLocalizedFailureReasonErrorKey];
125+
return [self appCheckErrorWithCode:FIRAppCheckErrorCodeUnknown
126+
failureReason:failureReason
127+
underlyingError:error];
128+
}
129+
98130
+ (NSError *)appCheckErrorWithCode:(FIRAppCheckErrorCode)code
99131
failureReason:(nullable NSString *)failureReason
100132
underlyingError:(nullable NSError *)underlyingError {
101133
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
102134
userInfo[NSUnderlyingErrorKey] = underlyingError;
103135
userInfo[NSLocalizedFailureReasonErrorKey] = failureReason;
104136

105-
return [NSError errorWithDomain:kFIRAppCheckErrorDomain code:code userInfo:userInfo];
137+
return [NSError errorWithDomain:FIRAppCheckErrorDomain code:code userInfo:userInfo];
106138
}
107139

108140
@end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* Copyright 2021 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
19+
#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckErrors.h"
20+
21+
NSErrorDomain const FIRAppCheckErrorDomain = @"com.firebase.appCheck";

FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckHTTPError.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@
1717
#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckHTTPError.h"
1818

1919
#import "FirebaseAppCheck/Sources/Core/Errors/FIRAppCheckErrorUtil.h"
20+
#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckErrors.h"
2021

2122
@implementation FIRAppCheckHTTPError
2223

2324
- (instancetype)initWithHTTPResponse:(NSHTTPURLResponse *)HTTPResponse
2425
data:(nullable NSData *)data {
2526
NSDictionary *userInfo = [[self class] userInfoWithHTTPResponse:HTTPResponse data:data];
26-
self = [super initWithDomain:kFIRAppCheckErrorDomain
27+
self = [super initWithDomain:FIRAppCheckErrorDomain
2728
code:FIRAppCheckErrorCodeUnknown
2829
userInfo:userInfo];
2930
if (self) {

FirebaseAppCheck/Sources/Core/FIRAppCheck.m

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#import "FBLPromises.h"
2323
#endif
2424

25+
#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckErrors.h"
2526
#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckProvider.h"
2627
#import "FirebaseAppCheck/Sources/Public/FirebaseAppCheck/FIRAppCheckProviderFactory.h"
2728

@@ -171,7 +172,7 @@ - (instancetype)initWithAppName:(NSString *)appName
171172
+ (instancetype)appCheck {
172173
FIRApp *defaultApp = [FIRApp defaultApp];
173174
if (!defaultApp) {
174-
[NSException raise:kFIRAppCheckErrorDomain
175+
[NSException raise:FIRAppCheckErrorDomain
175176
format:@"The default FirebaseApp instance must be configured before the default"
176177
@"AppCheck instance can be initialized. One way to ensure that is to "
177178
@"call `[FIRApp configure];` (`FirebaseApp.configure()` in Swift) in the App"
@@ -186,6 +187,19 @@ + (nullable instancetype)appCheckWithApp:(FIRApp *)firebaseApp {
186187
return (FIRAppCheck *)appCheck;
187188
}
188189

190+
- (void)tokenForcingRefresh:(BOOL)forcingRefresh
191+
completion:(void (^)(FIRAppCheckToken *_Nullable token,
192+
NSError *_Nullable error))handler {
193+
[self retrieveOrRefreshTokenForcingRefresh:forcingRefresh]
194+
.then(^id _Nullable(FIRAppCheckToken *token) {
195+
handler(token, nil);
196+
return token;
197+
})
198+
.catch(^(NSError *_Nonnull error) {
199+
handler(nil, [FIRAppCheckErrorUtil publicDomainErrorWithError:error]);
200+
});
201+
}
202+
189203
+ (void)setAppCheckProviderFactory:(nullable id<FIRAppCheckProviderFactory>)factory {
190204
self.providerFactory = factory;
191205
}

0 commit comments

Comments
 (0)