Procházet zdrojové kódy

Sending authentication token for crashlytics and session (#12383)

themiswang před 2 roky
rodič
revize
cb2f6e901a
25 změnil soubory, kde provedl 233 přidání a 56 odebrání
  1. 4 0
      Crashlytics/CHANGELOG.md
  2. 1 0
      Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h
  3. 5 2
      Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.m
  4. 1 1
      Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.h
  5. 23 11
      Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.m
  6. 2 1
      Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter.h
  7. 5 1
      Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter.m
  8. 2 1
      Crashlytics/Protogen/nanopb/crashlytics.nanopb.c
  9. 3 1
      Crashlytics/Protogen/nanopb/crashlytics.nanopb.h
  10. 6 3
      Crashlytics/UnitTests/FIRCLSContextManagerTests.m
  11. 49 20
      Crashlytics/UnitTests/FIRCLSInstallIdentifierModelTests.m
  12. 3 1
      Crashlytics/UnitTests/FIRCLSReportAdapterTests.m
  13. 3 0
      Crashlytics/UnitTests/Mocks/FIRMockInstallations.h
  14. 17 0
      Crashlytics/UnitTests/Mocks/FIRMockInstallations.m
  15. 2 0
      FirebaseSessions/Sources/Development/DevEventConsoleLogger.swift
  16. 4 0
      FirebaseSessions/Sources/FirebaseSessions.swift
  17. 2 0
      FirebaseSessions/Sources/FirebaseSessionsError.swift
  18. 49 5
      FirebaseSessions/Sources/Installations+InstallationsProtocol.swift
  19. 4 2
      FirebaseSessions/Sources/SessionCoordinator.swift
  20. 7 0
      FirebaseSessions/Sources/SessionStartEvent.swift
  21. 2 2
      FirebaseSessions/Sources/Settings/SettingsDownloadClient.swift
  22. 2 1
      FirebaseSessions/SourcesObjC/Protogen/nanopb/sessions.nanopb.c
  23. 3 1
      FirebaseSessions/SourcesObjC/Protogen/nanopb/sessions.nanopb.h
  24. 18 3
      FirebaseSessions/Tests/Unit/Mocks/MockInstallationsProtocol.swift
  25. 16 0
      FirebaseSessions/Tests/Unit/SessionCoordinatorTests.swift

+ 4 - 0
Crashlytics/CHANGELOG.md

@@ -1,3 +1,7 @@
+# Unreleased
+
+- [fixed] Force validation or rotation of FIDs for FirebaseSessions.
+
 # 10.22.0
 - [changed] Removed calls to statfs in the Crashlytics SDK to comply with Apple Privacy Manifests. This change removes support for collecting Disk Space Free in Crashlytics reports.
 - [fixed] Fixed FirebaseSessions crash on startup that occurs in release mode in Xcode 15.3 and other build configurations. (#11403)

+ 1 - 0
Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.h

@@ -28,6 +28,7 @@
 @property(nonatomic, readonly) NSOperationQueue *operationQueue;
 @property(nonatomic, readonly) FIRCLSFileManager *fileManager;
 @property(nonatomic, copy) NSString *fiid;
+@property(nonatomic, copy) NSString *authToken;
 
 - (void)prepareAndSubmitReport:(FIRCLSInternalReport *)report
            dataCollectionToken:(FIRCLSDataCollectionToken *)dataCollectionToken

+ 5 - 2
Crashlytics/Crashlytics/Controllers/FIRCLSReportUploader.m

@@ -95,8 +95,10 @@
         // urgent mode. Since urgent mode happens when the app is in a crash loop,
         // we can safely assume users aren't rotating their FIID, so this can be skipped.
         if (!urgent) {
-          [self.installIDModel regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull newFIID) {
+          [self.installIDModel regenerateInstallIDIfNeededWithBlock:^(
+                                   NSString *_Nonnull newFIID, NSString *_Nonnull authToken) {
             self.fiid = [newFIID copy];
+            self.authToken = [authToken copy];
           }];
         } else {
           FIRCLSWarningLog(
@@ -186,7 +188,8 @@
   FIRCLSReportAdapter *adapter = [[FIRCLSReportAdapter alloc] initWithPath:path
                                                                googleAppId:self.googleAppID
                                                             installIDModel:self.installIDModel
-                                                                      fiid:self.fiid];
+                                                                      fiid:self.fiid
+                                                                 authToken:self.authToken];
 
   GDTCOREvent *event = [self.googleTransport eventForTransport];
   event.dataObject = adapter;

+ 1 - 1
Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.h

@@ -44,7 +44,7 @@ NS_ASSUME_NONNULL_BEGIN
  *  - Concern 2: Whatever the FIID is, we should send it with the Crash report so we're in sync with
  * Sessions and other Firebase SDKs
  */
-- (BOOL)regenerateInstallIDIfNeededWithBlock:(void (^)(NSString *fiid))block;
+- (BOOL)regenerateInstallIDIfNeededWithBlock:(void (^)(NSString *fiid, NSString *authToken))block;
 
 @end
 

+ 23 - 11
Crashlytics/Crashlytics/Models/FIRCLSInstallIdentifierModel.m

@@ -98,33 +98,45 @@ static unsigned long long FIRCLSInstallationsWaitTime = 10 * NSEC_PER_SEC;
 
 #pragma mark Privacy Shield
 
-- (BOOL)regenerateInstallIDIfNeededWithBlock:(void (^)(NSString *fiid))block {
+- (BOOL)regenerateInstallIDIfNeededWithBlock:(void (^)(NSString *fiid, NSString *authToken))block {
   BOOL __block didRotate = false;
+  NSString __block *authTokenComplete = @"";
+  NSString __block *currentIIDComplete = @"";
 
-  dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+  // Installations Completions run async, so wait a reasonable amount of time for it to finish.
+  dispatch_group_t workingGroup = dispatch_group_create();
 
-  // This runs Completion async, so wait a reasonable amount of time for it to finish.
+  dispatch_group_enter(workingGroup);
   [self.installations
-      installationIDWithCompletion:^(NSString *_Nullable currentIID, NSError *_Nullable error) {
-        // Provide the IID to the callback. For this case we don't care
-        // if the FIID is null because it's the best we can do - we just want
-        // to send up the same FIID that is sent by other SDKs (eg. the Sessions SDK).
-        block(currentIID);
+      authTokenWithCompletion:^(FIRInstallationsAuthTokenResult *_Nullable tokenResult,
+                                NSError *_Nullable error) {
+        authTokenComplete = tokenResult.authToken;
+        dispatch_group_leave(workingGroup);
+      }];
 
+  dispatch_group_enter(workingGroup);
+  [self.installations
+      installationIDWithCompletion:^(NSString *_Nullable currentIID, NSError *_Nullable error) {
+        currentIIDComplete = currentIID;
         didRotate = [self rotateCrashlyticsInstallUUIDWithIID:currentIID error:error];
 
         if (didRotate) {
           FIRCLSInfoLog(@"Rotated Crashlytics Install UUID because Firebase Install ID changed");
         }
-        dispatch_semaphore_signal(semaphore);
+        dispatch_group_leave(workingGroup);
       }];
 
-  intptr_t result = dispatch_semaphore_wait(
-      semaphore, dispatch_time(DISPATCH_TIME_NOW, FIRCLSInstallationsWaitTime));
+  intptr_t result = dispatch_group_wait(
+      workingGroup, dispatch_time(DISPATCH_TIME_NOW, FIRCLSInstallationsWaitTime));
+
   if (result != 0) {
     FIRCLSErrorLog(@"Crashlytics timed out while checking for Firebase Installation ID");
   }
 
+  // Provide the IID to the callback. For this case we don't care
+  // if the FIID is null because it's the best we can do - we just want
+  // to send up the same FIID that is sent by other SDKs (eg. the Sessions SDK).
+  block(currentIIDComplete, authTokenComplete);
   return didRotate;
 }
 

+ 2 - 1
Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter.h

@@ -35,5 +35,6 @@
 - (instancetype)initWithPath:(NSString *)folderPath
                  googleAppId:(NSString *)googleAppID
               installIDModel:(FIRCLSInstallIdentifierModel *)installIDModel
-                        fiid:(NSString *)fiid;
+                        fiid:(NSString *)fiid
+                   authToken:(NSString *)authToken;
 @end

+ 5 - 1
Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter.m

@@ -30,6 +30,7 @@
 
 @property(nonatomic, strong) FIRCLSInstallIdentifierModel *installIDModel;
 @property(nonatomic, copy) NSString *fiid;
+@property(nonatomic, copy) NSString *authToken;
 
 @end
 
@@ -38,13 +39,15 @@
 - (instancetype)initWithPath:(NSString *)folderPath
                  googleAppId:(NSString *)googleAppID
               installIDModel:(FIRCLSInstallIdentifierModel *)installIDModel
-                        fiid:(NSString *)fiid {
+                        fiid:(NSString *)fiid
+                   authToken:(NSString *)authToken {
   self = [super init];
   if (self) {
     _folderPath = folderPath;
     _googleAppID = googleAppID;
     _installIDModel = installIDModel;
     _fiid = [fiid copy];
+    _authToken = [authToken copy];
 
     [self loadMetaDataFile];
 
@@ -156,6 +159,7 @@
   report.installation_uuid = FIRCLSEncodeString(self.installIDModel.installID);
   report.firebase_installation_id = FIRCLSEncodeString(self.fiid);
   report.app_quality_session_id = FIRCLSEncodeString(self.identity.app_quality_session_id);
+  report.firebase_authentication_token = FIRCLSEncodeString(self.authToken);
   report.build_version = FIRCLSEncodeString(self.application.build_version);
   report.display_version = FIRCLSEncodeString(self.application.display_version);
   report.apple_payload = [self protoFilesPayload];

+ 2 - 1
Crashlytics/Protogen/nanopb/crashlytics.nanopb.c

@@ -26,7 +26,7 @@
 
 
 
-const pb_field_t google_crashlytics_Report_fields[10] = {
+const pb_field_t google_crashlytics_Report_fields[11] = {
     PB_FIELD(  1, BYTES   , SINGULAR, POINTER , FIRST, google_crashlytics_Report, sdk_version, sdk_version, 0),
     PB_FIELD(  3, BYTES   , SINGULAR, POINTER , OTHER, google_crashlytics_Report, gmp_app_id, sdk_version, 0),
     PB_FIELD(  4, UENUM   , SINGULAR, STATIC  , OTHER, google_crashlytics_Report, platform, gmp_app_id, 0),
@@ -36,6 +36,7 @@ const pb_field_t google_crashlytics_Report_fields[10] = {
     PB_FIELD( 10, MESSAGE , SINGULAR, STATIC  , OTHER, google_crashlytics_Report, apple_payload, display_version, &google_crashlytics_FilesPayload_fields),
     PB_FIELD( 16, BYTES   , SINGULAR, POINTER , OTHER, google_crashlytics_Report, firebase_installation_id, apple_payload, 0),
     PB_FIELD( 17, BYTES   , SINGULAR, POINTER , OTHER, google_crashlytics_Report, app_quality_session_id, firebase_installation_id, 0),
+    PB_FIELD( 19, BYTES   , SINGULAR, POINTER , OTHER, google_crashlytics_Report, firebase_authentication_token, app_quality_session_id, 0),
     PB_LAST_FIELD
 };
 

+ 3 - 1
Crashlytics/Protogen/nanopb/crashlytics.nanopb.h

@@ -61,6 +61,7 @@ typedef struct _google_crashlytics_Report {
     google_crashlytics_FilesPayload apple_payload;
     pb_bytes_array_t *firebase_installation_id;
     pb_bytes_array_t *app_quality_session_id;
+    pb_bytes_array_t *firebase_authentication_token;
 /* @@protoc_insertion_point(struct:google_crashlytics_Report) */
 } google_crashlytics_Report;
 
@@ -84,12 +85,13 @@ typedef struct _google_crashlytics_Report {
 #define google_crashlytics_Report_installation_uuid_tag 5
 #define google_crashlytics_Report_firebase_installation_id_tag 16
 #define google_crashlytics_Report_app_quality_session_id_tag 17
+#define google_crashlytics_Report_firebase_authentication_token 19
 #define google_crashlytics_Report_build_version_tag 6
 #define google_crashlytics_Report_display_version_tag 7
 #define google_crashlytics_Report_apple_payload_tag 10
 
 /* Struct field encoding specification for nanopb */
-extern const pb_field_t google_crashlytics_Report_fields[10];
+extern const pb_field_t google_crashlytics_Report_fields[11];
 extern const pb_field_t google_crashlytics_FilesPayload_fields[2];
 extern const pb_field_t google_crashlytics_FilesPayload_File_fields[3];
 

+ 6 - 3
Crashlytics/UnitTests/FIRCLSContextManagerTests.m

@@ -75,7 +75,8 @@ NSString *const TestContextSessionID2 = @"TestContextSessionID2";
   FIRCLSReportAdapter *adapter = [[FIRCLSReportAdapter alloc] initWithPath:self.report.path
                                                                googleAppId:@"TestGoogleAppID"
                                                             installIDModel:self.installIDModel
-                                                                      fiid:@"TestFIID"];
+                                                                      fiid:@"TestFIID"
+                                                                 authToken:@"TestAuthToken"];
 
   XCTAssertEqualObjects(adapter.identity.app_quality_session_id, @"");
 }
@@ -92,7 +93,8 @@ NSString *const TestContextSessionID2 = @"TestContextSessionID2";
   FIRCLSReportAdapter *adapter = [[FIRCLSReportAdapter alloc] initWithPath:self.report.path
                                                                googleAppId:@"TestGoogleAppID"
                                                             installIDModel:self.installIDModel
-                                                                      fiid:@"TestFIID"];
+                                                                      fiid:@"TestFIID"
+                                                                 authToken:@"TestAuthToken"];
   NSLog(@"reportPath: %@", self.report.path);
 
   XCTAssertEqualObjects(adapter.identity.app_quality_session_id, TestContextSessionID2);
@@ -110,7 +112,8 @@ NSString *const TestContextSessionID2 = @"TestContextSessionID2";
   FIRCLSReportAdapter *adapter = [[FIRCLSReportAdapter alloc] initWithPath:self.report.path
                                                                googleAppId:@"TestGoogleAppID"
                                                             installIDModel:self.installIDModel
-                                                                      fiid:@"TestFIID"];
+                                                                      fiid:@"TestFIID"
+                                                                 authToken:@"TestAuthToken"];
   NSLog(@"reportPath: %@", self.report.path);
 
   XCTAssertEqualObjects(adapter.identity.app_quality_session_id, TestContextSessionID2);

+ 49 - 20
Crashlytics/UnitTests/FIRCLSInstallIdentifierModelTests.m

@@ -66,10 +66,13 @@ static NSString *const FIRCLSTestHashOfTestInstanceID =
       [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:iid];
   XCTAssertNotNil(model.installID);
 
-  BOOL didRotate = [model regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid){
-  }];
+  BOOL didRotate = [model
+      regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid, NSString *_Nonnull authToken){
+      }];
   sleep(1);
 
+  XCTAssertTrue(iid.authTokenFinished);
+  XCTAssertTrue(iid.installationIDFinished);
   XCTAssertFalse(didRotate);
   XCTAssertEqualObjects([_defaults objectForKey:FABInstallationUUIDKey], model.installID);
   XCTAssertNil([_defaults objectForKey:FABInstallationADIDKey]);
@@ -85,10 +88,13 @@ static NSString *const FIRCLSTestHashOfTestInstanceID =
       [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:iid];
   XCTAssertNotNil(model.installID);
 
-  BOOL didRotate = [model regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid){
-  }];
+  BOOL didRotate = [model
+      regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid, NSString *_Nonnull authToken){
+      }];
 
   XCTAssertFalse(didRotate);
+  XCTAssertTrue(iid.authTokenFinished);
+  XCTAssertTrue(iid.installationIDFinished);
   XCTAssertEqualObjects([_defaults objectForKey:FABInstallationUUIDKey], model.installID);
   XCTAssertNil([_defaults objectForKey:FABInstallationADIDKey]);
   XCTAssertEqualObjects(nil, [_defaults objectForKey:FIRCLSInstallationIIDHashKey]);
@@ -135,10 +141,13 @@ static NSString *const FIRCLSTestHashOfTestInstanceID =
       [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:iid];
   XCTAssertNotNil(model.installID);
 
-  BOOL didRotate = [model regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid){
-  }];
+  BOOL didRotate = [model
+      regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid, NSString *_Nonnull authToken){
+      }];
   XCTAssertTrue(didRotate);
 
+  XCTAssertTrue(iid.authTokenFinished);
+  XCTAssertTrue(iid.installationIDFinished);
   // Test that the UUID changed.
   XCTAssertNotEqualObjects(model.installID, @"old_uuid");
   XCTAssertEqualObjects([_defaults objectForKey:FABInstallationUUIDKey], model.installID);
@@ -158,10 +167,13 @@ static NSString *const FIRCLSTestHashOfTestInstanceID =
       [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:iid];
   XCTAssertNotNil(model.installID);
 
-  BOOL didRotate = [model regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid){
-  }];
+  BOOL didRotate = [model
+      regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid, NSString *_Nonnull authToken){
+      }];
   XCTAssertFalse(didRotate);
 
