Skip to content

Commit 32f05c3

Browse files
authored
Migrating Auth API to the new Identity Toolkit endpoint (#220)
* Migrating Auth API to the new Identity Toolkit endpoint * Updated CHANGELOG
1 parent 4f21d1e commit 32f05c3

File tree

4 files changed

+68
-33
lines changed

4 files changed

+68
-33
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
code.
66
- [fixed] FCM errors sent by the back-end now include more details
77
that are helpful when debugging problems.
8+
- [changed] Migrated the `FirebaseAuth` user management API to the
9+
new Identity Toolkit endpoint.
810

911
# v6.5.0
1012

src/main/java/com/google/firebase/auth/FirebaseUserManager.java

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import static com.google.common.base.Preconditions.checkNotNull;
2121

2222
import com.google.api.client.http.GenericUrl;
23+
import com.google.api.client.http.HttpContent;
2324
import com.google.api.client.http.HttpRequest;
2425
import com.google.api.client.http.HttpRequestFactory;
2526
import com.google.api.client.http.HttpResponse;
@@ -36,6 +37,7 @@
3637
import com.google.common.collect.ImmutableList;
3738
import com.google.common.collect.ImmutableMap;
3839
import com.google.firebase.FirebaseApp;
40+
import com.google.firebase.ImplFirebaseTrampolines;
3941
import com.google.firebase.auth.UserRecord.CreateRequest;
4042
import com.google.firebase.auth.UserRecord.UpdateRequest;
4143
import com.google.firebase.auth.internal.DownloadAccountResponse;
@@ -45,7 +47,9 @@
4547
import com.google.firebase.auth.internal.UploadAccountResponse;
4648
import com.google.firebase.internal.FirebaseRequestInitializer;
4749
import com.google.firebase.internal.NonNull;
50+
import com.google.firebase.internal.Nullable;
4851
import com.google.firebase.internal.SdkUtils;
52+
4953
import java.io.IOException;
5054
import java.util.List;
5155
import java.util.Map;
@@ -91,9 +95,10 @@ class FirebaseUserManager {
9195
"iss", "jti", "nbf", "nonce", "sub", "firebase");
9296

9397
private static final String ID_TOOLKIT_URL =
94-
"https://www.googleapis.com/identitytoolkit/v3/relyingparty/";
98+
"https://identitytoolkit.googleapis.com/v1/projects/%s";
9599
private static final String CLIENT_VERSION_HEADER = "X-Client-Version";
96100

101+
private final String baseUrl;
97102
private final JsonFactory jsonFactory;
98103
private final HttpRequestFactory requestFactory;
99104
private final String clientVersion = "Java/Admin/" + SdkUtils.getVersion();
@@ -107,6 +112,12 @@ class FirebaseUserManager {
107112
*/
108113
FirebaseUserManager(@NonNull FirebaseApp app) {
109114
checkNotNull(app, "FirebaseApp must not be null");
115+
String projectId = ImplFirebaseTrampolines.getProjectId(app);
116+
checkArgument(!Strings.isNullOrEmpty(projectId),
117+
"Project ID is required to access the auth service. Use a service account credential or "
118+
+ "set the project ID explicitly via FirebaseOptions. Alternatively you can also "
119+
+ "set the project ID via the GOOGLE_CLOUD_PROJECT environment variable.");
120+
this.baseUrl = String.format(ID_TOOLKIT_URL, projectId);
110121
this.jsonFactory = app.getOptions().getJsonFactory();
111122
HttpTransport transport = app.getOptions().getHttpTransport();
112123
this.requestFactory = transport.createRequestFactory(new FirebaseRequestInitializer(app));
@@ -121,7 +132,7 @@ UserRecord getUserById(String uid) throws FirebaseAuthException {
121132
final Map<String, Object> payload = ImmutableMap.<String, Object>of(
122133
"localId", ImmutableList.of(uid));
123134
GetAccountInfoResponse response = post(
124-
"getAccountInfo", payload, GetAccountInfoResponse.class);
135+
"/accounts:lookup", payload, GetAccountInfoResponse.class);
125136
if (response == null || response.getUsers() == null || response.getUsers().isEmpty()) {
126137
throw new FirebaseAuthException(USER_NOT_FOUND_ERROR,
127138
"No user record found for the provided user ID: " + uid);
@@ -133,7 +144,7 @@ UserRecord getUserByEmail(String email) throws FirebaseAuthException {
133144
final Map<String, Object> payload = ImmutableMap.<String, Object>of(
134145
"email", ImmutableList.of(email));
135146
GetAccountInfoResponse response = post(
136-
"getAccountInfo", payload, GetAccountInfoResponse.class);
147+
"/accounts:lookup", payload, GetAccountInfoResponse.class);
137148
if (response == null || response.getUsers() == null || response.getUsers().isEmpty()) {
138149
throw new FirebaseAuthException(USER_NOT_FOUND_ERROR,
139150
"No user record found for the provided email: " + email);
@@ -145,7 +156,7 @@ UserRecord getUserByPhoneNumber(String phoneNumber) throws FirebaseAuthException
145156
final Map<String, Object> payload = ImmutableMap.<String, Object>of(
146157
"phoneNumber", ImmutableList.of(phoneNumber));
147158
GetAccountInfoResponse response = post(
148-
"getAccountInfo", payload, GetAccountInfoResponse.class);
159+
"/accounts:lookup", payload, GetAccountInfoResponse.class);
149160
if (response == null || response.getUsers() == null || response.getUsers().isEmpty()) {
150161
throw new FirebaseAuthException(USER_NOT_FOUND_ERROR,
151162
"No user record found for the provided phone number: " + phoneNumber);
@@ -155,7 +166,7 @@ UserRecord getUserByPhoneNumber(String phoneNumber) throws FirebaseAuthException
155166

156167
String createUser(CreateRequest request) throws FirebaseAuthException {
157168
GenericJson response = post(
158-
"signupNewUser", request.getProperties(), GenericJson.class);
169+
"/accounts", request.getProperties(), GenericJson.class);
159170
if (response != null) {
160171
String uid = (String) response.get("localId");
161172
if (!Strings.isNullOrEmpty(uid)) {
@@ -167,7 +178,7 @@ String createUser(CreateRequest request) throws FirebaseAuthException {
167178

168179
void updateUser(UpdateRequest request, JsonFactory jsonFactory) throws FirebaseAuthException {
169180
GenericJson response = post(
170-
"setAccountInfo", request.getProperties(jsonFactory), GenericJson.class);
181+
"/accounts:update", request.getProperties(jsonFactory), GenericJson.class);
171182
if (response == null || !request.getUid().equals(response.get("localId"))) {
172183
throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to update user: " + request.getUid());
173184
}
@@ -176,7 +187,7 @@ void updateUser(UpdateRequest request, JsonFactory jsonFactory) throws FirebaseA
176187
void deleteUser(String uid) throws FirebaseAuthException {
177188
final Map<String, Object> payload = ImmutableMap.<String, Object>of("localId", uid);
178189
GenericJson response = post(
179-
"deleteAccount", payload, GenericJson.class);
190+
"/accounts:delete", payload, GenericJson.class);
180191
if (response == null || !response.containsKey("kind")) {
181192
throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to delete user: " + uid);
182193
}
@@ -190,8 +201,10 @@ DownloadAccountResponse listUsers(int maxResults, String pageToken) throws Fireb
190201
builder.put("nextPageToken", pageToken);
191202
}
192203

193-
DownloadAccountResponse response = post(
194-
"downloadAccount", builder.build(), DownloadAccountResponse.class);
204+
GenericUrl url = new GenericUrl(baseUrl + "/accounts:batchGet");
205+
url.putAll(builder.build());
206+
DownloadAccountResponse response = sendRequest(
207+
"GET", url, null, DownloadAccountResponse.class);
195208
if (response == null) {
196209
throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to retrieve users.");
197210
}
@@ -200,7 +213,8 @@ DownloadAccountResponse listUsers(int maxResults, String pageToken) throws Fireb
200213

201214
UserImportResult importUsers(UserImportRequest request) throws FirebaseAuthException {
202215
checkNotNull(request);
203-
UploadAccountResponse response = post("uploadAccount", request, UploadAccountResponse.class);
216+
UploadAccountResponse response = post(
217+
"/accounts:batchCreate", request, UploadAccountResponse.class);
204218
if (response == null) {
205219
throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to import users.");
206220
}
@@ -211,7 +225,7 @@ String createSessionCookie(String idToken,
211225
SessionCookieOptions options) throws FirebaseAuthException {
212226
final Map<String, Object> payload = ImmutableMap.<String, Object>of(
213227
"idToken", idToken, "validDuration", options.getExpiresInSeconds());
214-
GenericJson response = post("createSessionCookie", payload, GenericJson.class);
228+
GenericJson response = post(":createSessionCookie", payload, GenericJson.class);
215229
if (response != null) {
216230
String cookie = (String) response.get("sessionCookie");
217231
if (!Strings.isNullOrEmpty(cookie)) {
@@ -223,14 +237,22 @@ String createSessionCookie(String idToken,
223237

224238
private <T> T post(String path, Object content, Class<T> clazz) throws FirebaseAuthException {
225239
checkArgument(!Strings.isNullOrEmpty(path), "path must not be null or empty");
226-
checkNotNull(content, "content must not be null");
227-
checkNotNull(clazz, "response class must not be null");
240+
checkNotNull(content, "content must not be null for POST requests");
241+
GenericUrl url = new GenericUrl(baseUrl + path);
242+
return sendRequest("POST", url, content, clazz);
243+
}
228244

229-
GenericUrl url = new GenericUrl(ID_TOOLKIT_URL + path);
245+
private <T> T sendRequest(
246+
String method, GenericUrl url,
247+
@Nullable Object content, Class<T> clazz) throws FirebaseAuthException {
248+
249+
checkArgument(!Strings.isNullOrEmpty(method), "method must not be null or empty");
250+
checkNotNull(url, "url must not be null");
251+
checkNotNull(clazz, "response class must not be null");
230252
HttpResponse response = null;
231253
try {
232-
HttpRequest request = requestFactory.buildPostRequest(url,
233-
new JsonHttpContent(jsonFactory, content));
254+
HttpContent httpContent = content != null ? new JsonHttpContent(jsonFactory, content) : null;
255+
HttpRequest request = requestFactory.buildRequest(method, url, httpContent);
234256
request.setParser(new JsonObjectParser(jsonFactory));
235257
request.getHeaders().set(CLIENT_VERSION_HEADER, clientVersion);
236258
request.setResponseInterceptor(interceptor);

src/test/java/com/google/firebase/auth/FirebaseAuthTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import com.google.auth.oauth2.UserCredentials;
3737
import com.google.common.base.Defaults;
3838
import com.google.common.base.Strings;
39-
import com.google.common.collect.ImmutableList;
4039
import com.google.firebase.FirebaseApp;
4140
import com.google.firebase.FirebaseOptions;
4241
import com.google.firebase.ImplFirebaseTrampolines;
@@ -98,7 +97,10 @@ public static Collection<Object[]> data() throws Exception {
9897
/* isCertCredential */ true
9998
},
10099
{
101-
new FirebaseOptions.Builder().setCredentials(createRefreshTokenCredential()).build(),
100+
new FirebaseOptions.Builder()
101+
.setCredentials(createRefreshTokenCredential())
102+
.setProjectId("test-project-id")
103+
.build(),
102104
/* isCertCredential */ false
103105
},
104106
{

src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import static org.junit.Assert.fail;
2525

2626
import com.google.api.client.googleapis.util.Utils;
27+
import com.google.api.client.http.GenericUrl;
2728
import com.google.api.client.http.HttpHeaders;
2829
import com.google.api.client.http.HttpRequest;
2930
import com.google.api.client.http.HttpResponseException;
@@ -67,6 +68,18 @@ public void tearDown() {
6768
TestOnlyImplFirebaseTrampolines.clearInstancesForTest();
6869
}
6970

71+
@Test
72+
public void testProjectIdRequired() {
73+
FirebaseApp.initializeApp(new FirebaseOptions.Builder()
74+
.setCredentials(credentials)
75+
.build());
76+
try {
77+
FirebaseAuth.getInstance();
78+
fail("No error thrown for missing project ID");
79+
} catch (IllegalArgumentException expected) {
80+
}
81+
}
82+
7083
@Test
7184
public void testGetUser() throws Exception {
7285
TestResponseInterceptor interceptor = initializeAppForUserManagement(
@@ -149,12 +162,9 @@ public void testListUsers() throws Exception {
149162
assertEquals("", page.getNextPageToken());
150163
checkRequestHeaders(interceptor);
151164

152-
ByteArrayOutputStream out = new ByteArrayOutputStream();
153-
interceptor.getResponse().getRequest().getContent().writeTo(out);
154-
JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
155-
GenericJson parsed = jsonFactory.fromString(new String(out.toByteArray()), GenericJson.class);
156-
assertEquals(new BigDecimal(999), parsed.get("maxResults"));
157-
assertNull(parsed.get("nextPageToken"));
165+
GenericUrl url = interceptor.getResponse().getRequest().getUrl();
166+
assertEquals(999, url.getFirst("maxResults"));
167+
assertNull(url.getFirst("nextPageToken"));
158168
}
159169

160170
@Test
@@ -171,12 +181,9 @@ public void testListUsersWithPageToken() throws Exception {
171181
assertEquals("", page.getNextPageToken());
172182
checkRequestHeaders(interceptor);
173183

174-
ByteArrayOutputStream out = new ByteArrayOutputStream();
175-
interceptor.getResponse().getRequest().getContent().writeTo(out);
176-
JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
177-
GenericJson parsed = jsonFactory.fromString(new String(out.toByteArray()), GenericJson.class);
178-
assertEquals(new BigDecimal(999), parsed.get("maxResults"));
179-
assertEquals("token", parsed.get("nextPageToken"));
184+
GenericUrl url = interceptor.getResponse().getRequest().getUrl();
185+
assertEquals(999, url.getFirst("maxResults"));
186+
assertEquals("token", url.getFirst("nextPageToken"));
180187
}
181188

182189
@Test
@@ -427,9 +434,7 @@ public void testCreateSessionCookie() throws Exception {
427434

428435
@Test
429436
public void testCreateSessionCookieInvalidArguments() {
430-
FirebaseApp.initializeApp(new FirebaseOptions.Builder()
431-
.setCredentials(credentials)
432-
.build());
437+
initializeAppForUserManagement();
433438
SessionCookieOptions options = SessionCookieOptions.builder()
434439
.setExpiresIn(TimeUnit.HOURS.toMillis(1))
435440
.build();
@@ -532,6 +537,7 @@ public void call(FirebaseAuth auth) throws Exception {
532537
.build();
533538
FirebaseApp.initializeApp(new FirebaseOptions.Builder()
534539
.setCredentials(credentials)
540+
.setProjectId("test-project-id")
535541
.setHttpTransport(transport)
536542
.build());
537543

@@ -596,6 +602,7 @@ public void testGetUserUnexpectedHttpError() throws Exception {
596602
.build();
597603
FirebaseApp.initializeApp(new FirebaseOptions.Builder()
598604
.setCredentials(credentials)
605+
.setProjectId("test-project-id")
599606
.setHttpTransport(transport)
600607
.build());
601608
try {
@@ -617,6 +624,7 @@ public void testTimeout() throws Exception {
617624
new MockLowLevelHttpResponse().setContent(TestUtils.loadResource("getUser.json"))));
618625
FirebaseApp.initializeApp(new FirebaseOptions.Builder()
619626
.setCredentials(credentials)
627+
.setProjectId("test-project-id")
620628
.setHttpTransport(transport)
621629
.setConnectTimeout(30000)
622630
.setReadTimeout(60000)
@@ -989,6 +997,7 @@ private static TestResponseInterceptor initializeAppForUserManagement(String ...
989997
FirebaseApp.initializeApp(new FirebaseOptions.Builder()
990998
.setCredentials(credentials)
991999
.setHttpTransport(transport)
1000+
.setProjectId("test-project-id")
9921001
.build());
9931002
FirebaseAuth auth = FirebaseAuth.getInstance();
9941003
FirebaseUserManager userManager = auth.getUserManager();

0 commit comments

Comments
 (0)