9
votes

Can someone specify (with some sample code) how to verify the firebase token in an google cloud endpoint? The recently asked question does not clarifiy it at all (How to integrate firebase authentication with google app engine endpoints)

Google Authentication in endpoint is done automatically by adding the User Parameter to an endpoint. Facebook Tokens can be verified in an cloud endpoint with the facebook graph api like this:

    @ApiMethod(name = "endpoint.addUser", httpMethod = HttpMethod.POST)
        public ResultObject addUser(HttpServletRequest request, User pUser) throws OAuthRequestException {
    String token = request.getHeader("Authorization");
    String graphUrl  = "https://graph.facebook.com/v2.6/me?fields=id,name,email&access_token=" + token;

    URL u = new URL(g);
    URLConnection c = u.openConnection();
    BufferedReader in = new BufferedReader(new InputStreamReader(c.getInputStream()));
    String inputLine;
    StringBuffer b = new StringBuffer();
    while ((inputLine = in.readLine()) != null){
             b.append(inputLine + "\n");            
    }
    in.close();
    graph = b.toString();
    JSONObject json = new JSONObject(graph);

    facebookId = json.getString("id");
    email = json.getString("email");
    //...
}

Is the verification of the firebase token as easy as the facebook token? Is it possible to retrieve the email from an firebase token?

2
I can't precisely answer about Firebase, but I would say this is not the correct way to integrate auth. You should implement com.google.api.server.spi.config.Authenticator instead. Then specify the Authenticator in your annotation. I believe Firebase auth tokens are JWTs so they don't require sending a request to verify them.saiyr
yes thats corect. In a final implementation you would use Authenticator. I only wanted to include the code for demonstration.SmilingM
As for firebase the reason why i want to verify the token is, that i already have a running app engine endpoint. Now i want to use firebase auth in the client and authenticate against the cloud endpoint. I do not want to migrate to firebase realtime database for now.SmilingM
I think I found the solution to the problem under this link: firebase.google.com/docs/auth/server/verify-id-tokensSmilingM
@SmilingM Thanks, this is a problem for me also. The link you posted in this last comment shows how to verify Firebase tokens, but it's not clear how this would work with the User parameter of a Google Cloud Endpoint. Can you explain in an answer to your question? Thanks.aez

2 Answers

2
votes

As far as I understand the documentation it seems you need to add user token to your request, for example as a header. Then you need to verify this token against Firebase admin sdk, and this way you'd get user id.

@ApiMethod(name = "someApiCall", httpMethod = ApiMethod.HttpMethod.POST)
public YourResponse someApiCall(YourRequestObject body, HttpServletRequest httpRequest) {
    String userToken = httpRequest.getHeader("USER_TOKEN_HEADER");

    Task<FirebaseToken> authTask = FirebaseAuth.getInstance().verifyIdToken(userToken)
        .addOnSuccessListener(new OnSuccessListener<FirebaseToken>() {
          @Override
          public void onSuccess(FirebaseToken firebaseToken) {
          }
        });

    try {
      Tasks.await(authTask);
    } catch (ExecutionException e) {
    } catch (InterruptedException e) {
    }

    FirebaseToken result = authTask.getResult();
    String userId = result.getUid();

    return new YourResponse();
}

I based my code on:

https://firebase.google.com/docs/auth/admin/verify-id-tokens

How do I secure my Google Cloud Endpoints APIs with Firebase token verification?

1
votes

You can use a CustomAuthenticator:

public class CustomAuthenticator implements Authenticator {
    private static final Logger LOG = Logger.getLogger(CustomAuthenticator.class.getName());
    private static final String COOKIE_FIREBASE_TOKEN = "firebase_token";

    static {
        LOG.info("CustomAuthenticator: initializing");
        InputStream serviceAccountResourceStream = CustomAuthenticator.class.getResourceAsStream("/serviceAccountKey.json");
        FirebaseOptions options = new FirebaseOptions.Builder()
                .setServiceAccount(serviceAccountResourceStream)
                .build();

        FirebaseApp.initializeApp(options);
        LOG.info("CustomAuthenticator: initialized");
    }

    @Override
    public User authenticate(HttpServletRequest httpServletRequest) {
        User user = null;
        if (httpServletRequest.getCookies() != null) {
            for (Cookie cookie : httpServletRequest.getCookies()) {
                if (cookie.getName().equals(COOKIE_FIREBASE_TOKEN)) {
                    FirebaseToken firebaseToken = FirebaseAuth.getInstance().verifyIdToken(cookie.getValue()).getResult();
                    user = new User(firebaseToken.getUid(), firebaseToken.getEmail());
                }
            }
        }
        return user;
    }
}

In your API implementation, don't forget to enable your custom authenticator:

@Api(name = "exampleWithAuth",
        version = "v1",
        ...
        auth = @ApiAuth(allowCookieAuth = AnnotationBoolean.TRUE), // This is needed to process your cookie for the token
        authenticators = {CustomAuthenticator.class} // Declare your custom authenticator
)
public class ExampleWithAuthEndpoint {

    @ApiMethod(httpMethod = "GET", path = "example")
    public Example getExample(User user /* Add User to enable API authentication */) {
        if (user != null) {
            // Do something
        }
        return null;
    }
}

Now when you call your API, just add the cookie firebase_token to your request.

I hope this will help.