Skip to content

Make Auth an deferred dependency #2418

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion firebase-database/firebase-database.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ dependencies {
implementation 'com.google.android.gms:play-services-basement:17.0.0'
implementation 'com.google.android.gms:play-services-base:17.0.0'
implementation 'com.google.android.gms:play-services-tasks:17.0.0'
implementation('com.google.firebase:firebase-auth-interop:18.0.0') {
implementation('com.google.firebase:firebase-auth-interop:19.0.2') {
exclude group: "com.google.firebase", module: "firebase-common"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,11 @@ public static DatabaseConfig newTestConfig() {
config.setEventTarget(new TestEventTarget());
config.setRunLoop(runLoop);
config.setFirebaseApp(FirebaseApp.getInstance());
config.setAuthTokenProvider(AndroidAuthTokenProvider.forUnauthenticatedAccess());
config.setAuthTokenProvider(
new AndroidAuthTokenProvider(
never -> {
// Auth is not available in our integration tests
}));
config.setSessionPersistenceKey(UUID.randomUUID().toString());
return config;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ public List<Component<?>> getComponents() {
return Arrays.asList(
Component.builder(FirebaseDatabaseComponent.class)
.add(Dependency.required(FirebaseApp.class))
.add(Dependency.optional(InternalAuthProvider.class))
.add(Dependency.deferred(InternalAuthProvider.class))
.factory(
c ->
new FirebaseDatabaseComponent(
c.get(FirebaseApp.class), c.get(InternalAuthProvider.class)))
c.get(FirebaseApp.class), c.getDeferred(InternalAuthProvider.class)))
.build(),
LibraryVersionComponent.create("fire-rtdb", BuildConfig.VERSION_NAME));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
package com.google.firebase.database;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.firebase.FirebaseApp;
import com.google.firebase.auth.internal.InternalAuthProvider;
import com.google.firebase.database.android.AndroidAuthTokenProvider;
import com.google.firebase.database.core.AuthTokenProvider;
import com.google.firebase.database.core.DatabaseConfig;
import com.google.firebase.database.core.RepoInfo;
import com.google.firebase.inject.Deferred;
import java.util.HashMap;
import java.util.Map;

Expand All @@ -38,14 +38,9 @@ class FirebaseDatabaseComponent {
private final FirebaseApp app;
private final AuthTokenProvider authProvider;

FirebaseDatabaseComponent(@NonNull FirebaseApp app, @Nullable InternalAuthProvider authProvider) {
FirebaseDatabaseComponent(@NonNull FirebaseApp app, Deferred<InternalAuthProvider> authProvider) {
this.app = app;

if (authProvider != null) {
this.authProvider = AndroidAuthTokenProvider.forAuthenticatedAccess(authProvider);
} else {
this.authProvider = AndroidAuthTokenProvider.forUnauthenticatedAccess();
}
this.authProvider = new AndroidAuthTokenProvider(authProvider);
}

/** Provides instances of Firebase Database for the given RepoInfo */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,76 +18,67 @@
import com.google.android.gms.tasks.Task;
import com.google.firebase.FirebaseApiNotAvailableException;
import com.google.firebase.auth.GetTokenResult;
import com.google.firebase.auth.internal.IdTokenListener;
import com.google.firebase.auth.internal.InternalAuthProvider;
import com.google.firebase.database.core.AuthTokenProvider;
import com.google.firebase.inject.Deferred;
import com.google.firebase.internal.api.FirebaseNoSignedInUserException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;

public abstract class AndroidAuthTokenProvider implements AuthTokenProvider {
public class AndroidAuthTokenProvider implements AuthTokenProvider {
private final Deferred<InternalAuthProvider> deferredAuthProvider;
private final AtomicReference<InternalAuthProvider> internalAuth;

public static AuthTokenProvider forAuthenticatedAccess(
@NonNull final InternalAuthProvider authProvider) {
return new AuthTokenProvider() {
@Override
public void getToken(
boolean forceRefresh, @NonNull final GetTokenCompletionListener listener) {
Task<GetTokenResult> getTokenResult = authProvider.getAccessToken(forceRefresh);
public AndroidAuthTokenProvider(Deferred<InternalAuthProvider> deferredAuthProvider) {
this.deferredAuthProvider = deferredAuthProvider;
this.internalAuth = new AtomicReference<>();

getTokenResult
.addOnSuccessListener(result -> listener.onSuccess(result.getToken()))
.addOnFailureListener(
e -> {
if (isUnauthenticatedUsage(e)) {
listener.onSuccess(null);
} else {
// TODO: Figure out how to plumb errors through in a sane way.
listener.onError(e.getMessage());
}
});
}
deferredAuthProvider.whenAvailable(authProvider -> internalAuth.set(authProvider.get()));
}

@Override
public void addTokenChangeListener(
final ExecutorService executorService, final TokenChangeListener tokenListener) {
IdTokenListener idTokenListener =
tokenResult ->
executorService.execute(
() -> tokenListener.onTokenChange(/* nullable */ tokenResult.getToken()));
authProvider.addIdTokenListener(idTokenListener);
}
@Override
public void getToken(boolean forceRefresh, @NonNull final GetTokenCompletionListener listener) {
InternalAuthProvider authProvider = internalAuth.get();

@Override
public void removeTokenChangeListener(TokenChangeListener tokenListener) {
// TODO Implement removeIdTokenListener.
}
};
}
if (authProvider != null) {
Task<GetTokenResult> getTokenResult = authProvider.getAccessToken(forceRefresh);

public static AuthTokenProvider forUnauthenticatedAccess() {
return new AuthTokenProvider() {
@Override
public void getToken(boolean forceRefresh, GetTokenCompletionListener listener) {
listener.onSuccess(null);
}
getTokenResult
.addOnSuccessListener(result -> listener.onSuccess(result.getToken()))
.addOnFailureListener(
e -> {
if (isUnauthenticatedUsage(e)) {
listener.onSuccess(null);
} else {
// TODO: Figure out how to plumb errors through in a sane way.
listener.onError(e.getMessage());
}
});
} else {
listener.onSuccess(null);
}
}

@Override
public void addTokenChangeListener(
ExecutorService executorService, TokenChangeListener listener) {
executorService.execute(() -> listener.onTokenChange(null));
}
@Override
public void addTokenChangeListener(
final ExecutorService executorService, final TokenChangeListener tokenListener) {
deferredAuthProvider.whenAvailable(
provider ->
provider
.get()
.addIdTokenListener(
tokenResult ->
executorService.execute(
() -> tokenListener.onTokenChange(tokenResult.getToken()))));
}

@Override
public void removeTokenChangeListener(TokenChangeListener listener) {}
};
@Override
public void removeTokenChangeListener(TokenChangeListener tokenListener) {
// TODO Implement removeIdTokenListener.
}

private static boolean isUnauthenticatedUsage(Exception e) {
if (e instanceof FirebaseApiNotAvailableException
|| e instanceof FirebaseNoSignedInUserException) {
return true;
}

return false;
return e instanceof FirebaseApiNotAvailableException
|| e instanceof FirebaseNoSignedInUserException;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,11 @@ public static DatabaseConfig newTestConfig() {
config.setEventTarget(new TestEventTarget());
config.setRunLoop(runLoop);
config.setFirebaseApp(FirebaseApp.getInstance());
config.setAuthTokenProvider(AndroidAuthTokenProvider.forUnauthenticatedAccess());
config.setAuthTokenProvider(
new AndroidAuthTokenProvider(
never -> {
// Auth is not available in our unit tests
}));
return config;
}

Expand Down