+  XCTAssertTrue(iid.authTokenFinished);
+  XCTAssertTrue(iid.installationIDFinished);
   // Test that the UUID changed.
   XCTAssertEqualObjects(model.installID, @"test_uuid");
   XCTAssertEqualObjects([_defaults objectForKey:FABInstallationUUIDKey], model.installID);
@@ -180,10 +192,13 @@ static NSString *const FIRCLSTestHashOfTestInstanceID =
       [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:iid];
   XCTAssertNotNil(model.installID);
 
-  BOOL didRotate = [model regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid){
-  }];
+  BOOL didRotate = [model
+      regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid, NSString *_Nonnull authToken){
+      }];
   XCTAssertFalse(didRotate);
 
+  XCTAssertTrue(iid.authTokenFinished);
+  XCTAssertTrue(iid.installationIDFinished);
   // Test that the UUID did not change. The FIID can be nil if
   // there's no FIID cached, so we can't say whether to regenerate
   XCTAssertEqualObjects(model.installID, @"old_uuid");
@@ -202,10 +217,13 @@ static NSString *const FIRCLSTestHashOfTestInstanceID =
       [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:iid];
   XCTAssertNotNil(model.installID);
 
-  BOOL didRotate = [model regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid){
-  }];
+  BOOL didRotate = [model
+      regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid, NSString *_Nonnull authToken){
+      }];
   XCTAssertFalse(didRotate);
 
