Skip to content

Commit bb1177c

Browse files
authored
Implement platform logging for App Check (#2619)
* Implement platform logging. * Update tests.
1 parent ae512bc commit bb1177c

File tree

8 files changed

+158
-72
lines changed

8 files changed

+158
-72
lines changed

appcheck/firebase-appcheck-debug/src/main/java/com/google/firebase/appcheck/debug/internal/DebugAppCheckProvider.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,7 @@ public class DebugAppCheckProvider implements AppCheckProvider {
4545

4646
public DebugAppCheckProvider(@NonNull FirebaseApp firebaseApp, @Nullable String debugSecret) {
4747
checkNotNull(firebaseApp);
48-
String projectId = firebaseApp.getOptions().getProjectId();
49-
if (projectId == null) {
50-
throw new IllegalArgumentException("FirebaseOptions#getProjectId cannot be null.");
51-
}
52-
53-
this.networkClient =
54-
new NetworkClient(
55-
firebaseApp.getOptions().getApiKey(),
56-
firebaseApp.getOptions().getApplicationId(),
57-
projectId);
48+
this.networkClient = new NetworkClient(firebaseApp);
5849
this.backgroundExecutor = Executors.newCachedThreadPool();
5950
this.debugSecretTask =
6051
debugSecret == null

appcheck/firebase-appcheck-safetynet/src/main/java/com/google/firebase/appcheck/safetynet/internal/SafetyNetAppCheckProvider.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,9 @@ public SafetyNetAppCheckProvider(@NonNull FirebaseApp firebaseApp) {
6767
checkNotNull(googleApiAvailability);
6868
this.context = firebaseApp.getApplicationContext();
6969
this.apiKey = firebaseApp.getOptions().getApiKey();
70-
String appId = firebaseApp.getOptions().getApplicationId();
71-
String projectId = firebaseApp.getOptions().getProjectId();
72-
if (projectId == null) {
73-
throw new IllegalArgumentException("FirebaseOptions#getProjectId cannot be null.");
74-
}
7570
this.backgroundExecutor = backgroundExecutor;
7671
this.safetyNetClientTask = initSafetyNetClient(googleApiAvailability, this.backgroundExecutor);
77-
this.networkClient = new NetworkClient(apiKey, appId, projectId);
72+
this.networkClient = new NetworkClient(firebaseApp);
7873
}
7974

8075
@VisibleForTesting

appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/FirebaseAppCheckRegistrar.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
import com.google.firebase.components.Component;
2222
import com.google.firebase.components.ComponentRegistrar;
2323
import com.google.firebase.components.Dependency;
24+
import com.google.firebase.heartbeatinfo.HeartBeatInfo;
25+
import com.google.firebase.platforminfo.LibraryVersionComponent;
26+
import com.google.firebase.platforminfo.UserAgentPublisher;
2427
import java.util.Arrays;
2528
import java.util.List;
2629

@@ -38,8 +41,16 @@ public List<Component<?>> getComponents() {
3841
return Arrays.asList(
3942
Component.builder(FirebaseAppCheck.class, (InternalAppCheckTokenProvider.class))
4043
.add(Dependency.required(FirebaseApp.class))
41-
.factory((container) -> new DefaultFirebaseAppCheck(container.get(FirebaseApp.class)))
44+
.add(Dependency.optionalProvider(UserAgentPublisher.class))
45+
.add(Dependency.optionalProvider(HeartBeatInfo.class))
46+
.factory(
47+
(container) ->
48+
new DefaultFirebaseAppCheck(
49+
container.get(FirebaseApp.class),
50+
container.getProvider(UserAgentPublisher.class),
51+
container.getProvider(HeartBeatInfo.class)))
4252
.alwaysEager()
43-
.build());
53+
.build(),
54+
LibraryVersionComponent.create("fire-app-check", BuildConfig.VERSION_NAME));
4455
}
4556
}

appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/DefaultFirebaseAppCheck.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,20 @@
3131
import com.google.firebase.appcheck.FirebaseAppCheck;
3232
import com.google.firebase.appcheck.internal.util.Clock;
3333
import com.google.firebase.appcheck.interop.AppCheckTokenListener;
34+
import com.google.firebase.heartbeatinfo.HeartBeatInfo;
35+
import com.google.firebase.inject.Provider;
36+
import com.google.firebase.platforminfo.UserAgentPublisher;
3437
import java.util.ArrayList;
3538
import java.util.List;
3639

3740
public class DefaultFirebaseAppCheck extends FirebaseAppCheck {
3841

3942
private static final long BUFFER_TIME_MILLIS = 5 * 60 * 1000; // 5 minutes in milliseconds
43+
private static final String HEART_BEAT_STORAGE_TAG = "fire-app-check";
4044

4145
private final FirebaseApp firebaseApp;
46+
private final Provider<UserAgentPublisher> userAgentPublisherProvider;
47+
private final Provider<HeartBeatInfo> heartBeatInfoProvider;
4248
private final List<AppCheckTokenListener> appCheckTokenListenerList;
4349
private final StorageHelper storageHelper;
4450
private final TokenRefreshManager tokenRefreshManager;
@@ -48,9 +54,16 @@ public class DefaultFirebaseAppCheck extends FirebaseAppCheck {
4854
private AppCheckProvider appCheckProvider;
4955
private AppCheckToken cachedToken;
5056

51-
public DefaultFirebaseAppCheck(@NonNull FirebaseApp firebaseApp) {
57+
public DefaultFirebaseAppCheck(
58+
@NonNull FirebaseApp firebaseApp,
59+
@NonNull Provider<UserAgentPublisher> userAgentPublisherProvider,
60+
@NonNull Provider<HeartBeatInfo> heartBeatInfoProvider) {
5261
checkNotNull(firebaseApp);
62+
checkNotNull(userAgentPublisherProvider);
63+
checkNotNull(heartBeatInfoProvider);
5364
this.firebaseApp = firebaseApp;
65+
this.userAgentPublisherProvider = userAgentPublisherProvider;
66+
this.heartBeatInfoProvider = heartBeatInfoProvider;
5467
this.appCheckTokenListenerList = new ArrayList<>();
5568
this.storageHelper =
5669
new StorageHelper(firebaseApp.getApplicationContext(), firebaseApp.getPersistenceKey());
@@ -155,6 +168,21 @@ public Task<AppCheckTokenResult> then(@NonNull Task<AppCheckToken> task) {
155168
});
156169
}
157170

171+
@Nullable
172+
String getUserAgent() {
173+
return userAgentPublisherProvider.get() != null
174+
? userAgentPublisherProvider.get().getUserAgent()
175+
: null;
176+
}
177+
178+
@Nullable
179+
String getHeartbeatCode() {
180+
return heartBeatInfoProvider.get() != null
181+
? Integer.toString(
182+
heartBeatInfoProvider.get().getHeartBeatCode(HEART_BEAT_STORAGE_TAG).getCode())
183+
: null;
184+
}
185+
158186
/** Sets the in-memory cached {@link AppCheckToken}. */
159187
@VisibleForTesting
160188
void setCachedToken(@NonNull AppCheckToken token) {

appcheck/firebase-appcheck/src/main/java/com/google/firebase/appcheck/internal/NetworkClient.java

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
import androidx.annotation.IntDef;
2020
import androidx.annotation.NonNull;
2121
import androidx.annotation.VisibleForTesting;
22+
import com.google.firebase.FirebaseApp;
2223
import com.google.firebase.FirebaseException;
23-
import com.google.firebase.appcheck.BuildConfig;
24+
import com.google.firebase.appcheck.FirebaseAppCheck;
2425
import java.io.BufferedOutputStream;
2526
import java.io.BufferedReader;
2627
import java.io.IOException;
@@ -46,9 +47,10 @@ public class NetworkClient {
4647
private static final String CONTENT_TYPE = "Content-Type";
4748
private static final String APPLICATION_JSON = "application/json";
4849
private static final String UTF_8 = "UTF-8";
49-
private static final String X_FIREBASE_CLIENT = "X-Firebase-Client";
50-
private static final String PLATFORM_NAME = "fire-app-check";
50+
@VisibleForTesting static final String X_FIREBASE_CLIENT = "X-Firebase-Client";
51+
@VisibleForTesting static final String X_FIREBASE_CLIENT_LOG_TYPE = "X-Firebase-Client-Log-Type";
5152

53+
private final DefaultFirebaseAppCheck firebaseAppCheck;
5254
private final String apiKey;
5355
private final String appId;
5456
private final String projectId;
@@ -61,13 +63,15 @@ public class NetworkClient {
6163
public static final int SAFETY_NET = 1;
6264
public static final int DEBUG = 2;
6365

64-
public NetworkClient(@NonNull String apiKey, @NonNull String appId, @NonNull String projectId) {
65-
checkNotNull(apiKey);
66-
checkNotNull(appId);
67-
checkNotNull(projectId);
68-
this.apiKey = apiKey;
69-
this.appId = appId;
70-
this.projectId = projectId;
66+
public NetworkClient(@NonNull FirebaseApp firebaseApp) {
67+
checkNotNull(firebaseApp);
68+
this.firebaseAppCheck = (DefaultFirebaseAppCheck) FirebaseAppCheck.getInstance(firebaseApp);
69+
this.apiKey = firebaseApp.getOptions().getApiKey();
70+
this.appId = firebaseApp.getOptions().getApplicationId();
71+
this.projectId = firebaseApp.getOptions().getProjectId();
72+
if (projectId == null) {
73+
throw new IllegalArgumentException("FirebaseOptions#getProjectId cannot be null.");
74+
}
7175
}
7276

7377
/**
@@ -85,8 +89,13 @@ public AppCheckTokenResponse exchangeAttestationForAppCheckToken(
8589
urlConnection.setDoOutput(true);
8690
urlConnection.setFixedLengthStreamingMode(requestBytes.length);
8791
urlConnection.setRequestProperty(CONTENT_TYPE, APPLICATION_JSON);
88-
urlConnection.setRequestProperty(
89-
X_FIREBASE_CLIENT, PLATFORM_NAME + "/" + BuildConfig.VERSION_NAME);
92+
if (firebaseAppCheck.getUserAgent() != null) {
93+
urlConnection.setRequestProperty(X_FIREBASE_CLIENT, firebaseAppCheck.getUserAgent());
94+
}
95+
if (firebaseAppCheck.getHeartbeatCode() != null) {
96+
urlConnection.setRequestProperty(
97+
X_FIREBASE_CLIENT_LOG_TYPE, firebaseAppCheck.getHeartbeatCode());
98+
}
9099

91100
try (OutputStream out =
92101
new BufferedOutputStream(urlConnection.getOutputStream(), requestBytes.length)) {

appcheck/firebase-appcheck/src/test/java/com/google/firebase/appcheck/FirebaseAppCheckRegistrarTest.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import com.google.firebase.FirebaseApp;
2020
import com.google.firebase.components.Component;
2121
import com.google.firebase.components.Dependency;
22+
import com.google.firebase.heartbeatinfo.HeartBeatInfo;
23+
import com.google.firebase.platforminfo.UserAgentPublisher;
2224
import java.util.List;
2325
import org.junit.Test;
2426
import org.junit.runner.RunWith;
@@ -33,10 +35,13 @@ public void testGetComponents() {
3335
FirebaseAppCheckRegistrar firebaseAppCheckRegistrar = new FirebaseAppCheckRegistrar();
3436
List<Component<?>> components = firebaseAppCheckRegistrar.getComponents();
3537
assertThat(components).isNotEmpty();
36-
assertThat(components).hasSize(1);
38+
assertThat(components).hasSize(2);
3739
Component<?> firebaseAppCheckComponent = components.get(0);
3840
assertThat(firebaseAppCheckComponent.getDependencies())
39-
.containsExactly(Dependency.required(FirebaseApp.class));
41+
.containsExactly(
42+
Dependency.required(FirebaseApp.class),
43+
Dependency.optionalProvider(UserAgentPublisher.class),
44+
Dependency.optionalProvider(HeartBeatInfo.class));
4045
assertThat(firebaseAppCheckComponent.isAlwaysEager()).isTrue();
4146
}
4247
}

appcheck/firebase-appcheck/src/test/java/com/google/firebase/appcheck/internal/DefaultFirebaseAppCheckTest.java

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import com.google.firebase.appcheck.AppCheckProviderFactory;
3030
import com.google.firebase.appcheck.AppCheckTokenResult;
3131
import com.google.firebase.appcheck.interop.AppCheckTokenListener;
32+
import com.google.firebase.heartbeatinfo.HeartBeatInfo;
33+
import com.google.firebase.platforminfo.UserAgentPublisher;
3234
import org.junit.Before;
3335
import org.junit.Test;
3436
import org.junit.runner.RunWith;
@@ -53,8 +55,11 @@ public class DefaultFirebaseAppCheckTest {
5355
@Mock private AppCheckProviderFactory mockAppCheckProviderFactory;
5456
@Mock private AppCheckProvider mockAppCheckProvider;
5557
@Mock private AppCheckTokenListener mockAppCheckTokenListener;
58+
@Mock private UserAgentPublisher mockUserAgentPublisher;
59+
@Mock private HeartBeatInfo mockHeartBeatInfo;
5660

5761
private DefaultAppCheckToken validDefaultAppCheckToken;
62+
private DefaultFirebaseAppCheck defaultFirebaseAppCheck;
5863

5964
@Before
6065
public void setup() {
@@ -66,20 +71,41 @@ public void setup() {
6671
when(mockFirebaseApp.getPersistenceKey()).thenReturn(PERSISTENCE_KEY);
6772
when(mockAppCheckProviderFactory.create(any())).thenReturn(mockAppCheckProvider);
6873
when(mockAppCheckProvider.getToken()).thenReturn(Tasks.forResult(validDefaultAppCheckToken));
74+
75+
defaultFirebaseAppCheck =
76+
new DefaultFirebaseAppCheck(
77+
mockFirebaseApp, () -> mockUserAgentPublisher, () -> mockHeartBeatInfo);
6978
}
7079

7180
@Test
7281
public void testConstructor_nullFirebaseApp_expectThrows() {
7382
assertThrows(
7483
NullPointerException.class,
7584
() -> {
76-
new DefaultFirebaseAppCheck(null);
85+
new DefaultFirebaseAppCheck(null, () -> mockUserAgentPublisher, () -> mockHeartBeatInfo);
86+
});
87+
}
88+
89+
@Test
90+
public void testConstructor_nullUserAgentPublisherProvider_expectThrows() {
91+
assertThrows(
92+
NullPointerException.class,
93+
() -> {
94+
new DefaultFirebaseAppCheck(mockFirebaseApp, null, () -> mockHeartBeatInfo);
95+
});
96+
}
97+
98+
@Test
99+
public void testConstructor_nullHeartBeatInfoProvider_expectThrows() {
100+
assertThrows(
101+
NullPointerException.class,
102+
() -> {
103+
new DefaultFirebaseAppCheck(mockFirebaseApp, () -> mockUserAgentPublisher, null);
77104
});
78105
}
79106

80107
@Test
81108
public void testInstallAppCheckFactory_nullFactory_expectThrows() {
82-
DefaultFirebaseAppCheck defaultFirebaseAppCheck = new DefaultFirebaseAppCheck(mockFirebaseApp);
83109
assertThrows(
84110
NullPointerException.class,
85111
() -> {
@@ -89,7 +115,6 @@ public void testInstallAppCheckFactory_nullFactory_expectThrows() {
89115

90116
@Test
91117
public void testAddAppCheckTokenListener_nullListener_expectThrows() {
92-
DefaultFirebaseAppCheck defaultFirebaseAppCheck = new DefaultFirebaseAppCheck(mockFirebaseApp);
93118
assertThrows(
94119
NullPointerException.class,
95120
() -> {
@@ -99,7 +124,6 @@ public void testAddAppCheckTokenListener_nullListener_expectThrows() {
99124

100125
@Test
101126
public void testRemoveAppCheckTokenListener_nullListener_expectThrows() {
102-
DefaultFirebaseAppCheck defaultFirebaseAppCheck = new DefaultFirebaseAppCheck(mockFirebaseApp);
103127
assertThrows(
104128
NullPointerException.class,
105129
() -> {
@@ -109,7 +133,6 @@ public void testRemoveAppCheckTokenListener_nullListener_expectThrows() {
109133

110134
@Test
111135
public void testGetToken_noFactoryInstalled_returnResultWithError() throws Exception {
112-
DefaultFirebaseAppCheck defaultFirebaseAppCheck = new DefaultFirebaseAppCheck(mockFirebaseApp);
113136
Task<AppCheckTokenResult> tokenTask =
114137
defaultFirebaseAppCheck.getToken(/* forceRefresh= */ false);
115138
assertThat(tokenTask.isComplete()).isTrue();
@@ -120,7 +143,6 @@ public void testGetToken_noFactoryInstalled_returnResultWithError() throws Excep
120143

121144
@Test
122145
public void testGetToken_factoryInstalled_proxiesToAppCheckFactory() {
123-
DefaultFirebaseAppCheck defaultFirebaseAppCheck = new DefaultFirebaseAppCheck(mockFirebaseApp);
124146
defaultFirebaseAppCheck.installAppCheckProviderFactory(mockAppCheckProviderFactory);
125147

126148
defaultFirebaseAppCheck.getToken(/* forceRefresh= */ false);
@@ -130,14 +152,11 @@ public void testGetToken_factoryInstalled_proxiesToAppCheckFactory() {
130152

131153
@Test
132154
public void testGetInstalledAppCheckProviderFactory_noFactoryInstalled_returnsNull() {
133-
DefaultFirebaseAppCheck defaultFirebaseAppCheck = new DefaultFirebaseAppCheck(mockFirebaseApp);
134-
135155
assertThat(defaultFirebaseAppCheck.getInstalledAppCheckProviderFactory()).isNull();
136156
}
137157

138158
@Test
139159
public void testGetInstalledAppCheckProviderFactory_factoryInstalled_returnsFactory() {
140-
DefaultFirebaseAppCheck defaultFirebaseAppCheck = new DefaultFirebaseAppCheck(mockFirebaseApp);
141160
defaultFirebaseAppCheck.installAppCheckProviderFactory(mockAppCheckProviderFactory);
142161

143162
assertThat(defaultFirebaseAppCheck.getInstalledAppCheckProviderFactory())
@@ -146,7 +165,6 @@ public void testGetInstalledAppCheckProviderFactory_factoryInstalled_returnsFact
146165

147166
@Test
148167
public void testGetToken_factoryInstalledAndListenerRegistered_triggersListenerOnSuccess() {
149-
DefaultFirebaseAppCheck defaultFirebaseAppCheck = new DefaultFirebaseAppCheck(mockFirebaseApp);
150168
defaultFirebaseAppCheck.installAppCheckProviderFactory(mockAppCheckProviderFactory);
151169
defaultFirebaseAppCheck.addAppCheckTokenListener(mockAppCheckTokenListener);
152170

@@ -162,7 +180,6 @@ public void testGetToken_factoryInstalledAndListenerRegistered_triggersListenerO
162180

163181
@Test
164182
public void testGetToken_factoryInstalledAndListenerRegistered_doesNotTriggerListenerOnFailure() {
165-
DefaultFirebaseAppCheck defaultFirebaseAppCheck = new DefaultFirebaseAppCheck(mockFirebaseApp);
166183
defaultFirebaseAppCheck.installAppCheckProviderFactory(mockAppCheckProviderFactory);
167184
defaultFirebaseAppCheck.addAppCheckTokenListener(mockAppCheckTokenListener);
168185

@@ -177,7 +194,6 @@ public void testGetToken_factoryInstalledAndListenerRegistered_doesNotTriggerLis
177194

178195
@Test
179196
public void testGetToken_existingValidToken_triggersListenerUponAdding() {
180-
DefaultFirebaseAppCheck defaultFirebaseAppCheck = new DefaultFirebaseAppCheck(mockFirebaseApp);
181197
defaultFirebaseAppCheck.setCachedToken(validDefaultAppCheckToken);
182198

183199
defaultFirebaseAppCheck.addAppCheckTokenListener(mockAppCheckTokenListener);
@@ -193,8 +209,6 @@ public void testGetToken_existingValidToken_triggersListenerUponAdding() {
193209
public void testGetToken_existingInvalidToken_doesNotTriggerListenerUponAdding() {
194210
DefaultAppCheckToken invalidDefaultAppCheckToken =
195211
new DefaultAppCheckToken(TOKEN_PAYLOAD, EXPIRES_NOW);
196-
197-
DefaultFirebaseAppCheck defaultFirebaseAppCheck = new DefaultFirebaseAppCheck(mockFirebaseApp);
198212
defaultFirebaseAppCheck.setCachedToken(invalidDefaultAppCheckToken);
199213

200214
defaultFirebaseAppCheck.addAppCheckTokenListener(mockAppCheckTokenListener);
@@ -204,7 +218,6 @@ public void testGetToken_existingInvalidToken_doesNotTriggerListenerUponAdding()
204218

205219
@Test
206220
public void testGetToken_existingValidToken_doesNotRequestNewToken() {
207-
DefaultFirebaseAppCheck defaultFirebaseAppCheck = new DefaultFirebaseAppCheck(mockFirebaseApp);
208221
defaultFirebaseAppCheck.setCachedToken(validDefaultAppCheckToken);
209222
defaultFirebaseAppCheck.installAppCheckProviderFactory(mockAppCheckProviderFactory);
210223

@@ -215,7 +228,6 @@ public void testGetToken_existingValidToken_doesNotRequestNewToken() {
215228

216229
@Test
217230
public void testGetToken_existingValidToken_forceRefresh_requestsNewToken() {
218-
DefaultFirebaseAppCheck defaultFirebaseAppCheck = new DefaultFirebaseAppCheck(mockFirebaseApp);
219231
defaultFirebaseAppCheck.setCachedToken(validDefaultAppCheckToken);
220232
defaultFirebaseAppCheck.installAppCheckProviderFactory(mockAppCheckProviderFactory);
221233

@@ -228,8 +240,6 @@ public void testGetToken_existingValidToken_forceRefresh_requestsNewToken() {
228240
public void testGetToken_existingInvalidToken_requestsNewToken() {
229241
DefaultAppCheckToken invalidDefaultAppCheckToken =
230242
new DefaultAppCheckToken(TOKEN_PAYLOAD, EXPIRES_NOW);
231-
232-
DefaultFirebaseAppCheck defaultFirebaseAppCheck = new DefaultFirebaseAppCheck(mockFirebaseApp);
233243
defaultFirebaseAppCheck.setCachedToken(invalidDefaultAppCheckToken);
234244
defaultFirebaseAppCheck.installAppCheckProviderFactory(mockAppCheckProviderFactory);
235245

0 commit comments

Comments
 (0)