No Firebase Admin SDK for Go? No Problem ...
Firebase is great and makes serverless apps very easy to spin up but there’s always going to be times you need to access the Firebase data and / or user accounts from your server code (e.g. to sync accounts with a legacy system while we move to a Firebase auth enabled client).
There is an Official Admin SDK for Node.js, Java and Python but not all of them support user administration and poor Go doesn’t even get a mention (sad face).
But it turns out, we don’t need an official Firebase SDK for Go to be able to access the database and / or manage user accounts - all the packages to do it are already available and it’s very easy to use.
Let’s start with the database as it’s likely to be the most common requirement. We’re going to authenticate with Firebase from our Go code and then access the database via REST.
The first thing we need is a service-account credentials file. This is generated and downloaded from the Firebase console - under Settings > Service accounts:
Don’t worry that Go isn’t listed. I’m sure they will add an official Admin SDK for it at some point but all we need are the credentials. Save the JSON file it gives you and keep it safe. You won’t be able to regenerate the same file and having a copy will provide full access to the Firebase project so treat it accordingly.
Creating an authenticated client is very simple in Go using the golang.org/x/oauth2
package and golang.org/x/oauth2/google
sub-package which handles the parsing of the credentials file:
package main
import (
"net/http"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
)
// FirebaseClient returns an *http.Client authorized to access firebase
// using the service account credentials file provided
func FirebaseClient(credentialsFile string) (*http.Client, error) {
b, err := ioutil.ReadFile(credentialsFile)
if err != nil {
return nil, err
}
conf, err := google.JWTConfigFromJSON(b, "https://www.googleapis.com/auth/firebase.database", "https://www.googleapis.com/auth/userinfo.email")
if err != nil {
return nil, err
}
client := conf.Client(oauth2.NoContext)
return client, nil
}
The OAuth client handles the business of authenticating and generating or refreshing access tokens (there are things you can do to have these saved, if it’s for a long-running service this may not be necessary).
Creating this client is simply a case of passing the location of the credentials JSON file we saved earlier:
client, err := FirebaseClient("firebase-credentials.json")
We can then use it to access the Firebase Database using the REST API:
resp, err := client.Get("https://captain-codeman.firebaseio.com/topics.json")
if err != nil {
return err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
// or decode JSON from resp.Body
See the REST Reference Docs for details for how to query the database.
The user accounts in your project don’t appear anywhere in your Firebase database (although you may have user profile info that you allow users to set which will).
It turns out that the Firebase Auth system was previously known as the Google Identity Toolkit and has since been rebranded. Looking through the source of the Node Admin SDK confirms that this is what it uses to provide access to user accounts from the server so it’s safe to use from Go - we just have to authenticate and call the right API.
In typical Google fashion, there are several packages we could use to do this. There is the auto-generated Identity Toolkit API which isn’t the friendliest or most convenient to use so instead we’ll go with the cool sounding “gitkit” Google Identity Toolkit Go Client.
If we’re running on a Google cloud platform host such as AppEngine or Compute Engine then there is a good chance that this will “just work” because it uses the Google Application Default Credentials which does make things easier once you figure out all the IAM permissions and exactly what account and role your service is running under (which needs to be granted access to firebase).
But I find it easier to use the service account file and this also means you can develop and test locally (although there are options to set things up with the default credentials on your dev machine too).
package main
import (
"github.com/google/identity-toolkit-go-client/gitkit"
)
// GitkitClient returns a *gitkit.Client to access the Google
// Identity Toolkit data using the service account credentials
// file provided
func GitkitClient(credentialsFile string) (*gitkit.Client, error) {
return gitkit.New(oauth2.NoContext, &gitkit.Config{
GoogleAppCredentialsPath: credentialsFile,
})
}
This gives us a convenient method to create a client similar to how we did earlier:
client, err := GitkitClient("firebase-credentials.json")
This client provides methods to use to fetch, manipulate and create users (called “UploadUsers” if you have trouble finding it). Here’s a simple example to list use emails and names:
list := client.ListUsers(ctx)
for u := range list.C {
println(u.Email, u.DisplayName)
}
I expect at some point all these things will be wrapped up in a more convenient and branded “Firebase Admin SDK for Go” which will hopefully also include real-time database access but until then, there’s nothing to prevent us rolling our own.