+  XCTAssertTrue(iid.authTokenFinished);
+  XCTAssertTrue(iid.installationIDFinished);
   // Test that the UUID did not change. The FIID can be nil if
   // there's no FIID cached, so we can't say whether to regenerate
   XCTAssertEqualObjects(model.installID, @"old_uuid");
@@ -226,10 +244,14 @@ static NSString *const FIRCLSTestHashOfTestInstanceID =
       [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:iid];
   XCTAssertNotNil(model.installID);
 
-  BOOL didRotate = [model regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid){
-  }];
+  BOOL didRotate = [model
+      regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid, NSString *_Nonnull authToken){
+      }];
   XCTAssertFalse(didRotate);
 
+  XCTAssertTrue(iid.authTokenFinished);
+  XCTAssertTrue(iid.installationIDFinished);
+
   // Test that the UUID didn't change.
   XCTAssertEqualObjects(model.installID, @"test_uuid");
   XCTAssertEqualObjects([_defaults objectForKey:FABInstallationUUIDKey], model.installID);
@@ -248,10 +270,13 @@ static NSString *const FIRCLSTestHashOfTestInstanceID =
       [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:iid];
   XCTAssertNotNil(model.installID);
 
-  BOOL didRotate = [model regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid){
-  }];
+  BOOL didRotate = [model
+      regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid, NSString *_Nonnull authToken){
+      }];
   XCTAssertFalse(didRotate);
 
