diff --git a/src/main/java/com/google/firebase/auth/AuthErrorCode.java b/src/main/java/com/google/firebase/auth/AuthErrorCode.java new file mode 100644 index 000000000..83287d712 --- /dev/null +++ b/src/main/java/com/google/firebase/auth/AuthErrorCode.java @@ -0,0 +1,54 @@ +/* + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.auth; + +/** + * Error codes that can be raised by the Firebase Auth APIs. + */ +public enum AuthErrorCode { + + /** + * A user already exists with the provided email. + */ + EMAIL_ALREADY_EXISTS, + + /** + * The provided dynamic link domain is not configured or authorized for the current project. + */ + INVALID_DYNAMIC_LINK_DOMAIN, + + /** + * A user already exists with the provided phone number. + */ + PHONE_NUMBER_ALREADY_EXISTS, + + /** + * A user already exists with the provided UID. + */ + UID_ALREADY_EXISTS, + + /** + * The domain of the continue URL is not whitelisted. Whitelist the domain in the Firebase + * console. + */ + UNAUTHORIZED_CONTINUE_URL, + + /** + * No user record found for the given identifier. + */ + USER_NOT_FOUND, +} diff --git a/src/main/java/com/google/firebase/auth/AuthErrorHandler.java b/src/main/java/com/google/firebase/auth/AuthErrorHandler.java new file mode 100644 index 000000000..a6cea3914 --- /dev/null +++ b/src/main/java/com/google/firebase/auth/AuthErrorHandler.java @@ -0,0 +1,213 @@ +/* + * Copyright 2020 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.auth; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.api.client.json.GenericJson; +import com.google.api.client.json.JsonFactory; +import com.google.api.client.util.Key; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; +import com.google.firebase.ErrorCode; +import com.google.firebase.FirebaseException; +import com.google.firebase.internal.AbstractHttpErrorHandler; +import com.google.firebase.internal.Nullable; +import java.io.IOException; +import java.util.Map; + +final class AuthErrorHandler extends AbstractHttpErrorHandler { + + private static final Map ERROR_CODES = + ImmutableMap.builder() + .put( + "DUPLICATE_EMAIL", + new AuthError( + ErrorCode.ALREADY_EXISTS, + "The user with the provided email already exists", + AuthErrorCode.EMAIL_ALREADY_EXISTS)) + .put( + "DUPLICATE_LOCAL_ID", + new AuthError( + ErrorCode.ALREADY_EXISTS, + "The user with the provided uid already exists", + AuthErrorCode.UID_ALREADY_EXISTS)) + .put( + "EMAIL_EXISTS", + new AuthError( + ErrorCode.ALREADY_EXISTS, + "The user with the provided email already exists", + AuthErrorCode.EMAIL_ALREADY_EXISTS)) + .put( + "INVALID_DYNAMIC_LINK_DOMAIN", + new AuthError( + ErrorCode.INVALID_ARGUMENT, + "The provided dynamic link domain is not " + + "configured or authorized for the current project", + AuthErrorCode.INVALID_DYNAMIC_LINK_DOMAIN)) + .put( + "PHONE_NUMBER_EXISTS", + new AuthError( + ErrorCode.ALREADY_EXISTS, + "The user with the provided phone number already exists", + AuthErrorCode.PHONE_NUMBER_ALREADY_EXISTS)) + .put( + "UNAUTHORIZED_DOMAIN", + new AuthError( + ErrorCode.INVALID_ARGUMENT, + "The domain of the continue URL is not whitelisted", + AuthErrorCode.UNAUTHORIZED_CONTINUE_URL)) + .put( + "USER_NOT_FOUND", + new AuthError( + ErrorCode.NOT_FOUND, + "No user record found for the given identifier", + AuthErrorCode.USER_NOT_FOUND)) + .build(); + + private final JsonFactory jsonFactory; + + AuthErrorHandler(JsonFactory jsonFactory) { + this.jsonFactory = checkNotNull(jsonFactory); + } + + @Override + protected FirebaseAuthException createException(FirebaseException base) { + String response = getResponse(base); + AuthServiceErrorResponse parsed = safeParse(response); + AuthError errorInfo = ERROR_CODES.get(parsed.getCode()); + if (errorInfo != null) { + return new FirebaseAuthException( + errorInfo.getErrorCode(), + errorInfo.buildMessage(parsed), + base.getCause(), + base.getHttpResponse(), + errorInfo.getAuthErrorCode()); + } + + return new FirebaseAuthException( + base.getErrorCodeNew(), + base.getMessage(), + base.getCause(), + base.getHttpResponse(), + null); + } + + private String getResponse(FirebaseException base) { + if (base.getHttpResponse() == null) { + return null; + } + + return base.getHttpResponse().getContent(); + } + + private AuthServiceErrorResponse safeParse(String response) { + AuthServiceErrorResponse parsed = new AuthServiceErrorResponse(); + if (!Strings.isNullOrEmpty(response)) { + try { + jsonFactory.createJsonParser(response).parse(parsed); + } catch (IOException ignore) { + // Ignore any error that may occur while parsing the error response. The server + // may have responded with a non-json payload. + } + } + + return parsed; + } + + private static class AuthError { + + private final ErrorCode errorCode; + private final String message; + private final AuthErrorCode authErrorCode; + + AuthError(ErrorCode errorCode, String message, AuthErrorCode authErrorCode) { + this.errorCode = errorCode; + this.message = message; + this.authErrorCode = authErrorCode; + } + + ErrorCode getErrorCode() { + return errorCode; + } + + AuthErrorCode getAuthErrorCode() { + return authErrorCode; + } + + String buildMessage(AuthServiceErrorResponse response) { + StringBuilder builder = new StringBuilder(this.message) + .append(" (").append(response.getCode()).append(")"); + String detail = response.getDetail(); + if (!Strings.isNullOrEmpty(detail)) { + builder.append(": ").append(detail); + } else { + builder.append("."); + } + + return builder.toString(); + } + } + + /** + * JSON data binding for JSON error messages sent by Google identity toolkit service. These + * error messages take the form `{"error": {"message": "CODE: OPTIONAL DETAILS"}}`. + */ + private static class AuthServiceErrorResponse { + + @Key("error") + private GenericJson error; + + @Nullable + public String getCode() { + String message = getMessage(); + if (Strings.isNullOrEmpty(message)) { + return null; + } + + int separator = message.indexOf(':'); + if (separator != -1) { + return message.substring(0, separator); + } + + return message; + } + + @Nullable + public String getDetail() { + String message = getMessage(); + if (Strings.isNullOrEmpty(message)) { + return null; + } + + int separator = message.indexOf(':'); + if (separator != -1) { + return message.substring(separator + 1).trim(); + } + + return null; + } + + private String getMessage() { + if (error == null) { + return null; + } + + return (String) error.get("message"); + } + } +} diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuthException.java b/src/main/java/com/google/firebase/auth/FirebaseAuthException.java index 2314a69d2..bd7f60e7f 100644 --- a/src/main/java/com/google/firebase/auth/FirebaseAuthException.java +++ b/src/main/java/com/google/firebase/auth/FirebaseAuthException.java @@ -16,17 +16,14 @@ package com.google.firebase.auth; -// TODO: Move it out from firebase-common. Temporary host it their for -// database's integration.http://b/27624510. - -// TODO: Decide if changing this not enforcing an error code. Need to align -// with the decision in http://b/27677218. Also, need to turn this into abstract later. - import static com.google.common.base.Preconditions.checkArgument; import com.google.common.base.Strings; +import com.google.firebase.ErrorCode; import com.google.firebase.FirebaseException; +import com.google.firebase.IncomingHttpResponse; import com.google.firebase.internal.NonNull; +import com.google.firebase.internal.Nullable; /** * Generic exception related to Firebase Authentication. Check the error code and message for more @@ -34,22 +31,42 @@ */ public class FirebaseAuthException extends FirebaseException { - private final String errorCode; + private final AuthErrorCode errorCode; + private final String deprecatedErrorCode; + + FirebaseAuthException( + @NonNull ErrorCode errorCode, + @NonNull String message, + Throwable cause, + IncomingHttpResponse response, + AuthErrorCode authErrorCode) { + super(errorCode, message, cause, response); + this.errorCode = authErrorCode; + this.deprecatedErrorCode = null; + } + @Deprecated public FirebaseAuthException(@NonNull String errorCode, @NonNull String detailMessage) { this(errorCode, detailMessage, null); } - public FirebaseAuthException(@NonNull String errorCode, @NonNull String detailMessage, - Throwable throwable) { + @Deprecated + public FirebaseAuthException( + @NonNull String errorCode, @NonNull String detailMessage, Throwable throwable) { super(detailMessage, throwable); checkArgument(!Strings.isNullOrEmpty(errorCode)); - this.errorCode = errorCode; + this.errorCode = null; + this.deprecatedErrorCode = errorCode; } - /** Returns an error code that may provide more information about the error. */ - @NonNull - public String getErrorCode() { + @Nullable + public AuthErrorCode getAuthErrorCode() { return errorCode; } + + /** Returns an error code that may provide more information about the error. */ + @Deprecated + public String getDeprecatedErrorCode() { + return deprecatedErrorCode; + } } diff --git a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java index 03c2813bc..650f9d019 100644 --- a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java +++ b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java @@ -21,35 +21,33 @@ import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpContent; -import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpMethods; import com.google.api.client.http.HttpRequestFactory; -import com.google.api.client.http.HttpResponse; -import com.google.api.client.http.HttpResponseException; import com.google.api.client.http.HttpResponseInterceptor; import com.google.api.client.http.json.JsonHttpContent; import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonFactory; -import com.google.api.client.json.JsonObjectParser; import com.google.api.client.util.Key; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.firebase.ErrorCode; import com.google.firebase.FirebaseApp; import com.google.firebase.ImplFirebaseTrampolines; +import com.google.firebase.IncomingHttpResponse; import com.google.firebase.auth.UserRecord.CreateRequest; import com.google.firebase.auth.UserRecord.UpdateRequest; import com.google.firebase.auth.internal.DownloadAccountResponse; import com.google.firebase.auth.internal.GetAccountInfoResponse; - -import com.google.firebase.auth.internal.HttpErrorResponse; import com.google.firebase.auth.internal.UploadAccountResponse; import com.google.firebase.internal.ApiClientUtils; +import com.google.firebase.internal.ErrorHandlingHttpClient; +import com.google.firebase.internal.HttpRequestInfo; import com.google.firebase.internal.NonNull; import com.google.firebase.internal.Nullable; import com.google.firebase.internal.SdkUtils; -import java.io.IOException; import java.util.List; import java.util.Map; @@ -62,30 +60,6 @@ */ class FirebaseUserManager { - static final String USER_NOT_FOUND_ERROR = "user-not-found"; - static final String INTERNAL_ERROR = "internal-error"; - - // Map of server-side error codes to SDK error codes. - // SDK error codes defined at: https://firebase.google.com/docs/auth/admin/errors - private static final Map ERROR_CODES = ImmutableMap.builder() - .put("CLAIMS_TOO_LARGE", "claims-too-large") - .put("CONFIGURATION_NOT_FOUND", "project-not-found") - .put("INSUFFICIENT_PERMISSION", "insufficient-permission") - .put("DUPLICATE_EMAIL", "email-already-exists") - .put("DUPLICATE_LOCAL_ID", "uid-already-exists") - .put("EMAIL_EXISTS", "email-already-exists") - .put("INVALID_CLAIMS", "invalid-claims") - .put("INVALID_EMAIL", "invalid-email") - .put("INVALID_PAGE_SELECTION", "invalid-page-token") - .put("INVALID_PHONE_NUMBER", "invalid-phone-number") - .put("PHONE_NUMBER_EXISTS", "phone-number-already-exists") - .put("PROJECT_NOT_FOUND", "project-not-found") - .put("USER_NOT_FOUND", USER_NOT_FOUND_ERROR) - .put("WEAK_PASSWORD", "invalid-password") - .put("UNAUTHORIZED_DOMAIN", "unauthorized-continue-uri") - .put("INVALID_DYNAMIC_LINK_DOMAIN", "invalid-dynamic-link-domain") - .build(); - static final int MAX_LIST_USERS_RESULTS = 1000; static final int MAX_IMPORT_USERS = 1000; @@ -96,11 +70,11 @@ class FirebaseUserManager { private static final String ID_TOOLKIT_URL = "https://identitytoolkit.googleapis.com/v1/projects/%s"; private static final String CLIENT_VERSION_HEADER = "X-Client-Version"; + private static final String CLIENT_VERSION = "Java/Admin/" + SdkUtils.getVersion(); private final String baseUrl; private final JsonFactory jsonFactory; - private final HttpRequestFactory requestFactory; - private final String clientVersion = "Java/Admin/" + SdkUtils.getVersion(); + private final ErrorHandlingHttpClient httpClient; private HttpResponseInterceptor interceptor; @@ -122,12 +96,12 @@ class FirebaseUserManager { + "set the project ID via the GOOGLE_CLOUD_PROJECT environment variable."); this.baseUrl = String.format(ID_TOOLKIT_URL, projectId); this.jsonFactory = app.getOptions().getJsonFactory(); - if (requestFactory == null) { requestFactory = ApiClientUtils.newAuthorizedRequestFactory(app); } - this.requestFactory = requestFactory; + AuthErrorHandler authErrorHandler = new AuthErrorHandler(jsonFactory); + this.httpClient = new ErrorHandlingHttpClient<>(requestFactory, jsonFactory, authErrorHandler); } @VisibleForTesting @@ -138,66 +112,34 @@ void setInterceptor(HttpResponseInterceptor interceptor) { UserRecord getUserById(String uid) throws FirebaseAuthException { final Map payload = ImmutableMap.of( "localId", ImmutableList.of(uid)); - GetAccountInfoResponse response = post( - "/accounts:lookup", payload, GetAccountInfoResponse.class); - if (response == null || response.getUsers() == null || response.getUsers().isEmpty()) { - throw new FirebaseAuthException(USER_NOT_FOUND_ERROR, - "No user record found for the provided user ID: " + uid); - } - return new UserRecord(response.getUsers().get(0), jsonFactory); + return lookupUserAccount(payload, "user ID: " + uid); } UserRecord getUserByEmail(String email) throws FirebaseAuthException { final Map payload = ImmutableMap.of( "email", ImmutableList.of(email)); - GetAccountInfoResponse response = post( - "/accounts:lookup", payload, GetAccountInfoResponse.class); - if (response == null || response.getUsers() == null || response.getUsers().isEmpty()) { - throw new FirebaseAuthException(USER_NOT_FOUND_ERROR, - "No user record found for the provided email: " + email); - } - return new UserRecord(response.getUsers().get(0), jsonFactory); + return lookupUserAccount(payload, "email: " + email); } UserRecord getUserByPhoneNumber(String phoneNumber) throws FirebaseAuthException { final Map payload = ImmutableMap.of( "phoneNumber", ImmutableList.of(phoneNumber)); - GetAccountInfoResponse response = post( - "/accounts:lookup", payload, GetAccountInfoResponse.class); - if (response == null || response.getUsers() == null || response.getUsers().isEmpty()) { - throw new FirebaseAuthException(USER_NOT_FOUND_ERROR, - "No user record found for the provided phone number: " + phoneNumber); - } - return new UserRecord(response.getUsers().get(0), jsonFactory); + return lookupUserAccount(payload, "phone number: " + phoneNumber); } String createUser(CreateRequest request) throws FirebaseAuthException { GenericJson response = post( "/accounts", request.getProperties(), GenericJson.class); - if (response != null) { - String uid = (String) response.get("localId"); - if (!Strings.isNullOrEmpty(uid)) { - return uid; - } - } - throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to create new user"); + return (String) response.get("localId"); } void updateUser(UpdateRequest request, JsonFactory jsonFactory) throws FirebaseAuthException { - GenericJson response = post( - "/accounts:update", request.getProperties(jsonFactory), GenericJson.class); - if (response == null || !request.getUid().equals(response.get("localId"))) { - throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to update user: " + request.getUid()); - } + post("/accounts:update", request.getProperties(jsonFactory), GenericJson.class); } void deleteUser(String uid) throws FirebaseAuthException { final Map payload = ImmutableMap.of("localId", uid); - GenericJson response = post( - "/accounts:delete", payload, GenericJson.class); - if (response == null || !response.containsKey("kind")) { - throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to delete user: " + uid); - } + post("/accounts:delete", payload, GenericJson.class); } DownloadAccountResponse listUsers(int maxResults, String pageToken) throws FirebaseAuthException { @@ -210,21 +152,14 @@ DownloadAccountResponse listUsers(int maxResults, String pageToken) throws Fireb GenericUrl url = new GenericUrl(baseUrl + "/accounts:batchGet"); url.putAll(builder.build()); - DownloadAccountResponse response = sendRequest( - "GET", url, null, DownloadAccountResponse.class); - if (response == null) { - throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to retrieve users."); - } - return response; + return sendRequest( + HttpMethods.GET, url.toString(), null, DownloadAccountResponse.class); } UserImportResult importUsers(UserImportRequest request) throws FirebaseAuthException { checkNotNull(request); UploadAccountResponse response = post( - "/accounts:batchCreate", request, UploadAccountResponse.class); - if (response == null) { - throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to import users."); - } + "/accounts:batchCreate", request, UploadAccountResponse.class); return new UserImportResult(request.getUsersCount(), response); } @@ -233,13 +168,7 @@ String createSessionCookie(String idToken, final Map payload = ImmutableMap.of( "idToken", idToken, "validDuration", options.getExpiresInSeconds()); GenericJson response = post(":createSessionCookie", payload, GenericJson.class); - if (response != null) { - String cookie = (String) response.get("sessionCookie"); - if (!Strings.isNullOrEmpty(cookie)) { - return cookie; - } - } - throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to create session cookie"); + return (String) response.get("sessionCookie"); } String getEmailActionLink(EmailLinkType type, String email, @@ -251,71 +180,49 @@ String getEmailActionLink(EmailLinkType type, String email, if (settings != null) { payload.putAll(settings.getProperties()); } + GenericJson response = post("/accounts:sendOobCode", payload.build(), GenericJson.class); - if (response != null) { - String link = (String) response.get("oobLink"); - if (!Strings.isNullOrEmpty(link)) { - return link; - } + return (String) response.get("oobLink"); + } + + private UserRecord lookupUserAccount( + Map payload, String identifier) throws FirebaseAuthException { + IncomingHttpResponse response = sendRequest( + HttpMethods.POST, baseUrl + "/accounts:lookup", payload); + GetAccountInfoResponse parsed = httpClient.parse(response, GetAccountInfoResponse.class); + if (parsed.getUsers() == null || parsed.getUsers().isEmpty()) { + throw new FirebaseAuthException(ErrorCode.NOT_FOUND, + "No user record found for the provided " + identifier, + null, + response, + AuthErrorCode.USER_NOT_FOUND); } - throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to create email action link"); + + return new UserRecord(parsed.getUsers().get(0), jsonFactory); } private T post(String path, Object content, Class clazz) throws FirebaseAuthException { checkArgument(!Strings.isNullOrEmpty(path), "path must not be null or empty"); checkNotNull(content, "content must not be null for POST requests"); - GenericUrl url = new GenericUrl(baseUrl + path); - return sendRequest("POST", url, content, clazz); + return sendRequest(HttpMethods.POST, baseUrl + path, content, clazz); } private T sendRequest( - String method, GenericUrl url, - @Nullable Object content, Class clazz) throws FirebaseAuthException { - - checkArgument(!Strings.isNullOrEmpty(method), "method must not be null or empty"); - checkNotNull(url, "url must not be null"); - checkNotNull(clazz, "response class must not be null"); - HttpResponse response = null; - try { - HttpContent httpContent = content != null ? new JsonHttpContent(jsonFactory, content) : null; - HttpRequest request = requestFactory.buildRequest(method, url, httpContent); - request.setParser(new JsonObjectParser(jsonFactory)); - request.getHeaders().set(CLIENT_VERSION_HEADER, clientVersion); - request.setResponseInterceptor(interceptor); - response = request.execute(); - return response.parseAs(clazz); - } catch (HttpResponseException e) { - // Server responded with an HTTP error - handleHttpError(e); - return null; - } catch (IOException e) { - // All other IO errors (Connection refused, reset, parse error etc.) - throw new FirebaseAuthException( - INTERNAL_ERROR, "Error while calling user management backend service", e); - } finally { - if (response != null) { - try { - response.disconnect(); - } catch (IOException ignored) { - // Ignored - } - } - } + String method, String url, @Nullable Object content, Class clazz) + throws FirebaseAuthException { + + IncomingHttpResponse response = sendRequest(method, url, content); + return httpClient.parse(response, clazz); } - private void handleHttpError(HttpResponseException e) throws FirebaseAuthException { - try { - HttpErrorResponse response = jsonFactory.fromString(e.getContent(), HttpErrorResponse.class); - String code = ERROR_CODES.get(response.getErrorCode()); - if (code != null) { - throw new FirebaseAuthException(code, "User management service responded with an error", e); - } - } catch (IOException ignored) { - // Ignored - } - String msg = String.format( - "Unexpected HTTP response with status: %d; body: %s", e.getStatusCode(), e.getContent()); - throw new FirebaseAuthException(INTERNAL_ERROR, msg, e); + private IncomingHttpResponse sendRequest( + String method, String url, @Nullable Object content) throws FirebaseAuthException { + + HttpContent httpContent = content != null ? new JsonHttpContent(jsonFactory, content) : null; + HttpRequestInfo requestInfo = HttpRequestInfo.buildRequest(method, url, httpContent) + .addHeader(CLIENT_VERSION_HEADER, CLIENT_VERSION) + .setResponseInterceptor(interceptor); + return httpClient.send(requestInfo); } static class UserImportRequest extends GenericJson { diff --git a/src/main/java/com/google/firebase/auth/internal/HttpErrorResponse.java b/src/main/java/com/google/firebase/auth/internal/HttpErrorResponse.java deleted file mode 100644 index d4be4b4a6..000000000 --- a/src/main/java/com/google/firebase/auth/internal/HttpErrorResponse.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.firebase.auth.internal; - -import com.google.api.client.util.Key; -import com.google.common.base.Strings; - -/** - * JSON data binding for JSON error messages sent by Google identity toolkit service. - */ -public class HttpErrorResponse { - - @Key("error") - private Error error; - - public String getErrorCode() { - if (error != null) { - if (!Strings.isNullOrEmpty(error.getCode())) { - return error.getCode(); - } - } - return "unknown"; - } - - public static class Error { - - @Key("message") - private String code; - - public String getCode() { - return code; - } - } - -} diff --git a/src/main/java/com/google/firebase/internal/ErrorHandlingHttpClient.java b/src/main/java/com/google/firebase/internal/ErrorHandlingHttpClient.java index 7f9f859c4..e7d9e7373 100644 --- a/src/main/java/com/google/firebase/internal/ErrorHandlingHttpClient.java +++ b/src/main/java/com/google/firebase/internal/ErrorHandlingHttpClient.java @@ -106,7 +106,8 @@ public IncomingHttpResponse send(HttpRequestInfo requestInfo) throws T { } } - private V parse(IncomingHttpResponse response, Class responseType) throws T { + public V parse(IncomingHttpResponse response, Class responseType) throws T { + checkNotNull(responseType, "responseType must not be null"); try { JsonParser parser = jsonFactory.createJsonParser(response.getContent()); return parser.parseAndClose(responseType); diff --git a/src/main/java/com/google/firebase/internal/HttpRequestInfo.java b/src/main/java/com/google/firebase/internal/HttpRequestInfo.java index 8a29a8c7b..683ddcf73 100644 --- a/src/main/java/com/google/firebase/internal/HttpRequestInfo.java +++ b/src/main/java/com/google/firebase/internal/HttpRequestInfo.java @@ -16,12 +16,15 @@ package com.google.firebase.internal; +import static com.google.common.base.Preconditions.checkArgument; + import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpContent; import com.google.api.client.http.HttpMethods; import com.google.api.client.http.HttpRequest; import com.google.api.client.http.HttpRequestFactory; import com.google.api.client.http.HttpResponseInterceptor; +import com.google.common.base.Strings; import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -39,6 +42,8 @@ public final class HttpRequestInfo { private HttpResponseInterceptor interceptor; private HttpRequestInfo(String method, String url, HttpContent content) { + checkArgument(!Strings.isNullOrEmpty(method), "method must not be null"); + checkArgument(!Strings.isNullOrEmpty(url), "url must not be null"); this.method = method; this.url = new GenericUrl(url); this.content = content; @@ -60,7 +65,7 @@ public HttpRequestInfo setResponseInterceptor(HttpResponseInterceptor intercepto } public static HttpRequestInfo buildGetRequest(String url) { - return new HttpRequestInfo(HttpMethods.GET, url, null); + return buildRequest(HttpMethods.GET, url, null); } public static HttpRequestInfo buildDeleteRequest(String url) { @@ -68,7 +73,11 @@ public static HttpRequestInfo buildDeleteRequest(String url) { } public static HttpRequestInfo buildPostRequest(String url, HttpContent content) { - return new HttpRequestInfo(HttpMethods.POST, url, content); + return buildRequest(HttpMethods.POST, url, content); + } + + public static HttpRequestInfo buildRequest(String method, String url, HttpContent content) { + return new HttpRequestInfo(method, url, content); } HttpRequest newHttpRequest(HttpRequestFactory factory) throws IOException { diff --git a/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java b/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java index 6a8361d0d..804739e38 100644 --- a/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java +++ b/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java @@ -43,6 +43,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.io.BaseEncoding; import com.google.common.util.concurrent.MoreExecutors; +import com.google.firebase.ErrorCode; import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseOptions; import com.google.firebase.ImplFirebaseTrampolines; @@ -96,8 +97,14 @@ public void testGetNonExistingUser() throws Exception { fail("No error thrown for non existing uid"); } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); - assertEquals(FirebaseUserManager.USER_NOT_FOUND_ERROR, - ((FirebaseAuthException) e.getCause()).getErrorCode()); + FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); + assertEquals( + "No user record found for the provided user ID: non.existing", + authException.getMessage()); + assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCodeNew()); + assertNull(authException.getCause()); + assertNotNull(authException.getHttpResponse()); + assertEquals(AuthErrorCode.USER_NOT_FOUND, authException.getAuthErrorCode()); } } @@ -108,8 +115,14 @@ public void testGetNonExistingUserByEmail() throws Exception { fail("No error thrown for non existing email"); } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); - assertEquals(FirebaseUserManager.USER_NOT_FOUND_ERROR, - ((FirebaseAuthException) e.getCause()).getErrorCode()); + FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); + assertEquals( + "No user record found for the provided email: non.existing@definitely.non.existing", + authException.getMessage()); + assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCodeNew()); + assertNull(authException.getCause()); + assertNotNull(authException.getHttpResponse()); + assertEquals(AuthErrorCode.USER_NOT_FOUND, authException.getAuthErrorCode()); } } @@ -120,8 +133,14 @@ public void testUpdateNonExistingUser() throws Exception { fail("No error thrown for non existing uid"); } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); - assertEquals(FirebaseUserManager.USER_NOT_FOUND_ERROR, - ((FirebaseAuthException) e.getCause()).getErrorCode()); + FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); + assertEquals( + "No user record found for the given identifier (USER_NOT_FOUND).", + authException.getMessage()); + assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCodeNew()); + assertNotNull(authException.getCause()); + assertNotNull(authException.getHttpResponse()); + assertEquals(AuthErrorCode.USER_NOT_FOUND, authException.getAuthErrorCode()); } } @@ -132,8 +151,14 @@ public void testDeleteNonExistingUser() throws Exception { fail("No error thrown for non existing uid"); } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); - assertEquals(FirebaseUserManager.USER_NOT_FOUND_ERROR, - ((FirebaseAuthException) e.getCause()).getErrorCode()); + FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); + assertEquals( + "No user record found for the given identifier (USER_NOT_FOUND).", + authException.getMessage()); + assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCodeNew()); + assertNotNull(authException.getCause()); + assertNotNull(authException.getHttpResponse()); + assertEquals(AuthErrorCode.USER_NOT_FOUND, authException.getAuthErrorCode()); } } @@ -243,8 +268,14 @@ public void testUserLifecycle() throws Exception { fail("No error thrown for deleted user"); } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); - assertEquals(FirebaseUserManager.USER_NOT_FOUND_ERROR, - ((FirebaseAuthException) e.getCause()).getErrorCode()); + FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); + assertEquals( + "No user record found for the provided user ID: " + userRecord.getUid(), + authException.getMessage()); + assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCodeNew()); + assertNull(authException.getCause()); + assertNotNull(authException.getHttpResponse()); + assertEquals(AuthErrorCode.USER_NOT_FOUND, authException.getAuthErrorCode()); } } @@ -409,7 +440,7 @@ public void testVerifyIdToken() throws Exception { } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); assertEquals(RevocationCheckDecorator.ID_TOKEN_REVOKED_ERROR, - ((FirebaseAuthException) e.getCause()).getErrorCode()); + ((FirebaseAuthException) e.getCause()).getDeprecatedErrorCode()); } idToken = signInWithCustomToken(customToken); decoded = auth.verifyIdTokenAsync(idToken, true).get(); @@ -443,7 +474,7 @@ public void testVerifySessionCookie() throws Exception { } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); assertEquals(RevocationCheckDecorator.SESSION_COOKIE_REVOKED_ERROR, - ((FirebaseAuthException) e.getCause()).getErrorCode()); + ((FirebaseAuthException) e.getCause()).getDeprecatedErrorCode()); } idToken = signInWithCustomToken(customToken); @@ -692,7 +723,14 @@ private void checkRecreate(String uid) throws Exception { fail("No error thrown for creating user with existing ID"); } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); - assertEquals("uid-already-exists", ((FirebaseAuthException) e.getCause()).getErrorCode()); + FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); + assertEquals(ErrorCode.ALREADY_EXISTS, authException.getErrorCodeNew()); + assertEquals( + "The user with the provided uid already exists (DUPLICATE_LOCAL_ID).", + authException.getMessage()); + assertNotNull(authException.getCause()); + assertNotNull(authException.getHttpResponse()); + assertEquals(AuthErrorCode.UID_ALREADY_EXISTS, authException.getAuthErrorCode()); } } diff --git a/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java b/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java index 1bc05174f..3bc8ab2da 100644 --- a/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java +++ b/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java @@ -32,7 +32,6 @@ import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseOptions; import com.google.firebase.TestOnlyImplFirebaseTrampolines; -import com.google.firebase.auth.internal.FirebaseTokenFactory; import com.google.firebase.testing.ServiceAccount; import com.google.firebase.testing.TestUtils; import java.lang.reflect.InvocationTargetException; @@ -235,7 +234,7 @@ public void testVerifyIdTokenFailure() { auth.verifyIdToken("idtoken"); fail("No error thrown for invalid token"); } catch (FirebaseAuthException authException) { - assertEquals("TEST_CODE", authException.getErrorCode()); + assertEquals("TEST_CODE", authException.getDeprecatedErrorCode()); assertEquals("Test error message", authException.getMessage()); assertEquals("idtoken", tokenVerifier.getLastTokenString()); } @@ -264,7 +263,7 @@ public void testVerifyIdTokenAsyncFailure() throws InterruptedException { fail("No error thrown for invalid token"); } catch (ExecutionException e) { FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); - assertEquals("TEST_CODE", authException.getErrorCode()); + assertEquals("TEST_CODE", authException.getDeprecatedErrorCode()); assertEquals("Test error message", authException.getMessage()); assertEquals("idtoken", tokenVerifier.getLastTokenString()); } @@ -281,7 +280,7 @@ public void testVerifyIdTokenWithCheckRevokedAsyncFailure() throws InterruptedEx fail("No error thrown for invalid token"); } catch (ExecutionException e) { FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); - assertEquals("TEST_CODE", authException.getErrorCode()); + assertEquals("TEST_CODE", authException.getDeprecatedErrorCode()); assertEquals("Test error message", authException.getMessage()); assertEquals("idtoken", tokenVerifier.getLastTokenString()); } @@ -366,7 +365,7 @@ public void testVerifySessionCookieFailure() { auth.verifySessionCookie("idtoken"); fail("No error thrown for invalid token"); } catch (FirebaseAuthException authException) { - assertEquals("TEST_CODE", authException.getErrorCode()); + assertEquals("TEST_CODE", authException.getDeprecatedErrorCode()); assertEquals("Test error message", authException.getMessage()); assertEquals("idtoken", tokenVerifier.getLastTokenString()); } @@ -395,7 +394,7 @@ public void testVerifySessionCookieAsyncFailure() throws InterruptedException { fail("No error thrown for invalid token"); } catch (ExecutionException e) { FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); - assertEquals("TEST_CODE", authException.getErrorCode()); + assertEquals("TEST_CODE", authException.getDeprecatedErrorCode()); assertEquals("Test error message", authException.getMessage()); assertEquals("idtoken", tokenVerifier.getLastTokenString()); } @@ -412,7 +411,7 @@ public void testVerifySessionCookieWithCheckRevokedAsyncFailure() throws Interru fail("No error thrown for invalid token"); } catch (ExecutionException e) { FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); - assertEquals("TEST_CODE", authException.getErrorCode()); + assertEquals("TEST_CODE", authException.getDeprecatedErrorCode()); assertEquals("Test error message", authException.getMessage()); assertEquals("idtoken", tokenVerifier.getLastTokenString()); } diff --git a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java index de0b7fa29..29f1f170c 100644 --- a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java +++ b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java @@ -37,6 +37,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; +import com.google.firebase.ErrorCode; import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseOptions; import com.google.firebase.TestOnlyImplFirebaseTrampolines; @@ -54,7 +55,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; - import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.junit.After; @@ -117,7 +117,12 @@ public void testGetUserWithNotFoundError() throws Exception { } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); - assertEquals(FirebaseUserManager.USER_NOT_FOUND_ERROR, authException.getErrorCode()); + assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCodeNew()); + assertEquals( + "No user record found for the provided user ID: testuser", authException.getMessage()); + assertNull(authException.getCause()); + assertNotNull(authException.getHttpResponse()); + assertEquals(AuthErrorCode.USER_NOT_FOUND, authException.getAuthErrorCode()); } } @@ -140,7 +145,13 @@ public void testGetUserByEmailWithNotFoundError() throws Exception { } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); - assertEquals(FirebaseUserManager.USER_NOT_FOUND_ERROR, authException.getErrorCode()); + assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCodeNew()); + assertEquals( + "No user record found for the provided email: testuser@example.com", + authException.getMessage()); + assertNull(authException.getCause()); + assertNotNull(authException.getHttpResponse()); + assertEquals(AuthErrorCode.USER_NOT_FOUND, authException.getAuthErrorCode()); } } @@ -163,7 +174,13 @@ public void testGetUserByPhoneNumberWithNotFoundError() throws Exception { } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); - assertEquals(FirebaseUserManager.USER_NOT_FOUND_ERROR, authException.getErrorCode()); + assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCodeNew()); + assertEquals( + "No user record found for the provided phone number: +1234567890", + authException.getMessage()); + assertNull(authException.getCause()); + assertNotNull(authException.getHttpResponse()); + assertEquals(AuthErrorCode.USER_NOT_FOUND, authException.getAuthErrorCode()); } } @@ -182,7 +199,7 @@ public void testListUsers() throws Exception { checkRequestHeaders(interceptor); GenericUrl url = interceptor.getResponse().getRequest().getUrl(); - assertEquals(999, url.getFirst("maxResults")); + assertEquals("999", url.getFirst("maxResults")); assertNull(url.getFirst("nextPageToken")); } @@ -201,7 +218,7 @@ public void testListUsersWithPageToken() throws Exception { checkRequestHeaders(interceptor); GenericUrl url = interceptor.getResponse().getRequest().getUrl(); - assertEquals(999, url.getFirst("maxResults")); + assertEquals("999", url.getFirst("maxResults")); assertEquals("token", url.getFirst("nextPageToken")); } @@ -552,9 +569,15 @@ public void call(FirebaseAuth auth) throws Exception { MockLowLevelHttpResponse response = new MockLowLevelHttpResponse(); FirebaseAuth auth = getRetryDisabledAuth(response); + Map codes = ImmutableMap.of( + 302, ErrorCode.UNKNOWN, + 400, ErrorCode.INVALID_ARGUMENT, + 401, ErrorCode.UNAUTHENTICATED, + 404, ErrorCode.NOT_FOUND, + 500, ErrorCode.INTERNAL); // Test for common HTTP error codes - for (int code : ImmutableList.of(302, 400, 401, 404, 500)) { + for (int code : codes.keySet()) { for (UserManagerOp operation : operations) { // Need to reset these every iteration response.setContent("{}"); @@ -565,15 +588,17 @@ public void call(FirebaseAuth auth) throws Exception { } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); - String msg = String.format("Unexpected HTTP response with status: %d; body: {}", code); + assertEquals(codes.get(code), authException.getErrorCodeNew()); + String msg = String.format("Unexpected HTTP response with status: %d\n{}", code); assertEquals(msg, authException.getMessage()); assertTrue(authException.getCause() instanceof HttpResponseException); - assertEquals(FirebaseUserManager.INTERNAL_ERROR, authException.getErrorCode()); + assertNotNull(authException.getHttpResponse()); + assertNull(authException.getAuthErrorCode()); } } } - // Test error payload parsing + // Test error payload with code for (UserManagerOp operation : operations) { response.setContent("{\"error\": {\"message\": \"USER_NOT_FOUND\"}}"); response.setStatusCode(500); @@ -583,9 +608,33 @@ public void call(FirebaseAuth auth) throws Exception { } catch (ExecutionException e) { assertTrue(e.getCause().toString(), e.getCause() instanceof FirebaseAuthException); FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); - assertEquals("User management service responded with an error", authException.getMessage()); + assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCodeNew()); + assertEquals( + "No user record found for the given identifier (USER_NOT_FOUND).", + authException.getMessage()); + assertTrue(authException.getCause() instanceof HttpResponseException); + assertNotNull(authException.getHttpResponse()); + assertEquals(AuthErrorCode.USER_NOT_FOUND, authException.getAuthErrorCode()); + } + } + + // Test error payload with code and details + for (UserManagerOp operation : operations) { + response.setContent("{\"error\": {\"message\": \"USER_NOT_FOUND: Extra details\"}}"); + response.setStatusCode(500); + try { + operation.call(auth); + fail("No error thrown for HTTP error"); + } catch (ExecutionException e) { + assertTrue(e.getCause().toString(), e.getCause() instanceof FirebaseAuthException); + FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); + assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCodeNew()); + assertEquals( + "No user record found for the given identifier (USER_NOT_FOUND): Extra details", + authException.getMessage()); assertTrue(authException.getCause() instanceof HttpResponseException); - assertEquals(FirebaseUserManager.USER_NOT_FOUND_ERROR, authException.getErrorCode()); + assertNotNull(authException.getHttpResponse()); + assertEquals(AuthErrorCode.USER_NOT_FOUND, authException.getAuthErrorCode()); } } } @@ -599,8 +648,12 @@ public void testGetUserMalformedJsonError() throws Exception { } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); + assertEquals(ErrorCode.UNKNOWN, authException.getErrorCodeNew()); + assertTrue( + authException.getMessage().startsWith("Error while parsing HTTP response: ")); assertTrue(authException.getCause() instanceof IOException); - assertEquals(FirebaseUserManager.INTERNAL_ERROR, authException.getErrorCode()); + assertNotNull(authException.getHttpResponse()); + assertNull(authException.getAuthErrorCode()); } } @@ -616,10 +669,12 @@ public void testGetUserUnexpectedHttpError() throws Exception { } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); - assertTrue(authException.getCause() instanceof HttpResponseException); - assertEquals("Unexpected HTTP response with status: 500; body: {\"not\" json}", + assertEquals(ErrorCode.INTERNAL, authException.getErrorCodeNew()); + assertEquals("Unexpected HTTP response with status: 500\n{\"not\" json}", authException.getMessage()); - assertEquals(FirebaseUserManager.INTERNAL_ERROR, authException.getErrorCode()); + assertTrue(authException.getCause() instanceof HttpResponseException); + assertNotNull(authException.getHttpResponse()); + assertNull(authException.getAuthErrorCode()); } } @@ -1169,8 +1224,33 @@ public void testHttpErrorWithCode() { userManager.getEmailActionLink(EmailLinkType.PASSWORD_RESET, "test@example.com", null); fail("No exception thrown for HTTP error"); } catch (FirebaseAuthException e) { - assertEquals("unauthorized-continue-uri", e.getErrorCode()); + assertEquals(ErrorCode.INVALID_ARGUMENT, e.getErrorCodeNew()); + assertEquals( + "The domain of the continue URL is not whitelisted (UNAUTHORIZED_DOMAIN).", + e.getMessage()); + assertEquals(AuthErrorCode.UNAUTHORIZED_CONTINUE_URL, e.getAuthErrorCode()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + } + } + + @Test + public void testHttpErrorWithUnknownCode() { + String content = "{\"error\": {\"message\": \"SOMETHING_NEW\"}}"; + MockLowLevelHttpResponse response = new MockLowLevelHttpResponse() + .setContent(content) + .setStatusCode(500); + FirebaseAuth auth = getRetryDisabledAuth(response); + FirebaseUserManager userManager = auth.getUserManager(); + try { + userManager.getEmailActionLink(EmailLinkType.PASSWORD_RESET, "test@example.com", null); + fail("No exception thrown for HTTP error"); + } catch (FirebaseAuthException e) { + assertEquals(ErrorCode.INTERNAL, e.getErrorCodeNew()); + assertEquals("Unexpected HTTP response with status: 500\n" + content, e.getMessage()); + assertNull(e.getAuthErrorCode()); assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); } } @@ -1185,8 +1265,11 @@ public void testUnexpectedHttpError() { userManager.getEmailActionLink(EmailLinkType.PASSWORD_RESET, "test@example.com", null); fail("No exception thrown for HTTP error"); } catch (FirebaseAuthException e) { - assertEquals("internal-error", e.getErrorCode()); + assertEquals(ErrorCode.INTERNAL, e.getErrorCodeNew()); + assertEquals("Unexpected HTTP response with status: 500\n{}", e.getMessage()); assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertNull(e.getAuthErrorCode()); } } diff --git a/src/test/java/com/google/firebase/snippets/FirebaseAuthSnippets.java b/src/test/java/com/google/firebase/snippets/FirebaseAuthSnippets.java index fec3f5d2d..24b90ad45 100644 --- a/src/test/java/com/google/firebase/snippets/FirebaseAuthSnippets.java +++ b/src/test/java/com/google/firebase/snippets/FirebaseAuthSnippets.java @@ -254,7 +254,7 @@ public static void verifyIdTokenCheckRevoked(String idToken) { // Token is valid and not revoked. String uid = decodedToken.getUid(); } catch (FirebaseAuthException e) { - if (e.getErrorCode().equals("id-token-revoked")) { + if (e.getDeprecatedErrorCode().equals("id-token-revoked")) { // Token has been revoked. Inform the user to re-authenticate or signOut() the user. } else { // Token is invalid.