Skip to content
Advertisement

Firebase email+password authentication in Java

I’m trying to use Firebase email and password authentication in Java using their REST API, as their Admin SDK doesn’t provide the needed methods to log in etc., only user management methods.

With help from this answer, I’ve managed to put together the following code, which works for correct credentials but when trying to handle errors e.g. USER_NOT_FOUND or INVALID_PASSWORD, all I get is a java.io.IOException with the details Server returned HTTP response code: 400 for URL: https://www.googleapis.com/identitytoolkit/v3/relyingparty/getAccountInfo?key=key.

package com.amansprojects.craftclaw;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class FirebaseAuthManager {

    private static final String BASE_URL = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/";
    private static final String OPERATION_AUTH = "verifyPassword";
    private static final String OPERATION_ACCOUNT_INFO = "getAccountInfo";

    private String firebaseKey;

    private static FirebaseAuthManager instance = null;

    protected FirebaseAuthManager() {
        firebaseKey = "MY_KEY_HERE";
    }

    public static FirebaseAuthManager getInstance() {
        if (instance == null) {
            instance = new FirebaseAuthManager();
        }
        return instance;
    }

    public String auth(String username, String password) {
        HttpURLConnection urlRequest = null;
        String token = null;

        try {
            URL url = new URL(BASE_URL + OPERATION_AUTH + "?key=" + firebaseKey);
            urlRequest = (HttpURLConnection) url.openConnection();
            urlRequest.setDoOutput(true);
            urlRequest.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            OutputStream os = urlRequest.getOutputStream();
            OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
            osw.write("{"email": "" + username + "", "password": "" + password + "", "returnSecureToken": true}");
            osw.flush();
            osw.close();

            urlRequest.connect();

            JsonParser jp = new JsonParser();
            JsonElement root = jp.parse(new InputStreamReader((InputStream) urlRequest.getContent()));
            JsonObject rootObj = root.getAsJsonObject();

            token = rootObj.get("idToken").getAsString();
            System.out.println(rootObj); // debugging
        } catch (IOException e) { e.printStackTrace(); return null; }
        finally { urlRequest.disconnect(); }
        return token;
    }

    public String getAccountInfo(String token) {
        HttpURLConnection urlRequest = null;
        String email = null;

        try {
            URL url = new URL(BASE_URL + OPERATION_ACCOUNT_INFO + "?key=" + firebaseKey);
            urlRequest = (HttpURLConnection) url.openConnection();
            urlRequest.setDoOutput(true);
            urlRequest.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            OutputStream os = urlRequest.getOutputStream();
            OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
            osw.write("{"idToken": "" + token + ""}");
            osw.flush();
            osw.close();
            urlRequest.connect();

            JsonParser jp = new JsonParser();
            JsonElement root = jp.parse(new InputStreamReader((InputStream) urlRequest.getContent()));
            JsonObject rootObj = root.getAsJsonObject();

            email = rootObj.get("users").getAsJsonArray().get(0).getAsJsonObject().get("email").getAsString();
        } catch (IOException e) { e.printStackTrace(); return null; }
        finally { urlRequest.disconnect(); }
        return email;
    }
}

Thanks in advance.

Edit: I’ve also tried with the other domain/endpoint combination that Firebase shows on their docs page: https://identitytoolkit.googleapis.com/v1/accounts:endpoint

Advertisement

Answer

While debugging for another issue, I discovered that the Apache http libraries do not throw an IOException for 400 error codes and instead let you continue with parsing the JSON.

This is my final Firebase Auth REST API interfacing class in Java.

package com.amansprojects.craftclaw;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;

import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * A class to interface with the Firebase Auth REST API.
 */
public class FirebaseAuthManager {
    private static final String BASE_URL = "https://www.googleapis.com/identitytoolkit/v3/relyingparty/";
    private static final String OPERATION_AUTH = "verifyPassword";
    private static final String OPERATION_ACCOUNT_INFO = "getAccountInfo";
    private static final String OPERATION_SEND_PASSWORD_RESET = "getOobConfirmationCode";

    private final String firebaseKey;

    private static FirebaseAuthManager instance = null;

    protected FirebaseAuthManager() {
        firebaseKey = "YOUR_KEY_HERE";
    }

    public static FirebaseAuthManager getInstance() {
        if (instance == null) {
            instance = new FirebaseAuthManager();
        }
        return instance;
    }