+  XCTAssertTrue(iid.authTokenFinished);
+  XCTAssertTrue(iid.installationIDFinished);
   // Test that the UUID didn't change.
   XCTAssertEqualObjects(model.installID, @"test_uuid");
   XCTAssertEqualObjects([_defaults objectForKey:FABInstallationUUIDKey], model.installID);
@@ -272,8 +297,9 @@ static NSString *const FIRCLSTestHashOfTestInstanceID =
       [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:iid];
   XCTAssertNotNil(model.installID);
 
-  BOOL didRotate = [model regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid){
-  }];
+  BOOL didRotate = [model
+      regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid, NSString *_Nonnull authToken){
+      }];
   XCTAssertFalse(didRotate);
 
   // Test that the UUID didn't change.
@@ -297,10 +323,13 @@ static NSString *const FIRCLSTestHashOfTestInstanceID =
       [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:iid];
   XCTAssertNotNil(model.installID);
 
-  BOOL didRotate = [model regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid){
-  }];
+  BOOL didRotate = [model
+      regenerateInstallIDIfNeededWithBlock:^(NSString *_Nonnull fiid, NSString *_Nonnull authToken){
+      }];
   XCTAssertTrue(didRotate);
 
+  XCTAssertTrue(iid.authTokenFinished);
+  XCTAssertTrue(iid.installationIDFinished);
   // Test that the UUID change.
   XCTAssertNotEqualObjects(model.installID, @"test_uuid");
   XCTAssertEqualObjects([_defaults objectForKey:FABInstallationUUIDKey], model.installID);

