-
Notifications
You must be signed in to change notification settings - Fork 617
FIS getAuthToken implementation. #769
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
Changes from 8 commits
c54292e
b4b6ba8
b8bfc1a
7a2a112
42c20f6
46d936c
48fd7fe
82fb69e
fe04e15
e91cb7e
8569952
b74b163
58a30e3
96e30c2
9e3e34d
824cb63
90673c3
f5b4f8c
3e29941
75f2581
91e49bc
4578880
7d19b50
fe04884
f1d1d37
ec65040
396d273
81b4aeb
3932d3b
8d4d811
3f44b77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,6 +38,7 @@ | |
import com.google.firebase.installations.remote.FirebaseInstallationServiceClient; | ||
import com.google.firebase.installations.remote.FirebaseInstallationServiceException; | ||
import com.google.firebase.installations.remote.InstallationResponse; | ||
import com.google.firebase.installations.remote.InstallationTokenResult; | ||
ciarand marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import java.util.concurrent.ExecutionException; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.SynchronousQueue; | ||
|
@@ -92,9 +93,16 @@ public void setUp() throws FirebaseInstallationServiceException { | |
.setAuthToken( | ||
InstallationTokenResult.builder() | ||
.setToken(TEST_AUTH_TOKEN) | ||
.setTokenExpirationTimestampMillis(TEST_TOKEN_EXPIRATION_TIMESTAMP) | ||
.setTokenExpirationInSecs(TEST_TOKEN_EXPIRATION_TIMESTAMP) | ||
.build()) | ||
.build()); | ||
when(backendClientReturnsOk.generateAuthToken( | ||
anyString(), anyString(), anyString(), anyString())) | ||
.thenReturn( | ||
InstallationTokenResult.builder() | ||
.setToken(TEST_AUTH_TOKEN) | ||
.setTokenExpirationInSecs(TEST_TOKEN_EXPIRATION_TIMESTAMP) | ||
.build()); | ||
when(backendClientReturnsError.createFirebaseInstallation( | ||
anyString(), anyString(), anyString(), anyString())) | ||
.thenThrow( | ||
|
@@ -217,4 +225,22 @@ public void testGetId_PersistedFidError_BackendOk() throws InterruptedException | |
.isEqualTo(FirebaseInstallationsException.Status.CLIENT_ERROR); | ||
} | ||
} | ||
|
||
@Test | ||
public void testGetAuthToken_registeredFid_successful() throws Exception { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just added one test to review the IntDef usage. I will add more tests, once that is reviewed . Thank you. |
||
FirebaseInstallations firebaseInstallations = | ||
new FirebaseInstallations( | ||
mockClock, executor, firebaseApp, backendClientReturnsOk, persistedFid, mockUtils); | ||
firebaseInstallations.setRefreshAuthTokenOption(FirebaseInstallationsApi.DO_NOT_FORCE_REFRESH); | ||
|
||
Tasks.await(firebaseInstallations.getAuthToken()); | ||
|
||
// Waiting for Task that registers FID on the FIS Servers | ||
executor.awaitTermination(1000, TimeUnit.MILLISECONDS); | ||
|
||
PersistedFidEntry entryValue = persistedFid.readPersistedFidEntryValue(); | ||
assertWithMessage("Persisted Auth Token doesn't match") | ||
.that(entryValue.getAuthToken()) | ||
.isEqualTo(TEST_AUTH_TOKEN); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,7 +29,8 @@ | |
import com.google.firebase.installations.remote.FirebaseInstallationServiceClient; | ||
import com.google.firebase.installations.remote.FirebaseInstallationServiceException; | ||
import com.google.firebase.installations.remote.InstallationResponse; | ||
import java.util.concurrent.Executor; | ||
import com.google.firebase.installations.remote.InstallationTokenResult; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.SynchronousQueue; | ||
import java.util.concurrent.ThreadPoolExecutor; | ||
import java.util.concurrent.TimeUnit; | ||
|
@@ -47,13 +48,28 @@ | |
*/ | ||
public class FirebaseInstallations implements FirebaseInstallationsApi { | ||
|
||
@RefreshAuthTokenOption int refreshAuthTokenOption; | ||
|
||
@Override | ||
public void setRefreshAuthTokenOption(@RefreshAuthTokenOption int mode) { | ||
refreshAuthTokenOption = mode; | ||
} | ||
|
||
@Override | ||
@RefreshAuthTokenOption | ||
public int getRefreshAuthTokenOption() { | ||
return refreshAuthTokenOption; | ||
} | ||
|
||
private final FirebaseApp firebaseApp; | ||
private final FirebaseInstallationServiceClient serviceClient; | ||
private final PersistedFid persistedFid; | ||
private final Executor executor; | ||
private final ExecutorService executor; | ||
private final Clock clock; | ||
private final Utils utils; | ||
|
||
private static final long AUTH_TOKEN_EXPIRATION_BUFFER = 3600L; // 1 hour | ||
|
||
/** package private constructor. */ | ||
FirebaseInstallations(FirebaseApp firebaseApp) { | ||
this( | ||
|
@@ -67,7 +83,7 @@ public class FirebaseInstallations implements FirebaseInstallationsApi { | |
|
||
FirebaseInstallations( | ||
Clock clock, | ||
Executor executor, | ||
ExecutorService executor, | ||
FirebaseApp firebaseApp, | ||
FirebaseInstallationServiceClient serviceClient, | ||
PersistedFid persistedFid, | ||
|
@@ -115,11 +131,17 @@ public Task<String> getId() { | |
.onSuccessTask(this::registerFidIfNecessary); | ||
} | ||
|
||
/** Returns a auth token(public key) of this Firebase app installation. */ | ||
/** | ||
* Returns a valid authentication token for the Firebase installation. Generates a new token if | ||
* one doesn't exist, is expired or about to expire. | ||
* | ||
* <p>Should only be called if the Firebase Installation is registered. | ||
*/ | ||
@NonNull | ||
@Override | ||
public Task<InstallationTokenResult> getAuthToken(boolean forceRefresh) { | ||
return Tasks.forResult(InstallationTokenResult.builder().build()); | ||
public Task<String> getAuthToken() { | ||
return getId() | ||
.continueWith(executor, call(() -> refreshAuthTokenIfNecessary(refreshAuthTokenOption))); | ||
} | ||
|
||
/** | ||
|
@@ -169,6 +191,15 @@ private static <F, T> Continuation<F, T> orElse(@NonNull Supplier<T> supplier) { | |
}; | ||
} | ||
|
||
@NonNull | ||
private <F, T> Continuation<F, T> call(@NonNull Supplier<T> supplier) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. give this method a more specific name? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. PTAL. |
||
return t -> { | ||
// Waiting for Task that registers FID on the FIS Servers | ||
executor.awaitTermination(1000, TimeUnit.MILLISECONDS); | ||
ankitaj224 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return supplier.get(); | ||
}; | ||
} | ||
|
||
private PersistedFidEntry createAndPersistNewFid() throws FirebaseInstallationsException { | ||
ankitaj224 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
String fid = utils.createRandomFid(); | ||
persistFid(fid); | ||
|
@@ -214,7 +245,7 @@ private void updatePersistedFidWithPendingStatus(String fid) { | |
private Void registerAndSaveFid(PersistedFidEntry persistedFidEntry) | ||
throws FirebaseInstallationsException { | ||
try { | ||
long creationTime = TimeUnit.MILLISECONDS.toSeconds(clock.currentTimeMillis()); | ||
long creationTime = currentTime(); | ||
|
||
InstallationResponse installationResponse = | ||
serviceClient.createFirebaseInstallation( | ||
|
@@ -228,8 +259,7 @@ private Void registerAndSaveFid(PersistedFidEntry persistedFidEntry) | |
.setRegistrationStatus(RegistrationStatus.REGISTERED) | ||
.setAuthToken(installationResponse.getAuthToken().getToken()) | ||
.setRefreshToken(installationResponse.getRefreshToken()) | ||
.setExpiresInSecs( | ||
installationResponse.getAuthToken().getTokenExpirationTimestampMillis()) | ||
.setExpiresInSecs(installationResponse.getAuthToken().getTokenExpirationInSecs()) | ||
.setTokenCreationEpochInSecs(creationTime) | ||
.build()); | ||
|
||
|
@@ -244,6 +274,86 @@ private Void registerAndSaveFid(PersistedFidEntry persistedFidEntry) | |
} | ||
return null; | ||
} | ||
|
||
private String refreshAuthTokenIfNecessary(int refreshAuthTokenOption) | ||
throws FirebaseInstallationsException { | ||
|
||
PersistedFidEntry persistedFidEntry = persistedFid.readPersistedFidEntryValue(); | ||
|
||
if (persistedFidEntry == null) { | ||
ankitaj224 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
throw new FirebaseInstallationsException( | ||
"Failed to create Firebase Installation.", | ||
FirebaseInstallationsException.Status.SDK_INTERNAL_ERROR); | ||
} | ||
|
||
switch (refreshAuthTokenOption) { | ||
case FORCE_REFRESH: | ||
return fetchAuthTokenFromServer(persistedFidEntry); | ||
case DO_NOT_FORCE_REFRESH: | ||
return getPersistedAuthToken(persistedFidEntry); | ||
default: | ||
throw new FirebaseInstallationsException( | ||
"Incorrect refreshAuthTokenOption.", | ||
FirebaseInstallationsException.Status.SDK_INTERNAL_ERROR); | ||
} | ||
} | ||
|
||
private String getPersistedAuthToken(PersistedFidEntry persistedFidEntry) | ||
throws FirebaseInstallationsException { | ||
if (!isPersistedFidRegistered(persistedFidEntry)) { | ||
throw new FirebaseInstallationsException( | ||
"Firebase Installation is not registered.", | ||
FirebaseInstallationsException.Status.SDK_INTERNAL_ERROR); | ||
} | ||
|
||
return isAuthTokenExpired(persistedFidEntry) | ||
? fetchAuthTokenFromServer(persistedFidEntry) | ||
: persistedFidEntry.getAuthToken(); | ||
} | ||
|
||
private boolean isPersistedFidRegistered(PersistedFidEntry persistedFidEntry) { | ||
return persistedFidEntry != null | ||
&& persistedFidEntry.getRegistrationStatus() == RegistrationStatus.REGISTERED; | ||
} | ||
|
||
/** Calls the FIS servers to generate an auth token for this Firebase installation. */ | ||
private String fetchAuthTokenFromServer(PersistedFidEntry persistedFidEntry) | ||
throws FirebaseInstallationsException { | ||
try { | ||
long creationTime = currentTime(); | ||
InstallationTokenResult tokenResult = | ||
serviceClient.generateAuthToken( | ||
/*apiKey= */ firebaseApp.getOptions().getApiKey(), | ||
/*fid= */ persistedFidEntry.getFirebaseInstallationId(), | ||
/*projectID= */ firebaseApp.getOptions().getProjectId(), | ||
/*refreshToken= */ persistedFidEntry.getRefreshToken()); | ||
|
||
persistedFid.insertOrUpdatePersistedFidEntry( | ||
PersistedFidEntry.builder() | ||
.setFirebaseInstallationId(persistedFidEntry.getFirebaseInstallationId()) | ||
.setRegistrationStatus(RegistrationStatus.REGISTERED) | ||
.setAuthToken(tokenResult.getToken()) | ||
.setRefreshToken(persistedFidEntry.getRefreshToken()) | ||
.setExpiresInSecs(tokenResult.getTokenExpirationInSecs()) | ||
.setTokenCreationEpochInSecs(creationTime) | ||
.build()); | ||
|
||
return tokenResult.getToken(); | ||
} catch (FirebaseInstallationServiceException exception) { | ||
throw new FirebaseInstallationsException( | ||
"Failed to generate auth token for a Firebase Installation.", | ||
FirebaseInstallationsException.Status.SDK_INTERNAL_ERROR); | ||
} | ||
} | ||
|
||
private boolean isAuthTokenExpired(PersistedFidEntry persistedFidEntry) { | ||
return (persistedFidEntry.getTokenCreationEpochInSecs() + persistedFidEntry.getExpiresInSecs() | ||
> currentTime() + AUTH_TOKEN_EXPIRATION_BUFFER); | ||
} | ||
|
||
private long currentTime() { | ||
ankitaj224 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return TimeUnit.MILLISECONDS.toSeconds(clock.currentTimeMillis()); | ||
} | ||
} | ||
|
||
interface Supplier<T> { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright 2019 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.installations.remote; | ||
|
||
import androidx.annotation.NonNull; | ||
import com.google.auto.value.AutoValue; | ||
|
||
/** This class represents a set of values describing a FIS Auth Token Result. */ | ||
@AutoValue | ||
public abstract class InstallationTokenResult { | ||
|
||
/** A new FIS Auth-Token, created for this Firebase Installation. */ | ||
@NonNull | ||
public abstract String getToken(); | ||
/** | ||
* The amount of time, in milliseconds, before the auth-token expires for this Firebase | ||
* Installation. | ||
*/ | ||
@NonNull | ||
public abstract long getTokenExpirationInSecs(); | ||
|
||
@NonNull | ||
public abstract Builder toBuilder(); | ||
|
||
/** Returns a default Builder object to create an InstallationResponse object */ | ||
@NonNull | ||
public static InstallationTokenResult.Builder builder() { | ||
return new AutoValue_InstallationTokenResult.Builder(); | ||
} | ||
|
||
@AutoValue.Builder | ||
public abstract static class Builder { | ||
@NonNull | ||
public abstract Builder setToken(@NonNull String value); | ||
|
||
@NonNull | ||
public abstract Builder setTokenExpirationInSecs(@NonNull long value); | ||
|
||
@NonNull | ||
public abstract InstallationTokenResult build(); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.