Skip to content

Commit 71a4740

Browse files
authored
Send the FIS auth token in the RC fetch headers. (#1646)
1 parent 59db590 commit 71a4740

File tree

7 files changed

+80
-45
lines changed

7 files changed

+80
-45
lines changed

firebase-config/ktx/src/test/kotlin/com/google/firebase/remoteconfig/ktx/RemoteConfigTests.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ import org.robolectric.RuntimeEnvironment
3939
import org.mockito.Mockito.mock
4040
import org.mockito.Mockito.`when`
4141

42-
const val APP_ID = "APP_ID"
43-
const val API_KEY = "API_KEY"
42+
const val APP_ID = "1:14368190084:android:09cb977358c6f241"
43+
const val API_KEY = "AIzaSyabcdefghijklmnopqrstuvwxyz1234567"
4444

4545
const val EXISTING_APP = "existing"
4646

firebase-config/src/androidTest/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigIntegrationTest.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,9 @@
5050

5151
@RunWith(AndroidJUnit4.class)
5252
public class FirebaseRemoteConfigIntegrationTest {
53-
private static final String API_KEY = "api_key";
53+
private static final String API_KEY = "AIzaSyabcdefghijklmnopqrstuvwxyz1234567";
5454
private static final String APP_ID = "1:14368190084:android:09cb977358c6f241";
55+
private static final String PROJECT_ID = "fake-frc-test-id";
5556

5657
@Mock private ConfigCacheClient mockFetchedCache;
5758
@Mock private ConfigCacheClient mockActivatedCache;
@@ -84,7 +85,11 @@ public void setUp() {
8485
FirebaseApp firebaseApp =
8586
FirebaseApp.initializeApp(
8687
context,
87-
new FirebaseOptions.Builder().setApiKey(API_KEY).setApplicationId(APP_ID).build());
88+
new FirebaseOptions.Builder()
89+
.setApiKey(API_KEY)
90+
.setApplicationId(APP_ID)
91+
.setProjectId(PROJECT_ID)
92+
.build());
8893

8994
// Catch all to avoid NPEs (the getters should never return null).
9095
when(mockFetchedCache.get()).thenReturn(Tasks.forResult(null));

firebase-config/src/main/java/com/google/firebase/remoteconfig/RemoteConfigConstants.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ public final class RemoteConfigConstants {
3131
/**
3232
* Keys of fields in the Fetch request body that the client sends to the Firebase Remote Config
3333
* server.
34+
*
35+
* <p>{@code INSTANCE_ID} and {@code INSTANCE_ID_TOKEN} are legacy names for the fields that used
36+
* to be populated by the IID SDK. The fields have been replaced by the installation ID and
37+
* installation auth token, respectively, which are fetched from the FIS SDK.
3438
*/
3539
@StringDef({
3640
RequestFieldKey.INSTANCE_ID,

firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigFetchHttpClient.java

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ public class ConfigFetchHttpClient {
7878
private static final String X_ANDROID_PACKAGE_HEADER = "X-Android-Package";
7979
private static final String X_ANDROID_CERT_HEADER = "X-Android-Cert";
8080
private static final String X_GOOGLE_GFE_CAN_RETRY = "X-Google-GFE-Can-Retry";
81+
private static final String INSTALLATIONS_AUTH_TOKEN_HEADER =
82+
"X-Goog-Firebase-Installations-Auth";
8183

8284
private final Context context;
8385
private final String appId;
@@ -149,9 +151,9 @@ HttpURLConnection createHttpURLConnection() throws FirebaseRemoteConfigException
149151
*
150152
* @param urlConnection a {@link HttpURLConnection} created by a call to {@link
151153
* #createHttpURLConnection}.
152-
* @param instanceId the Firebase Instance ID that identifies a Firebase App Instance.
153-
* @param instanceIdToken a valid Firebase Instance ID Token that authenticates a Firebase App
154-
* Instance.
154+
* @param installationId the Firebase installation ID that identifies a Firebase App Instance.
155+
* @param installationAuthToken a valid Firebase installation auth token that authenticates a
156+
* Firebase App Instance.
155157
* @param analyticsUserProperties a map of Google Analytics User Properties and the device's
156158
* corresponding values.
157159
* @param lastFetchETag the ETag returned by the last successful fetch call to the FRC server. The
@@ -164,20 +166,20 @@ HttpURLConnection createHttpURLConnection() throws FirebaseRemoteConfigException
164166
@Keep
165167
FetchResponse fetch(
166168
HttpURLConnection urlConnection,
167-
String instanceId,
168-
String instanceIdToken,
169+
String installationId,
170+
String installationAuthToken,
169171
Map<String, String> analyticsUserProperties,
170172
String lastFetchETag,
171173
Map<String, String> customHeaders,
172174
Date currentTime)
173175
throws FirebaseRemoteConfigException {
174-
setUpUrlConnection(urlConnection, lastFetchETag, customHeaders);
176+
setUpUrlConnection(urlConnection, lastFetchETag, installationAuthToken, customHeaders);
175177

176178
String fetchResponseETag;
177179
JSONObject fetchResponse;
178180
try {
179181
byte[] requestBody =
180-
createFetchRequestBody(instanceId, instanceIdToken, analyticsUserProperties)
182+
createFetchRequestBody(installationId, installationAuthToken, analyticsUserProperties)
181183
.toString()
182184
.getBytes("utf-8");
183185
setFetchRequestBody(urlConnection, requestBody);
@@ -212,7 +214,10 @@ FetchResponse fetch(
212214
}
213215

214216
private void setUpUrlConnection(
215-
HttpURLConnection urlConnection, String lastFetchEtag, Map<String, String> customHeaders) {
217+
HttpURLConnection urlConnection,
218+
String lastFetchEtag,
219+
String installationAuthToken,
220+
Map<String, String> customHeaders) {
216221
urlConnection.setDoOutput(true);
217222
urlConnection.setConnectTimeout((int) SECONDS.toMillis(connectTimeoutInSeconds));
218223
urlConnection.setReadTimeout((int) SECONDS.toMillis(readTimeoutInSeconds));
@@ -221,15 +226,16 @@ private void setUpUrlConnection(
221226
// change in the Fetch Response since the last fetch call.
222227
urlConnection.setRequestProperty(IF_NONE_MATCH_HEADER, lastFetchEtag);
223228

224-
setCommonRequestHeaders(urlConnection);
229+
setCommonRequestHeaders(urlConnection, installationAuthToken);
225230
setCustomRequestHeaders(urlConnection, customHeaders);
226231
}
227232

228233
private String getFetchUrl(String projectNumber, String namespace) {
229234
return String.format(FETCH_REGEX_URL, projectNumber, namespace);
230235
}
231236

232-
private void setCommonRequestHeaders(HttpURLConnection urlConnection) {
237+
private void setCommonRequestHeaders(
238+
HttpURLConnection urlConnection, String installationAuthToken) {
233239
urlConnection.setRequestProperty(API_KEY_HEADER, apiKey);
234240

235241
// Headers required for Android API Key Restrictions.
@@ -239,6 +245,9 @@ private void setCommonRequestHeaders(HttpURLConnection urlConnection) {
239245
// Header to denote request is retryable on the server.
240246
urlConnection.setRequestProperty(X_GOOGLE_GFE_CAN_RETRY, "yes");
241247

248+
// Header for FIS auth token
249+
urlConnection.setRequestProperty(INSTALLATIONS_AUTH_TOKEN_HEADER, installationAuthToken);
250+
242251
// Headers to denote that the request body is a JSONObject.
243252
urlConnection.setRequestProperty("Content-Type", "application/json");
244253
urlConnection.setRequestProperty("Accept", "application/json");
@@ -278,16 +287,19 @@ private String getFingerprintHashForPackage() {
278287
* serialized as a JSON.
279288
*/
280289
private JSONObject createFetchRequestBody(
281-
String instanceId, String instanceIdToken, Map<String, String> analyticsUserProperties)
290+
String installationId,
291+
String installationAuthToken,
292+
Map<String, String> analyticsUserProperties)
282293
throws FirebaseRemoteConfigClientException {
283294
Map<String, Object> requestBodyMap = new HashMap<>();
284295

285-
if (instanceId == null) {
286-
throw new FirebaseRemoteConfigClientException("Fetch failed: Firebase instance id is null.");
296+
if (installationId == null) {
297+
throw new FirebaseRemoteConfigClientException(
298+
"Fetch failed: Firebase installation id is null.");
287299
}
288-
requestBodyMap.put(INSTANCE_ID, instanceId);
300+
requestBodyMap.put(INSTANCE_ID, installationId);
289301

290-
requestBodyMap.put(INSTANCE_ID_TOKEN, instanceIdToken);
302+
requestBodyMap.put(INSTANCE_ID_TOKEN, installationAuthToken);
291303
requestBodyMap.put(APP_ID, appId);
292304

293305
Locale locale = context.getResources().getConfiguration().locale;

firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@
8484
shadows = {ShadowPreconditions.class})
8585
public final class FirebaseRemoteConfigTest {
8686
private static final String APP_ID = "1:14368190084:android:09cb977358c6f241";
87-
private static final String API_KEY = "api_key";
87+
private static final String API_KEY = "AIzaSyabcdefghijklmnopqrstuvwxyz1234567";
88+
private static final String PROJECT_ID = "fake-frc-test-id";
8889

8990
private static final String FIREPERF_NAMESPACE = "fireperf";
9091

@@ -1220,6 +1221,11 @@ private static FirebaseApp initializeFirebaseApp(Context context) {
12201221
FirebaseApp.clearInstancesForTest();
12211222

12221223
return FirebaseApp.initializeApp(
1223-
context, new FirebaseOptions.Builder().setApiKey(API_KEY).setApplicationId(APP_ID).build());
1224+
context,
1225+
new FirebaseOptions.Builder()
1226+
.setApiKey(API_KEY)
1227+
.setApplicationId(APP_ID)
1228+
.setProjectId(PROJECT_ID)
1229+
.build());
12241230
}
12251231
}

firebase-config/src/test/java/com/google/firebase/remoteconfig/RemoteConfigComponentTest.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,10 @@
5858
@RunWith(RobolectricTestRunner.class)
5959
@Config(manifest = Config.NONE)
6060
public class RemoteConfigComponentTest {
61-
private static final String API_KEY = "api_key";
61+
private static final String API_KEY = "AIzaSyabcdefghijklmnopqrstuvwxyz1234567";
6262
private static final String APP_ID = "1:14368190084:android:09cb977358c6f241";
6363
private static final String DUMMY_API_KEY = "api_key";
64+
private static final String PROJECT_ID = "fake-frc-test-id";
6465

6566
@Mock private FirebaseApp mockFirebaseApp;
6667
@Mock private FirebaseInstallationsApi mockFirebaseInstallations;
@@ -237,6 +238,11 @@ private static FirebaseApp initializeFirebaseApp(Context context) {
237238
FirebaseApp.clearInstancesForTest();
238239

239240
return FirebaseApp.initializeApp(
240-
context, new FirebaseOptions.Builder().setApiKey(API_KEY).setApplicationId(APP_ID).build());
241+
context,
242+
new FirebaseOptions.Builder()
243+
.setApiKey(API_KEY)
244+
.setApplicationId(APP_ID)
245+
.setProjectId(PROJECT_ID)
246+
.build());
241247
}
242248
}

firebase-config/src/test/java/com/google/firebase/remoteconfig/internal/ConfigFetchHttpClientTest.java

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,9 @@ public class ConfigFetchHttpClientTest {
7272
private static final String API_KEY = "fake_api_key";
7373
private static final String FAKE_APP_ID = "1:14368190084:android:09cb977358c6f241";
7474
private static final String PROJECT_NUMBER = "14368190084";
75-
private static final String INSTANCE_ID_STRING = "fake instance id";
76-
private static final String INSTANCE_ID_TOKEN_STRING = "fake instance id token";
75+
private static final String INSTALLATION_ID_STRING = "'fL71_VyL3uo9jNMWu1L60S";
76+
private static final String INSTALLATION_AUTH_TOKEN_STRING =
77+
"eyJhbGciOiJF.eyJmaWQiOiJmaXMt.AB2LPV8wRQIhAPs4NvEgA3uhubH";
7778
private static final String DEFAULT_NAMESPACE = RemoteConfigComponent.DEFAULT_NAMESPACE;
7879
private static final String ETAG_FORMAT =
7980
"etag-" + PROJECT_NUMBER + "-" + DEFAULT_NAMESPACE + "-fetch-%d";
@@ -177,6 +178,7 @@ public void fetch_setsAllHeaders_sendsAllHeadersToServer() throws Exception {
177178
expectedHeaders.put("X-Android-Package", context.getPackageName());
178179
expectedHeaders.put("X-Android-Cert", null);
179180
expectedHeaders.put("X-Google-GFE-Can-Retry", "yes");
181+
expectedHeaders.put("X-Goog-Firebase-Installations-Auth", INSTALLATION_AUTH_TOKEN_STRING);
180182
expectedHeaders.put("Content-Type", "application/json");
181183
expectedHeaders.put("Accept", "application/json");
182184
// Custom user-defined headers.
@@ -195,8 +197,8 @@ public void fetch_setsAllElementsOfRequestBody_sendsRequestBodyToServer() throws
195197
fetch(FIRST_ETAG, userProperties);
196198

197199
JSONObject requestBody = new JSONObject(fakeHttpURLConnection.getOutputStream().toString());
198-
assertThat(requestBody.get(INSTANCE_ID)).isEqualTo(INSTANCE_ID_STRING);
199-
assertThat(requestBody.get(INSTANCE_ID_TOKEN)).isEqualTo(INSTANCE_ID_TOKEN_STRING);
200+
assertThat(requestBody.get(INSTANCE_ID)).isEqualTo(INSTALLATION_ID_STRING);
201+
assertThat(requestBody.get(INSTANCE_ID_TOKEN)).isEqualTo(INSTALLATION_AUTH_TOKEN_STRING);
200202
assertThat(requestBody.get(APP_ID)).isEqualTo(FAKE_APP_ID);
201203
Locale locale = context.getResources().getConfiguration().locale;
202204
assertThat(requestBody.get(COUNTRY_CODE)).isEqualTo(locale.getCountry());
@@ -240,22 +242,22 @@ public void fetch_localeUsesToStringBelowLollipop() throws Exception {
240242
}
241243

242244
@Test
243-
public void fetch_instanceIdIsNull_throwsFRCClientException() throws Exception {
245+
public void fetch_installationIdIsNull_throwsFRCClientException() throws Exception {
244246
setServerResponseTo(noChangeResponseBody, SECOND_ETAG);
245247

246248
FirebaseRemoteConfigClientException frcException =
247-
assertThrows(FirebaseRemoteConfigClientException.class, () -> fetchWithoutIid());
249+
assertThrows(FirebaseRemoteConfigClientException.class, () -> fetchWithoutInstallationId());
248250

249-
assertThat(frcException).hasMessageThat().contains("instance id is null");
251+
assertThat(frcException).hasMessageThat().contains("installation id is null");
250252
}
251253

252254
@Test
253-
public void fetch_instanceIdTokenIsNull_doesNotThrowException() throws Exception {
255+
public void fetch_installationAuthTokenIsNull_doesNotThrowException() throws Exception {
254256
setServerResponseTo(noChangeResponseBody, SECOND_ETAG);
255257

256-
FetchResponse fetchResponse = fetchWithoutIidToken();
258+
FetchResponse fetchResponse = fetchWithoutInstallationAuthToken();
257259

258-
assertWithMessage("Fetch() failed with null instance id token!")
260+
assertWithMessage("Fetch() failed with null installation auth token!")
259261
.that(fetchResponse)
260262
.isNotNull();
261263
}
@@ -291,8 +293,8 @@ public void fetch_serverReturnsException_throwsFirebaseRemoteConfigException() {
291293
private FetchResponse fetch(String eTag) throws Exception {
292294
return configFetchHttpClient.fetch(
293295
fakeHttpURLConnection,
294-
INSTANCE_ID_STRING,
295-
INSTANCE_ID_TOKEN_STRING,
296+
INSTALLATION_ID_STRING,
297+
INSTALLATION_AUTH_TOKEN_STRING,
296298
/* analyticsUserProperties= */ ImmutableMap.of(),
297299
eTag,
298300
/* customHeaders= */ ImmutableMap.of(),
@@ -302,8 +304,8 @@ private FetchResponse fetch(String eTag) throws Exception {
302304
private FetchResponse fetch(String eTag, Map<String, String> userProperties) throws Exception {
303305
return configFetchHttpClient.fetch(
304306
fakeHttpURLConnection,
305-
INSTANCE_ID_STRING,
306-
INSTANCE_ID_TOKEN_STRING,
307+
INSTALLATION_ID_STRING,
308+
INSTALLATION_AUTH_TOKEN_STRING,
307309
userProperties,
308310
eTag,
309311
/* customHeaders= */ ImmutableMap.of(),
@@ -315,30 +317,30 @@ private FetchResponse fetch(
315317
throws Exception {
316318
return configFetchHttpClient.fetch(
317319
fakeHttpURLConnection,
318-
INSTANCE_ID_STRING,
319-
INSTANCE_ID_TOKEN_STRING,
320+
INSTALLATION_ID_STRING,
321+
INSTALLATION_AUTH_TOKEN_STRING,
320322
userProperties,
321323
eTag,
322324
customHeaders,
323325
new Date(mockClock.currentTimeMillis()));
324326
}
325327

326-
private FetchResponse fetchWithoutIid() throws Exception {
328+
private FetchResponse fetchWithoutInstallationId() throws Exception {
327329
return configFetchHttpClient.fetch(
328330
fakeHttpURLConnection,
329-
/* instanceId= */ null,
330-
INSTANCE_ID_TOKEN_STRING,
331+
/* installationId= */ null,
332+
INSTALLATION_AUTH_TOKEN_STRING,
331333
/* analyticsUserProperties= */ ImmutableMap.of(),
332334
/* lastFetchETag= */ "bogus-etag",
333335
/* customHeaders= */ ImmutableMap.of(),
334336
new Date(mockClock.currentTimeMillis()));
335337
}
336338

337-
private FetchResponse fetchWithoutIidToken() throws Exception {
339+
private FetchResponse fetchWithoutInstallationAuthToken() throws Exception {
338340
return configFetchHttpClient.fetch(
339341
fakeHttpURLConnection,
340-
INSTANCE_ID_STRING,
341-
/* instanceIdToken= */ null,
342+
INSTALLATION_ID_STRING,
343+
/* installationAuthToken= */ null,
342344
/* analyticsUserProperties= */ ImmutableMap.of(),
343345
/* lastFetchETag= */ "bogus-etag",
344346
/* customHeaders= */ ImmutableMap.of(),

0 commit comments

Comments
 (0)