+ 3 - 1
Crashlytics/UnitTests/FIRCLSReportAdapterTests.m

@@ -32,6 +32,7 @@
 @end
 
 static NSString *const TestFIID = @"TEST_FIID";
+static NSString *const TestAuthToken = @"TEST_AUTH_TOKEN";
 
 @implementation FIRCLSReportAdapterTests
 
@@ -46,7 +47,8 @@ static NSString *const TestFIID = @"TEST_FIID";
   return [[FIRCLSReportAdapter alloc] initWithPath:path
                                        googleAppId:googleAppID
                                     installIDModel:installIDModel
-                                              fiid:TestFIID];
+                                              fiid:TestFIID
+                                         authToken:TestAuthToken];
 }
 
 /// Attempt sending a proto report to the reporting endpoint

+ 3 - 0
Crashlytics/UnitTests/Mocks/FIRMockInstallations.h

@@ -16,6 +16,9 @@
 
 @interface FIRMockInstallations : FIRInstallations
 
+@property(nonatomic) BOOL authTokenFinished;
+@property(nonatomic) BOOL installationIDFinished;
+
 - (instancetype)initWithFID:(NSString *)installationID;
 - (instancetype)initWithError:(NSError *)error;
 

+ 17 - 0
Crashlytics/UnitTests/Mocks/FIRMockInstallations.m

@@ -21,11 +21,24 @@
 @property(nonatomic, copy) NSString *installationID;
 @property(nonatomic, strong) NSError *error;
 
+// the init function is not public for the token result, use as a placeholder to mock the token
+// completion block
+@property(nonatomic, strong) FIRInstallationsAuthTokenResult *tokenResult;
+
+@property(nonatomic) BOOL authTokenFinished;
+@property(nonatomic) BOOL installationIDFinished;
+
 @end
 
 @implementation FIRMockInstallationsImpl
 
