Google Cloud Endpoint executes asynchronous code in the endpoint
I’m trying to validate a client token created by the Firebase authentication library in Android in the GCE endpoint.
A guide on how to do this can be found here
Basically I need to call this code fragment from the endpoint (i.e. server backend code instead of android code).
FirebaseAuth.getInstance().verifyIdToken(idToken)
.addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
@Override
public void onSuccess(FirebaseToken decodedToken) {
String uid = decodedToken.getUid();
// ...
}
});
Let’s say I want to execute that code and return the user to the android client code. What should I do?
Here is my sample code that doesn’t make any sense. But it proves what I want to do!
@ApiMethod(name = "serverAuth")
public MyUser serverAuth(@Named("token") String token) {
FirebaseAuth.getInstance().verifyIdToken(token)
.addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
@Override
public void onSuccess(FirebaseToken decodedToken) {
String uid = decodedToken.getUid();
String email = decodedToken.getEmail();
String name = decodedToken.getName();
Map<String, Object> claims = decodedToken.getClaims();
String claimString = "";
for (Object claim : claims.values()) {
claimString += claims.toString();
}
MyUser user = new MyUser(uid, email, name, claimString);
How to return this user?
}
});
This is compile error since user varriable does not exist here
return user;
}
I googled how to execute async code in the GCE endpoint. But nowhere. What I get is something about code execution that keeps blocking until it’s done and then goes back to the user. But how do you write code to make the above asynchronous code blocking?
Solution
CountDownLatch is the magic class you need. It will make you wait until OnSuccessListener is actually done.
Adjust your approach this way: (I removed the step that led to the creation of MyUser so I can focus on the point.) )
@ApiMethod(name = "serverAuth")
public MyUser serverAuth(@Named("token") String token) {
final List<MyUser> users = new ArrayList<>();
final CountDownLatch cdl = new CountDownLatch(1);
FirebaseAuth.getInstance().verifyIdToken(token)
.addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
@Override
public void onSuccess(FirebaseToken decodedToken) {
// ... init uid, email, name and claimString
users.add(new MyUser(uid, email, name, claimString));
cdl.countDown();
}
});
try {
cdl.await(); This line blocks execution till count down latch is 0
} catch (InterruptedException ie) {
}
if (users.size() > 0) {
return users.get(0);
} else {
return null ;
}
}
This is the basic version you need. IMHO, it also needs 2 improvements:
You should also consider the possibility of failure:
FirebaseAuth.getInstance().verifyIdToken(token) .addOnSuccessListener(new OnSuccessListener<FirebaseToken>() { @Override public void onSuccess(FirebaseToken decodedToken) { cdl.countDown(); } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { log error, ... cdl.countDown(); } });
You should also consider the possibility that no listeners are invoked. In this case, your method will never return. To avoid this, you can set a timeout on the await() method:
try { This line blocks execution till count down latch is 0 or after 30 seconds. cdl.await(30l, TimeUnit.SECONDS); } catch (InterruptedException ie) { }
That’s it. Hope this might help.