Skip to content
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</execution>
</executions>
</plugin>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.firebase.auth.internal.Utils.isEmulatorMode;

import com.google.api.client.json.JsonFactory;
import com.google.api.client.util.Clock;
Expand Down Expand Up @@ -309,7 +310,7 @@ protected FirebaseToken execute() throws FirebaseAuthException {
@VisibleForTesting
FirebaseTokenVerifier getIdTokenVerifier(boolean checkRevoked) {
FirebaseTokenVerifier verifier = idTokenVerifier.get();
if (checkRevoked) {
if (checkRevoked || isEmulatorMode()) {
FirebaseUserManager userManager = getUserManager();
verifier = RevocationCheckDecorator.decorateIdTokenVerifier(verifier, userManager);
}
Expand Down Expand Up @@ -389,7 +390,7 @@ public FirebaseToken execute() throws FirebaseAuthException {
@VisibleForTesting
FirebaseTokenVerifier getSessionCookieVerifier(boolean checkRevoked) {
FirebaseTokenVerifier verifier = cookieVerifier.get();
if (checkRevoked) {
if (checkRevoked || isEmulatorMode()) {
FirebaseUserManager userManager = getUserManager();
verifier = RevocationCheckDecorator.decorateSessionCookieVerifier(verifier, userManager);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.firebase.ErrorCode;
import com.google.firebase.auth.internal.Utils;
import com.google.firebase.internal.Nullable;
import java.io.IOException;
import java.math.BigDecimal;
Expand Down Expand Up @@ -94,9 +95,12 @@ private FirebaseTokenVerifierImpl(Builder builder) {
*/
@Override
public FirebaseToken verifyToken(String token) throws FirebaseAuthException {
boolean isEmulatorMode = Utils.isEmulatorMode();
IdToken idToken = parse(token);
checkContents(idToken);
checkSignature(idToken);
checkContents(idToken, isEmulatorMode);
if (!isEmulatorMode) {
checkSignature(idToken);
}
FirebaseToken firebaseToken = new FirebaseToken(idToken.getPayload());
checkTenantId(firebaseToken);
return firebaseToken;
Expand Down Expand Up @@ -160,17 +164,18 @@ private void checkSignature(IdToken token) throws FirebaseAuthException {
}
}

private void checkContents(final IdToken idToken) throws FirebaseAuthException {
private void checkContents(final IdToken idToken, boolean isEmulatorMode)
throws FirebaseAuthException {
final Header header = idToken.getHeader();
final Payload payload = idToken.getPayload();

final long currentTimeMillis = idTokenVerifier.getClock().currentTimeMillis();
String errorMessage = null;
AuthErrorCode errorCode = invalidTokenErrorCode;

if (header.getKeyId() == null) {
if (!isEmulatorMode && header.getKeyId() == null) {
errorMessage = getErrorForTokenWithoutKid(header, payload);
} else if (!RS256.equals(header.getAlgorithm())) {
} else if (!isEmulatorMode && !RS256.equals(header.getAlgorithm())) {
errorMessage = String.format(
"Firebase %s has incorrect algorithm. Expected \"%s\" but got \"%s\".",
shortName,
Expand Down
14 changes: 12 additions & 2 deletions src/main/java/com/google/firebase/auth/FirebaseUserManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import com.google.firebase.auth.internal.ListOidcProviderConfigsResponse;
import com.google.firebase.auth.internal.ListSamlProviderConfigsResponse;
import com.google.firebase.auth.internal.UploadAccountResponse;
import com.google.firebase.auth.internal.Utils;
import com.google.firebase.internal.ApiClientUtils;
import com.google.firebase.internal.HttpRequestInfo;
import com.google.firebase.internal.NonNull;
Expand Down Expand Up @@ -72,6 +73,8 @@ final class FirebaseUserManager {

private static final String ID_TOOLKIT_URL =
"https://identitytoolkit.googleapis.com/%s/projects/%s";
private static final String ID_TOOLKIT_URL_EMULATOR =
"http://%s/identitytoolkit.googleapis.com/%s/projects/%s";

private final String userMgtBaseUrl;
private final String idpConfigMgtBaseUrl;
Expand All @@ -85,8 +88,8 @@ private FirebaseUserManager(Builder builder) {
+ "set the project ID explicitly via FirebaseOptions. Alternatively you can also "
+ "set the project ID via the GOOGLE_CLOUD_PROJECT environment variable.");
this.jsonFactory = checkNotNull(builder.jsonFactory, "JsonFactory must not be null");
final String idToolkitUrlV1 = String.format(ID_TOOLKIT_URL, "v1", projectId);
final String idToolkitUrlV2 = String.format(ID_TOOLKIT_URL, "v2", projectId);
final String idToolkitUrlV1 = getIdToolkitUrl(projectId, "v1");
final String idToolkitUrlV2 = getIdToolkitUrl(projectId, "v2");
final String tenantId = builder.tenantId;
if (tenantId == null) {
this.userMgtBaseUrl = idToolkitUrlV1;
Expand All @@ -100,6 +103,13 @@ private FirebaseUserManager(Builder builder) {
this.httpClient = new AuthHttpClient(jsonFactory, builder.requestFactory);
}

private String getIdToolkitUrl(String projectId, String version) {
if (Utils.isEmulatorMode()) {
return String.format(ID_TOOLKIT_URL_EMULATOR, Utils.getEmulatorHost(), version, projectId);
}
return String.format(ID_TOOLKIT_URL, version, projectId);
}

@VisibleForTesting
void setInterceptor(HttpResponseInterceptor interceptor) {
httpClient.setInterceptor(interceptor);
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/com/google/firebase/auth/internal/CryptoSigners.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.google.firebase.FirebaseException;
import com.google.firebase.ImplFirebaseTrampolines;
import com.google.firebase.auth.FirebaseAuthException;
import com.google.firebase.auth.internal.Utils;
import com.google.firebase.internal.AbstractPlatformErrorHandler;
import com.google.firebase.internal.ApiClientUtils;
import com.google.firebase.internal.ErrorHandlingHttpClient;
Expand Down Expand Up @@ -108,6 +109,25 @@ public String getAccount() {
}
}

/**
* A {@link CryptoSigner} implementation that doesn't sign data. For use with the Auth Emulator
* only
*/
public static class EmulatorCryptoSigner implements CryptoSigner {

private static final String ACCOUNT = "firebase-auth-emulator@example.com";

@Override
public byte[] sign(byte[] payload) {
return "".getBytes();
}

@Override
public String getAccount() {
return ACCOUNT;
}
}

private static class IAMErrorHandler
extends AbstractPlatformErrorHandler<FirebaseAuthException> {

Expand All @@ -126,6 +146,10 @@ protected FirebaseAuthException createException(FirebaseException base) {
* documented at go/firebase-admin-sign.
*/
public static CryptoSigner getCryptoSigner(FirebaseApp firebaseApp) throws IOException {
if (Utils.isEmulatorMode()) {
return new EmulatorCryptoSigner();
}

GoogleCredentials credentials = ImplFirebaseTrampolines.getCredentials(firebaseApp);

// If the SDK was initialized with a service account, use it to sign bytes.
Expand Down
34 changes: 34 additions & 0 deletions src/main/java/com/google/firebase/auth/internal/Utils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright 2021 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.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;

public class Utils {
@VisibleForTesting
public static final String AUTH_EMULATOR_HOST = "FIREBASE_AUTH_EMULATOR_HOST";

public static boolean isEmulatorMode() {
return !Strings.isNullOrEmpty(getEmulatorHost());
}

public static String getEmulatorHost() {
return System.getenv(AUTH_EMULATOR_HOST);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import com.google.firebase.auth.FirebaseAuthException;
import com.google.firebase.auth.internal.AuthHttpClient;
import com.google.firebase.auth.internal.ListTenantsResponse;
import com.google.firebase.auth.internal.Utils;
import com.google.firebase.internal.ApiClientUtils;
import com.google.firebase.internal.HttpRequestInfo;
import java.util.Map;
Expand All @@ -42,6 +43,9 @@ final class FirebaseTenantClient {
private static final String ID_TOOLKIT_URL =
"https://identitytoolkit.googleapis.com/%s/projects/%s";

private static final String ID_TOOLKIT_URL_EMULATOR =
"http://%s/identitytoolkit.googleapis.com/%s/projects/%s";

private final String tenantMgtBaseUrl;
private final AuthHttpClient httpClient;

Expand All @@ -58,10 +62,17 @@ final class FirebaseTenantClient {
"Project ID is required to access the auth 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.");
this.tenantMgtBaseUrl = String.format(ID_TOOLKIT_URL, "v2", projectId);
this.tenantMgtBaseUrl = getTenantMgtBaseUrl(projectId);
this.httpClient = new AuthHttpClient(jsonFactory, requestFactory);
}

private String getTenantMgtBaseUrl(String projectId) {
if (Utils.isEmulatorMode()) {
return String.format(ID_TOOLKIT_URL_EMULATOR, Utils.getEmulatorHost(), "v2", projectId);
}
return String.format(ID_TOOLKIT_URL, "v2", projectId);
}

void setInterceptor(HttpResponseInterceptor interceptor) {
httpClient.setInterceptor(interceptor);
}
Expand Down
Loading