+- (void)authTokenWithCompletion:(FIRInstallationsTokenHandler)completion {
+  self.authTokenFinished = true;
+  completion(self.tokenResult, self.error);
+}
+
 - (void)installationIDWithCompletion:(FIRInstallationsIDHandler)completion {
+  self.installationIDFinished = true;
   completion(self.installationID, self.error);
 }
 
@@ -41,6 +54,8 @@
   FIRMockInstallationsImpl *mock = [[FIRMockInstallationsImpl alloc] init];
   mock.installationID = [installationID copy];
   mock.error = nil;
+  mock.authTokenFinished = false;
+  mock.installationIDFinished = false;
   self = (id)mock;
   return self;
 }
@@ -49,6 +64,8 @@
   FIRMockInstallationsImpl *mock = [[FIRMockInstallationsImpl alloc] init];
   mock.installationID = nil;
   mock.error = error;
+  mock.authTokenFinished = false;
+  mock.installationIDFinished = false;
   self = (id)mock;
   return self;
 }

+ 2 - 0
FirebaseSessions/Sources/Development/DevEventConsoleLogger.swift

@@ -42,6 +42,8 @@ class DevEventConsoleLogger: EventGDTLoggerProtocol {
         session_index: \(proto.session_data.session_index)
         event_timestamp_us: \(proto.session_data.event_timestamp_us)
         firebase_installation_id: \(proto.session_data.firebase_installation_id.description)
+        firebase_autheticationtion_token:
+            \(proto.session_data.firebase_authentication_token.description)
         data_collection_status
           crashlytics: \(proto.session_data.data_collection_status.crashlytics)
           performance: \(proto.session_data.data_collection_status.performance)

+ 4 - 0
FirebaseSessions/Sources/FirebaseSessions.swift

@@ -118,6 +118,10 @@ private enum GoogleDataTransportConfig {
             .logDebug(
               "Data Collection is disabled for all subscribers. Skipping this Session Event"
             )
+        case .SessionInstallationsTimeOutError:
+          Logger.logError(
+            "Error getting Firebase Installation ID due to timeout. Skipping this Session Event"
+          )
         }
       }
     }

+ 2 - 0
FirebaseSessions/Sources/FirebaseSessionsError.swift