    /**
     * Exchange an email and password with the Firebase Auth REST API for an ID token.
     * @param username A username or email registered with Firebase Authentication.
     * @param password The password associated with the username or email.
     * @return An ID token from Firebase.
     * @throws FirebaseAuthException
     */
    public String auth(String username, String password) throws FirebaseAuthException {
        String token;
        try {
            HttpClient httpclient = HttpClients.createDefault();
            HttpPost httppost = new HttpPost(BASE_URL + OPERATION_AUTH + "?key=" + firebaseKey);
            List<NameValuePair> params = new ArrayList<NameValuePair>(1);
            params.add(new BasicNameValuePair("email", username));
            params.add(new BasicNameValuePair("password", password));
            params.add(new BasicNameValuePair("returnSecureToken", "true"));
            httppost.setEntity(new UrlEncodedFormEntity(params, StandardCharsets.UTF_8));
            HttpEntity entity = httpclient.execute(httppost).getEntity();

            JsonParser jp = new JsonParser();
            JsonElement root = jp.parse(new InputStreamReader(entity.getContent()));
            JsonObject rootObj = root.getAsJsonObject();

            if (rootObj.get("error") != null) {
                throw new FirebaseAuthException(rootObj.get("error").getAsJsonObject().get("message").getAsString());
            }
            token = rootObj.get("idToken").getAsString();
        } catch (IOException e) { System.out.println(e.getMessage()); return null; }
        return token;
    }

    /**
     * Exchange an ID token with the Firebase Auth REST API for a User object.
     * @param token An ID token from Firebase.
     * @return A user object with the email and UID returned by Firebase.
     */
    public User getAccountInfo(String token) {
        try {
            HttpClient httpclient = HttpClients.createDefault();
            HttpPost httppost = new HttpPost(BASE_URL + OPERATION_ACCOUNT_INFO + "?key=" + firebaseKey);
            List<NameValuePair> params = new ArrayList<NameValuePair>(1);
            params.add(new BasicNameValuePair("idToken", token));
            httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
            HttpEntity entity = httpclient.execute(httppost).getEntity();

            JsonParser jp = new JsonParser();
            JsonElement root = jp.parse(new InputStreamReader(entity.getContent()));
            JsonObject rootObj = root.getAsJsonObject();

            JsonObject userObj = rootObj.get("users").getAsJsonArray().get(0).getAsJsonObject();
            return new User(userObj.get("email").getAsString(), userObj.get("localId").getAsString(), token);
        } catch (IOException e) { System.out.println(e.getMessage()); return null; }
    }

    /**
     * Send a reset password email via Firebase.
     * @param email The email address to send the reset password email to.
     */
    public void sendResetPasswordLink(String email) {
        try {
            HttpClient httpclient = HttpClients.createDefault();
            HttpPost httppost = new HttpPost(BASE_URL + OPERATION_SEND_PASSWORD_RESET + "?key=" + firebaseKey);
            List<NameValuePair> params = new ArrayList<NameValuePair>(2);
            params.add(new BasicNameValuePair("requestType", "PASSWORD_RESET"));
            params.add(new BasicNameValuePair("email", email));
            httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
            httpclient.execute(httppost);
        } catch (IOException e) { e.printStackTrace(); }
    }

    /**
     * Exchange a refresh token with the Firebase Auth REST API for a new ID token.
     * @param refreshToken The refresh token used to receive an ID token.
     * @return A new ID token from Firebase.
     */
    public String[] exchangeRefreshToken(String refreshToken) {
        try {
            HttpClient httpclient = HttpClients.createDefault();
            HttpPost httppost = new HttpPost("https://securetoken.googleapis.com/v1/token?key="+ firebaseKey);
            List<NameValuePair> params = new ArrayList<NameValuePair>(2);
            params.add(new BasicNameValuePair("grant_type", "refresh_token"));
            params.add(new BasicNameValuePair("refresh_token", refreshToken));
            httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
            HttpEntity entity = httpclient.execute(httppost).getEntity();
            if (entity != null) {
                JsonParser jp = new JsonParser();
                JsonElement root = jp.parse(new InputStreamReader(entity.getContent()));
                JsonObject rootObj = root.getAsJsonObject();
                return new String[]{ rootObj.get("id_token").getAsString(), rootObj.get("refresh_token").getAsString() };
            }
        } catch (IOException e) { e.printStackTrace(); }
        return null;
    }
}
2 People found this is helpful
Advertisement