mirror of
https://github.com/google/app-check
synced 2026-11-09 22:05:52 +05:45
feat: Only log local debug tokens when not yet registered (#95)
Co-authored-by: Nick Cooke <nickcooke@google.com>
This commit is contained in:
1 parent
f6b4582ed1
commit
bb4002485f
4 files changed
+187
-15
No files matched your search
@@ -21,7 +21,7 @@ jobs:
|
||||
- os: macos-15
|
||||
xcode: Xcode_16.4
|
||||
- os: macos-26
|
||||
xcode: Xcode_26.1
|
||||
xcode: Xcode_26.2
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
@@ -36,18 +36,34 @@ static NSString *const kDebugTokenEnvKey = @"AppCheckDebugToken";
|
||||
static NSString *const kFirebaseDebugTokenEnvKey = @"FIRAAppCheckDebugToken";
|
||||
static NSString *const kDebugTokenUserDefaultsKey = @"GACAppCheckDebugToken";
|
||||
|
||||
// The base key for registration status.
|
||||
// NOTE: Do not use this key directly. Use `registeredUserDefaultsKeyForServiceName:resourceName:`
|
||||
// to obtain the namespaced key.
|
||||
static NSString *const kDebugTokenRegisteredUserDefaultsKey = @"GACAppCheckDebugTokenRegistered";
|
||||
|
||||
@interface GACAppCheckDebugProvider ()
|
||||
@property(nonatomic, readonly) id<GACAppCheckDebugProviderAPIServiceProtocol> APIService;
|
||||
@property(nonatomic, readonly, nullable, copy) NSString *debugTokenEnvValue;
|
||||
@property(nonatomic, readonly, copy) NSString *registeredUserDefaultsKey;
|
||||
|
||||
+ (NSString *)registeredUserDefaultsKeyForServiceName:(NSString *)serviceName
|
||||
resourceName:(NSString *)resourceName;
|
||||
@end
|
||||
|
||||
static NSString *_Nullable EnvironmentVariableDebugToken(NSString *registeredUserDefaultsKey);
|
||||
|
||||
@implementation GACAppCheckDebugProvider
|
||||
|
||||
- (instancetype)initWithAPIService:(id<GACAppCheckDebugProviderAPIServiceProtocol>)APIService {
|
||||
- (instancetype)initWithAPIService:(id<GACAppCheckDebugProviderAPIServiceProtocol>)APIService
|
||||
serviceName:(NSString *)serviceName
|
||||
resourceName:(NSString *)resourceName {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_APIService = APIService;
|
||||
_debugTokenEnvValue = EnvironmentVariableDebugToken();
|
||||
_registeredUserDefaultsKey =
|
||||
[[[self class] registeredUserDefaultsKeyForServiceName:serviceName
|
||||
resourceName:resourceName] copy];
|
||||
_debugTokenEnvValue = EnvironmentVariableDebugToken(_registeredUserDefaultsKey);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -70,7 +86,9 @@ static NSString *const kDebugTokenUserDefaultsKey = @"GACAppCheckDebugToken";
|
||||
[[GACAppCheckDebugProviderAPIService alloc] initWithAPIService:APIService
|
||||
resourceName:resourceName];
|
||||
|
||||
return [self initWithAPIService:debugAPIService];
|
||||
return [self initWithAPIService:debugAPIService
|
||||
serviceName:serviceName
|
||||
resourceName:resourceName];
|
||||
}
|
||||
|
||||
- (NSString *)currentDebugToken {
|
||||
@@ -108,6 +126,7 @@ static NSString *const kDebugTokenUserDefaultsKey = @"GACAppCheckDebugToken";
|
||||
return [self.APIService appCheckTokenWithDebugToken:debugToken limitedUse:limitedUse];
|
||||
})
|
||||
.then(^id(GACAppCheckToken *appCheckToken) {
|
||||
[[GULUserDefaults standardUserDefaults] setBool:YES forKey:self.registeredUserDefaultsKey];
|
||||
handler(appCheckToken, nil);
|
||||
return nil;
|
||||
})
|
||||
@@ -115,6 +134,10 @@ static NSString *const kDebugTokenUserDefaultsKey = @"GACAppCheckDebugToken";
|
||||
NSString *logMessage = [NSString
|
||||
stringWithFormat:@"Failed to exchange debug token to app check token: %@", error];
|
||||
GACAppCheckLogDebug(GACLoggerAppCheckMessageDebugProviderFailedExchange, logMessage);
|
||||
if (error.code != GACAppCheckErrorCodeServerUnreachable) {
|
||||
[[GULUserDefaults standardUserDefaults]
|
||||
removeObjectForKey:self.registeredUserDefaultsKey];
|
||||
}
|
||||
handler(nil, error);
|
||||
});
|
||||
}
|
||||
@@ -133,7 +156,19 @@ static NSString *GenerateAndStoreDebugToken(void) {
|
||||
return token;
|
||||
}
|
||||
|
||||
static NSString *_Nullable EnvironmentVariableDebugToken(void) {
|
||||
+ (NSString *)registeredUserDefaultsKeyForServiceName:(NSString *)serviceName
|
||||
resourceName:(NSString *)resourceName {
|
||||
NSString *safeServiceName = serviceName.length > 0 ? serviceName : @"default";
|
||||
NSString *safeResourceName = [resourceName stringByReplacingOccurrencesOfString:@"/"
|
||||
withString:@"_"];
|
||||
if (safeResourceName.length == 0) {
|
||||
safeResourceName = @"default";
|
||||
}
|
||||
return [NSString stringWithFormat:@"%@_%@_%@", kDebugTokenRegisteredUserDefaultsKey,
|
||||
safeServiceName, safeResourceName];
|
||||
}
|
||||
|
||||
static NSString *_Nullable EnvironmentVariableDebugToken(NSString *registeredUserDefaultsKey) {
|
||||
NSDictionary<NSString *, NSString *> *environment = [[NSProcessInfo processInfo] environment];
|
||||
NSString *envVariableValue = environment[kDebugTokenEnvKey];
|
||||
NSString *firebaseEnvVariableValue = environment[kFirebaseDebugTokenEnvKey];
|
||||
@@ -175,9 +210,14 @@ static NSString *_Nullable EnvironmentVariableDebugToken(void) {
|
||||
|
||||
return firebaseEnvVariableValue;
|
||||
} else {
|
||||
// Print only a locally generated token to avoid a valid token leak on CI.
|
||||
GACAppCheckLog(GACLoggerAppCheckMessageLocalDebugToken, GACAppCheckLogLevelWarning,
|
||||
[NSString stringWithFormat:@"App Check debug token: '%@'.", LocalDebugToken()]);
|
||||
BOOL isRegistered =
|
||||
[[GULUserDefaults standardUserDefaults] boolForKey:registeredUserDefaultsKey];
|
||||
if (!isRegistered) {
|
||||
// Print only a locally generated token to avoid a valid token leak on CI.
|
||||
GACAppCheckLog(
|
||||
GACLoggerAppCheckMessageLocalDebugToken, GACAppCheckLogLevelWarning,
|
||||
[NSString stringWithFormat:@"App Check debug token: '%@'.", LocalDebugToken()]);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@@ -22,15 +22,24 @@
|
||||
|
||||
#import "AppCheckCore/Sources/DebugProvider/API/GACAppCheckDebugProviderAPIService.h"
|
||||
#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckDebugProvider.h"
|
||||
#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckErrors.h"
|
||||
#import "AppCheckCore/Sources/Public/AppCheckCore/GACAppCheckToken.h"
|
||||
|
||||
static NSString *const kDebugTokenEnvKey = @"AppCheckDebugToken";
|
||||
static NSString *const kFirebaseDebugTokenEnvKey = @"FIRAAppCheckDebugToken";
|
||||
static NSString *const kDebugTokenUserDefaultsKey = @"GACAppCheckDebugToken";
|
||||
static NSString *const kDebugTokenRegisteredUserDefaultsKey = @"GACAppCheckDebugTokenRegistered";
|
||||
|
||||
@interface GACAppCheckDebugProvider (Tests)
|
||||
|
||||
- (instancetype)initWithAPIService:(id<GACAppCheckDebugProviderAPIServiceProtocol>)APIService;
|
||||
- (instancetype)initWithAPIService:(id<GACAppCheckDebugProviderAPIServiceProtocol>)APIService
|
||||
serviceName:(NSString *)serviceName
|
||||
resourceName:(NSString *)resourceName;
|
||||
|
||||
+ (NSString *)registeredUserDefaultsKeyForServiceName:(NSString *)serviceName
|
||||
resourceName:(NSString *)resourceName;
|
||||
|
||||
@property(nonatomic, readonly, copy) NSString *registeredUserDefaultsKey;
|
||||
|
||||
@end
|
||||
|
||||
@@ -51,7 +60,10 @@ typedef void (^GACAppCheckTokenValidationBlock)(GACAppCheckToken *_Nullable toke
|
||||
self.processInfoMock = OCMPartialMock([NSProcessInfo processInfo]);
|
||||
|
||||
self.fakeAPIService = OCMProtocolMock(@protocol(GACAppCheckDebugProviderAPIServiceProtocol));
|
||||
self.provider = [[GACAppCheckDebugProvider alloc] initWithAPIService:self.fakeAPIService];
|
||||
self.provider =
|
||||
[[GACAppCheckDebugProvider alloc] initWithAPIService:self.fakeAPIService
|
||||
serviceName:@"test-service"
|
||||
resourceName:@"projects/test-project/apps/test-app"];
|
||||
}
|
||||
|
||||
- (void)tearDown {
|
||||
@@ -59,6 +71,8 @@ typedef void (^GACAppCheckTokenValidationBlock)(GACAppCheckToken *_Nullable toke
|
||||
[self.processInfoMock stopMocking];
|
||||
self.processInfoMock = nil;
|
||||
[[GULUserDefaults standardUserDefaults] removeObjectForKey:kDebugTokenUserDefaultsKey];
|
||||
[[GULUserDefaults standardUserDefaults] removeObjectForKey:kDebugTokenRegisteredUserDefaultsKey];
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
#pragma mark - Debug token generating/storing
|
||||
@@ -69,7 +83,10 @@ typedef void (^GACAppCheckTokenValidationBlock)(GACAppCheckToken *_Nullable toke
|
||||
NSString *envToken = @"env token";
|
||||
OCMExpect([self.processInfoMock processInfo]).andReturn(self.processInfoMock);
|
||||
OCMExpect([self.processInfoMock environment]).andReturn(@{kDebugTokenEnvKey : envToken});
|
||||
self.provider = [[GACAppCheckDebugProvider alloc] initWithAPIService:self.fakeAPIService];
|
||||
self.provider =
|
||||
[[GACAppCheckDebugProvider alloc] initWithAPIService:self.fakeAPIService
|
||||
serviceName:@"test-service"
|
||||
resourceName:@"projects/test-project/apps/test-app"];
|
||||
|
||||
XCTAssertEqualObjects([self.provider currentDebugToken], envToken);
|
||||
}
|
||||
@@ -82,7 +99,10 @@ typedef void (^GACAppCheckTokenValidationBlock)(GACAppCheckToken *_Nullable toke
|
||||
OCMExpect([self.processInfoMock environment])
|
||||
.andReturn(
|
||||
(@{kDebugTokenEnvKey : envToken, kFirebaseDebugTokenEnvKey : @"firebase env token"}));
|
||||
self.provider = [[GACAppCheckDebugProvider alloc] initWithAPIService:self.fakeAPIService];
|
||||
self.provider =
|
||||
[[GACAppCheckDebugProvider alloc] initWithAPIService:self.fakeAPIService
|
||||
serviceName:@"test-service"
|
||||
resourceName:@"projects/test-project/apps/test-app"];
|
||||
|
||||
XCTAssertEqualObjects([self.provider currentDebugToken], envToken);
|
||||
}
|
||||
@@ -95,7 +115,10 @@ typedef void (^GACAppCheckTokenValidationBlock)(GACAppCheckToken *_Nullable toke
|
||||
OCMExpect([self.processInfoMock environment]).andReturn((@{
|
||||
kFirebaseDebugTokenEnvKey : envToken
|
||||
}));
|
||||
self.provider = [[GACAppCheckDebugProvider alloc] initWithAPIService:self.fakeAPIService];
|
||||
self.provider =
|
||||
[[GACAppCheckDebugProvider alloc] initWithAPIService:self.fakeAPIService
|
||||
serviceName:@"test-service"
|
||||
resourceName:@"projects/test-project/apps/test-app"];
|
||||
|
||||
XCTAssertEqualObjects([self.provider currentDebugToken], envToken);
|
||||
}
|
||||
@@ -106,7 +129,10 @@ typedef void (^GACAppCheckTokenValidationBlock)(GACAppCheckToken *_Nullable toke
|
||||
OCMExpect([self.processInfoMock environment])
|
||||
.andReturn(
|
||||
(@{kDebugTokenEnvKey : envToken, kFirebaseDebugTokenEnvKey : @"firebase env token"}));
|
||||
self.provider = [[GACAppCheckDebugProvider alloc] initWithAPIService:self.fakeAPIService];
|
||||
self.provider =
|
||||
[[GACAppCheckDebugProvider alloc] initWithAPIService:self.fakeAPIService
|
||||
serviceName:@"test-service"
|
||||
resourceName:@"projects/test-project/apps/test-app"];
|
||||
|
||||
XCTAssertEqualObjects([self.provider currentDebugToken], envToken);
|
||||
}
|
||||
@@ -225,6 +251,111 @@ typedef void (^GACAppCheckTokenValidationBlock)(GACAppCheckToken *_Nullable toke
|
||||
OCMVerifyAll(self.fakeAPIService);
|
||||
}
|
||||
|
||||
- (void)testGetTokenSuccessSetsRegisteredFlag {
|
||||
// 1. Stub API service.
|
||||
NSString *expectedDebugToken = [self.provider currentDebugToken];
|
||||
GACAppCheckToken *validToken = [[GACAppCheckToken alloc] initWithToken:@"valid_token"
|
||||
expirationDate:[NSDate date]
|
||||
receivedAtDate:[NSDate date]];
|
||||
FBLPromise *resolvedPromise = [FBLPromise pendingPromise];
|
||||
[resolvedPromise fulfill:validToken];
|
||||
OCMExpect([self.fakeAPIService appCheckTokenWithDebugToken:expectedDebugToken limitedUse:NO])
|
||||
.andReturn(resolvedPromise);
|
||||
|
||||
[[GULUserDefaults standardUserDefaults]
|
||||
removeObjectForKey:self.provider.registeredUserDefaultsKey];
|
||||
|
||||
// 2. Validate get token.
|
||||
[self validateGetToken:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
|
||||
XCTAssertNil(error);
|
||||
XCTAssertNotNil(token);
|
||||
}];
|
||||
|
||||
// 3. Verify flag is now YES.
|
||||
XCTAssertTrue(
|
||||
[[GULUserDefaults standardUserDefaults] boolForKey:self.provider.registeredUserDefaultsKey]);
|
||||
|
||||
// 4. Verify fakes.
|
||||
OCMVerifyAll(self.fakeAPIService);
|
||||
}
|
||||
|
||||
- (void)testGetTokenPermanentFailureClearsRegisteredFlag {
|
||||
// 1. Stub API service.
|
||||
NSString *expectedDebugToken = [self.provider currentDebugToken];
|
||||
NSError *APIError = [NSError errorWithDomain:@"testGetTokenPermanentFailureClearsRegisteredFlag"
|
||||
code:-1
|
||||
userInfo:nil];
|
||||
FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
|
||||
[rejectedPromise reject:APIError];
|
||||
OCMExpect([self.fakeAPIService appCheckTokenWithDebugToken:expectedDebugToken limitedUse:NO])
|
||||
.andReturn(rejectedPromise);
|
||||
|
||||
// Pre-populate flag to YES.
|
||||
[[GULUserDefaults standardUserDefaults] setBool:YES
|
||||
forKey:self.provider.registeredUserDefaultsKey];
|
||||
|
||||
// 2. Validate get token.
|
||||
[self validateGetToken:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
|
||||
XCTAssertNotNil(error);
|
||||
XCTAssertNil(token);
|
||||
}];
|
||||
|
||||
// 3. Verify flag is cleared.
|
||||
XCTAssertNil([[GULUserDefaults standardUserDefaults]
|
||||
objectForKey:self.provider.registeredUserDefaultsKey]);
|
||||
|
||||
// 4. Verify fakes.
|
||||
OCMVerifyAll(self.fakeAPIService);
|
||||
}
|
||||
|
||||
- (void)testGetTokenNetworkFailureDoesNotClearRegisteredFlag {
|
||||
// 1. Stub API service.
|
||||
NSString *expectedDebugToken = [self.provider currentDebugToken];
|
||||
NSError *networkError = [NSError errorWithDomain:GACAppCheckErrorDomain
|
||||
code:GACAppCheckErrorCodeServerUnreachable
|
||||
userInfo:nil];
|
||||
FBLPromise *rejectedPromise = [FBLPromise pendingPromise];
|
||||
[rejectedPromise reject:networkError];
|
||||
OCMExpect([self.fakeAPIService appCheckTokenWithDebugToken:expectedDebugToken limitedUse:NO])
|
||||
.andReturn(rejectedPromise);
|
||||
|
||||
// Pre-populate flag to YES.
|
||||
[[GULUserDefaults standardUserDefaults] setBool:YES
|
||||
forKey:self.provider.registeredUserDefaultsKey];
|
||||
|
||||
// 2. Validate get token.
|
||||
[self validateGetToken:^(GACAppCheckToken *_Nullable token, NSError *_Nullable error) {
|
||||
XCTAssertNotNil(error);
|
||||
XCTAssertNil(token);
|
||||
}];
|
||||
|
||||
// 3. Verify flag is still YES.
|
||||
XCTAssertTrue(
|
||||
[[GULUserDefaults standardUserDefaults] boolForKey:self.provider.registeredUserDefaultsKey]);
|
||||
|
||||
// 4. Verify fakes.
|
||||
OCMVerifyAll(self.fakeAPIService);
|
||||
}
|
||||
|
||||
#pragma mark - Keys
|
||||
|
||||
- (void)testRegisteredUserDefaultsKeyForServiceName_resourceName {
|
||||
XCTAssertEqualObjects(
|
||||
[GACAppCheckDebugProvider registeredUserDefaultsKeyForServiceName:@"app1"
|
||||
resourceName:@"projects/p1/apps/a1"],
|
||||
@"GACAppCheckDebugTokenRegistered_app1_projects_p1_apps_a1");
|
||||
XCTAssertEqualObjects(
|
||||
[GACAppCheckDebugProvider registeredUserDefaultsKeyForServiceName:@"app2"
|
||||
resourceName:@"projects/p2/apps/a2"],
|
||||
@"GACAppCheckDebugTokenRegistered_app2_projects_p2_apps_a2");
|
||||
XCTAssertEqualObjects([GACAppCheckDebugProvider registeredUserDefaultsKeyForServiceName:@""
|
||||
resourceName:@""],
|
||||
@"GACAppCheckDebugTokenRegistered_default_default");
|
||||
XCTAssertEqualObjects([GACAppCheckDebugProvider registeredUserDefaultsKeyForServiceName:nil
|
||||
resourceName:nil],
|
||||
@"GACAppCheckDebugTokenRegistered_default_default");
|
||||
}
|
||||
|
||||
#pragma mark - Helpers
|
||||
|
||||
- (void)validateGetToken:(GACAppCheckTokenValidationBlock)validationBlock {
|
||||
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
# 11.3.0
|
||||
- [changed] Added Recaptcha Enterprise attestation provider.
|
||||
- [changed] Added Recaptcha Enterprise attestation provider. (#94)
|
||||
- [changed] Only log local debug tokens when not yet registered. (#95)
|
||||
|
||||
# 11.2.0
|
||||
- [changed] To prevent reusing expired artifacts, skip local cache when making
|
||||
|
||||
Reference in new issue
Block a user