@@ -20,6 +20,8 @@ enum FirebaseSessionsError: Error {
   case SessionSamplingError
   /// Firebase Installation ID related error
   case SessionInstallationsError(Error)
+  /// Firebase Installation ID related timeout error
+  case SessionInstallationsTimeOutError
   /// Error from the GoogleDataTransport SDK
   case DataTransportError(Error)
   /// Sessions SDK is disabled via settings error

+ 49 - 5
FirebaseSessions/Sources/Installations+InstallationsProtocol.swift

@@ -16,19 +16,63 @@
 import Foundation
 
 @_implementationOnly import FirebaseInstallations
-
 protocol InstallationsProtocol {
-  func installationID(completion: @escaping (Result<String, Error>) -> Void)
+  var installationsWaitTimeInSecond: Int { get }
+
+  /// Override Installation function for testing
+  func authToken(completion: @escaping (InstallationsAuthTokenResult?, Error?) -> Void)
+
+  /// Override Installation function for testing
+  func installationID(completion: @escaping (String?, Error?) -> Void)
+
+  /// Return a tuple: (installationID, authenticationToken) for success result
+  func installationID(completion: @escaping (Result<(String, String), Error>) -> Void)
 }
 
-extension Installations: InstallationsProtocol {
-  func installationID(completion: @escaping (Result<String, Error>) -> Void) {
+extension InstallationsProtocol {
+  var installationsWaitTimeInSecond: Int {
+    return 10
+  }
+
+  func installationID(completion: @escaping (Result<(String, String), Error>) -> Void) {
+    var authTokenComplete = ""
+    var intallationComplete: String?
+    var errorComplete: Error?
+
+    let workingGroup = DispatchGroup()
+
+    workingGroup.enter()
+    authToken { (authTokenResult: InstallationsAuthTokenResult?, error: Error?) in
+      authTokenComplete = authTokenResult?.authToken ?? ""
+      workingGroup.leave()
+    }
+
+    workingGroup.enter()
     installationID { (installationID: String?, error: Error?) in
       if let installationID = installationID {
-        completion(.success(installationID))
+        intallationComplete = installationID
       } else if let error = error {
+        errorComplete = error
+      }
+      workingGroup.leave()
+    }
+
+    // adding timeout for 10 seconds
+    let result = workingGroup
+      .wait(timeout: .now() + DispatchTimeInterval.seconds(installationsWaitTimeInSecond))
+
+    switch result {
+    case .timedOut:
+      completion(.failure(FirebaseSessionsError.SessionInstallationsTimeOutError))
+      return
+    default:
+      if let installationID = intallationComplete {
+        completion(.success((installationID, authTokenComplete)))
+      } else if let error = errorComplete {
         completion(.failure(error))
       }
     }
   }
 }
+
+extension Installations: InstallationsProtocol {}

+ 4 - 2
FirebaseSessions/Sources/SessionCoordinator.swift

@@ -67,11 +67,13 @@ class SessionCoordinator: SessionCoordinatorProtocol {
                             -> Void) {
     installations.installationID { result in
       switch result {
-      case let .success(fiid):
-        event.setInstallationID(installationId: fiid)
+      case let .success(installationsInfo):
+        event.setInstallationID(installationId: installationsInfo.0)
+        event.setAuthenticationToken(authenticationToken: installationsInfo.1)
         callback(.success(()))
       case let .failure(error):
         event.setInstallationID(installationId: "")
+        event.setAuthenticationToken(authenticationToken: "")
         callback(.failure(FirebaseSessionsError.SessionInstallationsError(error)))
       }
     }

+ 7 - 0
FirebaseSessions/Sources/SessionStartEvent.swift

@@ -91,6 +91,7 @@ class SessionStartEvent: NSObject, GDTCOREventDataObject {
       proto.application_info.session_sdk_version,
       proto.session_data.session_id,
       proto.session_data.firebase_installation_id,
+      proto.session_data.firebase_authentication_token,
       proto.session_data.first_session_id,
     ]
     for pointer in garbage {
@@ -104,6 +105,12 @@ class SessionStartEvent: NSObject, GDTCOREventDataObject {
     nanopb_free(oldID)
   }
 
+  func setAuthenticationToken(authenticationToken: String) {
+    let oldToken = proto.session_data.firebase_authentication_token
+    proto.session_data.firebase_authentication_token = makeProtoString(authenticationToken)
+    nanopb_free(oldToken)
+  }
+
   func setSamplingRate(samplingRate: Double) {
     proto.session_data.data_collection_status.session_sampling_rate = samplingRate
   }

+ 2 - 2
FirebaseSessions/Sources/Settings/SettingsDownloadClient.swift

@@ -53,8 +53,8 @@ class SettingsDownloader: SettingsDownloadClient {
 
     installations.installationID { result in
       switch result {
-      case let .success(fiid):
-        let request = self.buildRequest(url: validURL, fiid: fiid)
+      case let .success(installationsInfo):
+        let request = self.buildRequest(url: validURL, fiid: installationsInfo.0)
         let task = URLSession.shared.dataTask(with: request) { data, response, error in
           if let data = data {
             if let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {

+ 2 - 1
FirebaseSessions/SourcesObjC/Protogen/nanopb/sessions.nanopb.c

@@ -39,13 +39,14 @@ const pb_field_t firebase_appquality_sessions_NetworkConnectionInfo_fields[3] =
     PB_LAST_FIELD
 };
 
-const pb_field_t firebase_appquality_sessions_SessionInfo_fields[7] = {
+const pb_field_t firebase_appquality_sessions_SessionInfo_fields[8] = {
     PB_FIELD(  1, BYTES   , SINGULAR, POINTER , FIRST, firebase_appquality_sessions_SessionInfo, session_id, session_id, 0),
     PB_FIELD(  3, BYTES   , SINGULAR, POINTER , OTHER, firebase_appquality_sessions_SessionInfo, firebase_installation_id, session_id, 0),
     PB_FIELD(  4, INT64   , SINGULAR, STATIC  , OTHER, firebase_appquality_sessions_SessionInfo, event_timestamp_us, firebase_installation_id, 0),
     PB_FIELD(  6, MESSAGE , SINGULAR, STATIC  , OTHER, firebase_appquality_sessions_SessionInfo, data_collection_status, event_timestamp_us, &firebase_appquality_sessions_DataCollectionStatus_fields),
     PB_FIELD(  7, BYTES   , SINGULAR, POINTER , OTHER, firebase_appquality_sessions_SessionInfo, first_session_id, data_collection_status, 0),
     PB_FIELD(  8, INT32   , SINGULAR, STATIC  , OTHER, firebase_appquality_sessions_SessionInfo, session_index, first_session_id, 0),
+    PB_FIELD(  9, BYTES   , SINGULAR, POINTER , OTHER, firebase_appquality_sessions_SessionInfo, firebase_authentication_token, session_index, 0),
     PB_LAST_FIELD
 };
 

+ 3 - 1
FirebaseSessions/SourcesObjC/Protogen/nanopb/sessions.nanopb.h

@@ -161,6 +161,7 @@ typedef struct _firebase_appquality_sessions_SessionInfo {
     firebase_appquality_sessions_DataCollectionStatus data_collection_status;
     pb_bytes_array_t *first_session_id;
     int32_t session_index;
+    pb_bytes_array_t *firebase_authentication_token;
 /* @@protoc_insertion_point(struct:firebase_appquality_sessions_SessionInfo) */
 } firebase_appquality_sessions_SessionInfo;
 
@@ -224,6 +225,7 @@ typedef struct _firebase_appquality_sessions_SessionEvent {
 #define firebase_appquality_sessions_SessionInfo_firebase_installation_id_tag 3
 #define firebase_appquality_sessions_SessionInfo_event_timestamp_us_tag 4
 #define firebase_appquality_sessions_SessionInfo_data_collection_status_tag 6
+#define firebase_appquality_sessions_SessionInfo_firebase_authentication_token_tag 9
 #define firebase_appquality_sessions_ApplicationInfo_android_app_info_tag 5
 #define firebase_appquality_sessions_ApplicationInfo_apple_app_info_tag 6
 #define firebase_appquality_sessions_ApplicationInfo_app_id_tag 1
@@ -240,7 +242,7 @@ typedef struct _firebase_appquality_sessions_SessionEvent {
 /* Struct field encoding specification for nanopb */
 extern const pb_field_t firebase_appquality_sessions_SessionEvent_fields[4];
 extern const pb_field_t firebase_appquality_sessions_NetworkConnectionInfo_fields[3];
-extern const pb_field_t firebase_appquality_sessions_SessionInfo_fields[7];
+extern const pb_field_t firebase_appquality_sessions_SessionInfo_fields[8];
 extern const pb_field_t firebase_appquality_sessions_DataCollectionStatus_fields[4];
 extern const pb_field_t firebase_appquality_sessions_ApplicationInfo_fields[10];
 extern const pb_field_t firebase_appquality_sessions_AndroidApplicationInfo_fields[3];

+ 18 - 3
FirebaseSessions/Tests/Unit/Mocks/MockInstallationsProtocol.swift

@@ -19,9 +19,24 @@
 
 class MockInstallationsProtocol: InstallationsProtocol {
   static let testInstallationId = "testInstallationId"
-  var result: Result<String, Error> = .success(testInstallationId)
+  static let testAuthToken = "testAuthToken"
+  var result: Result<(String, String), Error> = .success((testInstallationId, testAuthToken))
+  var installationIdFinished = false
+  var authTokenFinished = false
 
-  func installationID(completion: @escaping (Result<String, Error>) -> Void) {
-    completion(result)
+  func installationID(completion: @escaping (String?, Error?) -> Void) {
+    installationIdFinished = true
+    switch result {
+    case let .success(success):
+      completion(success.0, nil)
+    case let .failure(failure):
+      completion(nil, failure)
+    }
+  }
+
+  func authToken(completion: @escaping (InstallationsAuthTokenResult?, Error?) -> Void) {
+    Thread.sleep(forTimeInterval: 0.1)
+    authTokenFinished = true
+    completion(nil, nil)
   }
 }

+ 16 - 0
FirebaseSessions/Tests/Unit/SessionCoordinatorTests.swift

@@ -34,6 +34,11 @@ class SessionCoordinatorTests: XCTestCase {
     )
   }
 
+  override func tearDown() {
+    installations.authTokenFinished = false
+    installations.installationIdFinished = false
+  }
+
   var defaultSessionInfo: SessionInfo {
     return SessionInfo(
       sessionId: "test_session_id",
@@ -61,6 +66,9 @@ class SessionCoordinatorTests: XCTestCase {
       fieldName: "installation_id"
     )
 
+    XCTAssertTrue(installations.authTokenFinished)
+    XCTAssertTrue(installations.installationIdFinished)
+
     // We should have logged successfully
     XCTAssertEqual(fireLogger.loggedEvent, event)
     XCTAssert(resultSuccess)
@@ -82,6 +90,9 @@ class SessionCoordinatorTests: XCTestCase {
       }
     }
 
+    XCTAssertTrue(installations.authTokenFinished)
+    XCTAssertTrue(installations.installationIdFinished)
+
     // Make sure we've set the Installation ID
     assertEqualProtoString(
       event.proto.session_data.firebase_installation_id,
@@ -110,6 +121,8 @@ class SessionCoordinatorTests: XCTestCase {
       }
     }
 
+    XCTAssertTrue(installations.authTokenFinished)
+    XCTAssertTrue(installations.installationIdFinished)
     // We should have logged the event, but with a failed result
     XCTAssertNotNil(fireLogger.loggedEvent)
     XCTAssertFalse(resultSuccess)
@@ -138,6 +151,9 @@ class SessionCoordinatorTests: XCTestCase {
       }
     }
 
+    XCTAssertTrue(installations.authTokenFinished)
+    XCTAssertTrue(installations.installationIdFinished)
+
     // Make sure we've set the Installation ID to empty because the FIID
     // fetch failed
     assertEqualProtoString(