getClaims() {
+ return this.claims;
+ }
+}
diff --git a/src/main/java/com/google/firebase/appcheck/FirebaseAppCheck.java b/src/main/java/com/google/firebase/appcheck/FirebaseAppCheck.java
new file mode 100644
index 000000000..7ce1b1d0a
--- /dev/null
+++ b/src/main/java/com/google/firebase/appcheck/FirebaseAppCheck.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * 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.appcheck;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.api.core.ApiFuture;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.ImplFirebaseTrampolines;
+import com.google.firebase.internal.CallableOperation;
+import com.google.firebase.internal.FirebaseService;
+import com.google.firebase.internal.NonNull;
+
+/**
+ * This class is the entry point for all server-side Firebase App Check actions.
+ *
+ * You can get an instance of {@link FirebaseAppCheck} via {@link #getInstance(FirebaseApp)},
+ * and then use it to access App Check services.
+ */
+public final class FirebaseAppCheck {
+
+ private static final String SERVICE_ID = FirebaseAppCheck.class.getName();
+ private final FirebaseApp app;
+ private final FirebaseAppCheckClient appCheckClient;
+
+ @VisibleForTesting
+ FirebaseAppCheck(FirebaseApp app, FirebaseAppCheckClient client) {
+ this.app = checkNotNull(app);
+ this.appCheckClient = checkNotNull(client);
+ }
+
+ private FirebaseAppCheck(FirebaseApp app) {
+ this(app, FirebaseAppCheckClientImpl.fromApp(app));
+ }
+
+ /**
+ * Gets the {@link FirebaseAppCheck} instance for the default {@link FirebaseApp}.
+ *
+ * @return The {@link FirebaseAppCheck} instance for the default {@link FirebaseApp}.
+ */
+ public static FirebaseAppCheck getInstance() {
+ return getInstance(FirebaseApp.getInstance());
+ }
+
+ /**
+ * Gets the {@link FirebaseAppCheck} instance for the specified {@link FirebaseApp}.
+ *
+ * @return The {@link FirebaseAppCheck} instance for the specified {@link FirebaseApp}.
+ */
+ public static synchronized FirebaseAppCheck getInstance(FirebaseApp app) {
+ FirebaseAppCheck.FirebaseAppCheckService service = ImplFirebaseTrampolines.getService(app,
+ SERVICE_ID,
+ FirebaseAppCheck.FirebaseAppCheckService.class);
+ if (service == null) {
+ service = ImplFirebaseTrampolines.addService(app,
+ new FirebaseAppCheck.FirebaseAppCheckService(app));
+ }
+ return service.getInstance();
+ }
+
+ /**
+ * Verifies a given App Check Token.
+ *
+ * @param token The App Check token to be verified.
+ * @return A {@link VerifyAppCheckTokenResponse}.
+ * @throws FirebaseAppCheckException If an error occurs while getting the template.
+ */
+ public VerifyAppCheckTokenResponse verifyToken(
+ @NonNull String token) throws FirebaseAppCheckException {
+ return verifyTokenOp(token).call();
+ }
+
+ /**
+ * Similar to {@link #verifyToken(String token)} but performs the operation
+ * asynchronously.
+ *
+ * @param token The App Check token to be verified.
+ * @return An {@code ApiFuture} that completes with a {@link VerifyAppCheckTokenResponse} when
+ * the provided token is valid.
+ */
+ public ApiFuture verifyTokenAsync(@NonNull String token)
+ throws FirebaseAppCheckException {
+ return verifyTokenOp(token).callAsync(app);
+ }
+
+ private CallableOperation verifyTokenOp(
+ final String token) {
+ final FirebaseAppCheckClient appCheckClient = getAppCheckClient();
+ return new CallableOperation() {
+ @Override
+ protected VerifyAppCheckTokenResponse execute() throws FirebaseAppCheckException {
+ return appCheckClient.verifyToken(token);
+ }
+ };
+ }
+
+ @VisibleForTesting
+ FirebaseAppCheckClient getAppCheckClient() {
+ return appCheckClient;
+ }
+
+ private static class FirebaseAppCheckService extends FirebaseService {
+ FirebaseAppCheckService(FirebaseApp app) {
+ super(SERVICE_ID, new FirebaseAppCheck(app));
+ }
+ }
+}
diff --git a/src/main/java/com/google/firebase/appcheck/FirebaseAppCheckClient.java b/src/main/java/com/google/firebase/appcheck/FirebaseAppCheckClient.java
new file mode 100644
index 000000000..85423da42
--- /dev/null
+++ b/src/main/java/com/google/firebase/appcheck/FirebaseAppCheckClient.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * 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.appcheck;
+
+/**
+ * An interface for managing Firebase App Check services.
+ */
+interface FirebaseAppCheckClient {
+
+ /**
+ * Gets the current active version of the App Check template.
+ *
+ * @return A {@link VerifyAppCheckTokenResponse}.
+ * @throws FirebaseAppCheckException If an error occurs while getting the template.
+ */
+ VerifyAppCheckTokenResponse verifyToken(String token) throws FirebaseAppCheckException;
+}
diff --git a/src/main/java/com/google/firebase/appcheck/FirebaseAppCheckClientImpl.java b/src/main/java/com/google/firebase/appcheck/FirebaseAppCheckClientImpl.java
new file mode 100644
index 000000000..c164a92c7
--- /dev/null
+++ b/src/main/java/com/google/firebase/appcheck/FirebaseAppCheckClientImpl.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * 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.appcheck;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Strings;
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.ImplFirebaseTrampolines;
+
+/**
+ * A helper class for interacting with Firebase App Check service.
+ */
+final class FirebaseAppCheckClientImpl implements FirebaseAppCheckClient {
+ private final AppCheckTokenVerifier tokenVerifier;
+
+ private FirebaseAppCheckClientImpl(FirebaseAppCheckClientImpl.Builder builder) {
+ checkArgument(!Strings.isNullOrEmpty(builder.projectId));
+ checkArgument(builder.appCheckTokenVerifier != null);
+ this.tokenVerifier = builder.appCheckTokenVerifier;
+ }
+
+ static FirebaseAppCheckClientImpl fromApp(FirebaseApp app) {
+ String projectId = ImplFirebaseTrampolines.getProjectId(app);
+ checkArgument(!Strings.isNullOrEmpty(projectId),
+ "Project ID is required to access App Check service. Use a service "
+ + "account credential or set the project ID explicitly via FirebaseOptions. "
+ + "Alternatively you can also set the project ID via the GOOGLE_CLOUD_PROJECT "
+ + "environment variable.");
+ AppCheckTokenVerifier appCheckTokenVerifier = AppCheckTokenVerifier.builder()
+ .setProjectId(projectId)
+ .build();
+ return FirebaseAppCheckClientImpl.builder()
+ .setProjectId(projectId)
+ .setAppCheckTokenVerifier(appCheckTokenVerifier)
+ .build();
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ public VerifyAppCheckTokenResponse verifyToken(String token) throws FirebaseAppCheckException {
+ DecodedAppCheckToken decodedAppCheckToken = tokenVerifier.verifyToken(token);
+ return new VerifyAppCheckTokenResponse(decodedAppCheckToken);
+ }
+
+ static final class Builder {
+
+ private String projectId;
+ private AppCheckTokenVerifier appCheckTokenVerifier;
+
+ private Builder() {
+ }
+
+ Builder setProjectId(String projectId) {
+ this.projectId = projectId;
+ return this;
+ }
+
+ Builder setAppCheckTokenVerifier(AppCheckTokenVerifier appCheckTokenVerifier) {
+ this.appCheckTokenVerifier = appCheckTokenVerifier;
+ return this;
+ }
+
+ FirebaseAppCheckClientImpl build() {
+ return new FirebaseAppCheckClientImpl(this);
+ }
+ }
+}
diff --git a/src/main/java/com/google/firebase/appcheck/FirebaseAppCheckException.java b/src/main/java/com/google/firebase/appcheck/FirebaseAppCheckException.java
new file mode 100644
index 000000000..63eca70e9
--- /dev/null
+++ b/src/main/java/com/google/firebase/appcheck/FirebaseAppCheckException.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * 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.appcheck;
+
+import com.google.common.annotations.VisibleForTesting;
+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 App Check. Check the error code and message for more
+ * details.
+ */
+public final class FirebaseAppCheckException extends FirebaseException {
+
+ private final AppCheckErrorCode errorCode;
+
+ @VisibleForTesting
+ FirebaseAppCheckException(@NonNull ErrorCode code, @NonNull String message) {
+ this(code, message, null, null, null);
+ }
+
+ public FirebaseAppCheckException(
+ @NonNull ErrorCode errorCode,
+ @NonNull String message,
+ @Nullable Throwable cause,
+ @Nullable IncomingHttpResponse response,
+ @Nullable AppCheckErrorCode appCheckErrorCode) {
+ super(errorCode, message, cause, response);
+ this.errorCode = appCheckErrorCode;
+ }
+
+ static FirebaseAppCheckException withAppCheckErrorCode(
+ FirebaseException base, @Nullable AppCheckErrorCode errorCode) {
+ return new FirebaseAppCheckException(
+ base.getErrorCode(),
+ base.getMessage(),
+ base.getCause(),
+ base.getHttpResponse(),
+ errorCode);
+ }
+
+ @Nullable
+ public AppCheckErrorCode getAppCheckErrorCode() {
+ return errorCode;
+ }
+}
diff --git a/src/main/java/com/google/firebase/appcheck/VerifyAppCheckTokenResponse.java b/src/main/java/com/google/firebase/appcheck/VerifyAppCheckTokenResponse.java
new file mode 100644
index 000000000..a23e945b7
--- /dev/null
+++ b/src/main/java/com/google/firebase/appcheck/VerifyAppCheckTokenResponse.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * 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.appcheck;
+
+/**
+ * Interface representing a verified App Check token response.
+ */
+public final class VerifyAppCheckTokenResponse {
+ private final String appId;
+ private final DecodedAppCheckToken token;
+
+ VerifyAppCheckTokenResponse(DecodedAppCheckToken token) {
+ this.appId = token.getSubject();
+ this.token = token;
+ }
+
+ /** Returns the App ID for this token. */
+ public String getAppId() {
+ return this.appId;
+ }
+
+ /** Returns the decoded AppCheck token. */
+ public DecodedAppCheckToken getToken() {
+ return this.token;
+ }
+}
diff --git a/src/test/java/com/google/firebase/appcheck/FirebaseAppCheckTest.java b/src/test/java/com/google/firebase/appcheck/FirebaseAppCheckTest.java
new file mode 100644
index 000000000..e86754e16
--- /dev/null
+++ b/src/test/java/com/google/firebase/appcheck/FirebaseAppCheckTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2022 Google LLC
+ *
+ * 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.appcheck;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.firebase.FirebaseApp;
+import com.google.firebase.FirebaseOptions;
+import com.google.firebase.TestOnlyImplFirebaseTrampolines;
+import com.google.firebase.auth.MockGoogleCredentials;
+import org.junit.After;
+import org.junit.Test;
+
+public class FirebaseAppCheckTest {
+
+ private static final FirebaseOptions TEST_OPTIONS = FirebaseOptions.builder()
+ .setCredentials(new MockGoogleCredentials("test-token"))
+ .setProjectId("test-project")
+ .build();
+
+ @After
+ public void tearDown() {
+ TestOnlyImplFirebaseTrampolines.clearInstancesForTest();
+ }
+
+ @Test
+ public void testGetInstance() {
+ FirebaseApp.initializeApp(TEST_OPTIONS);
+
+ FirebaseAppCheck appCheck = FirebaseAppCheck.getInstance();
+
+ assertSame(appCheck, FirebaseAppCheck.getInstance());
+ }
+
+ @Test
+ public void testGetInstanceByApp() {
+ FirebaseApp app = FirebaseApp.initializeApp(TEST_OPTIONS, "custom-app");
+
+ FirebaseAppCheck appCheck = FirebaseAppCheck.getInstance(app);
+
+ assertSame(appCheck, FirebaseAppCheck.getInstance(app));
+ }
+
+ @Test
+ public void testDefaultAppCheckClient() {
+ FirebaseApp app = FirebaseApp.initializeApp(TEST_OPTIONS, "custom-app");
+ FirebaseAppCheck appCheck = FirebaseAppCheck.getInstance(app);
+
+ FirebaseAppCheckClient client = appCheck.getAppCheckClient();
+
+ assertTrue(client instanceof FirebaseAppCheckClientImpl);
+ assertSame(client, appCheck.getAppCheckClient());
+ }
+
+ @Test
+ public void testAppDelete() {
+ FirebaseApp app = FirebaseApp.initializeApp(TEST_OPTIONS, "custom-app");
+ FirebaseAppCheck appCheck = FirebaseAppCheck.getInstance(app);
+ assertNotNull(appCheck);
+
+ app.delete();
+
+ try {
+ FirebaseAppCheck.getInstance(app);
+ fail("No error thrown when getting app check instance after deleting app");
+ } catch (IllegalStateException expected) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testAppCheckClientWithoutProjectId() {
+ FirebaseOptions options = FirebaseOptions.builder()
+ .setCredentials(new MockGoogleCredentials("test-token"))
+ .build();
+ FirebaseApp.initializeApp(options);
+
+ try {
+ FirebaseAppCheck.getInstance();
+ fail("No error thrown for missing project ID");
+ } catch (IllegalArgumentException expected) {
+ String message = "Project ID is required to access App Check service. Use a service "
+ + "account credential or set the project ID explicitly via FirebaseOptions. "
+ + "Alternatively you can also set the project ID via the GOOGLE_CLOUD_PROJECT "
+ + "environment variable.";
+ assertEquals(message, expected.getMessage());
+ }
+ }
+}