If you've built for App Engine since its inception in 2008, you might have used its authorization flow. It's simple—add login: required
in app.yaml
, or require login programmatically—and now you have your users' Google account. 👍
Q: How do I use that with Firebase, which has its own auth system?
Naïvely, you could throw away your existing auth flows, and use Firebase directly. However, that will disrupt your users—they'll have to sign in again, which could be unexpected. 😲
The Answer
The answer is: you can't have your users automagically login with their Google account in Firebase. You ask: then why am I reading this post?! 🤔
So, while Firebase has amazing support for signing in with multiple providers—even your phone number! 📞—you actually can skip Firebase's signin code completely and create users directly, using the first strategy in the diagram below.
So we can create whole new users and generate what's known as a Custom Token, for those users to interact with Firebase's services. These users aren't technically associated with an existing Google account—since only your backend creates them, you can assert their validity.
(The second case is the more traditional model—Firebase has 'providers' for different login systems. But you can't migrate to this if you're using an existing login system, even if that login system 'matches'.)
Custom Token
To create a Custom Token, you can use the Firebase Admin SDK, which has support for Go, Node.JS, Python and Java.
First—if you're using dev_appserver
, you'll likely need to set up the credentials to your service account—this is the account that's actually signing you in. Head to the Firebase console's "Settings" » "Service Accounts" and download the private key. In your shell, run something like:
export GOOGLE_APPLICATION_CREDENTIALS="/full/path/to/file/serviceAccountKey.json"
# then you can start your app
dev_appserver.py app.yaml
⚠️ Never store your private key in a public place or check it in!
I'll demonstrate how to use CustomToken
inside a Go app. Inside the code, you'll need to use the App Engine user package to get access to the current user, passing its ID to Firebase's CustomToken
method:
// 0. create firebase.App
app, err := firebase.NewApp(ctx, nil)
if err != nil { ... }
// 1. get the current App Engine user
u := user.Current(ctx)
if u == nil || u.ID == "" { ... }
// 2. create auth.Auth
client, err := app.Auth(ctx)
if err != nil { ... }
// 3. use auth.Auth to mint a token for an ID, creating the account as nessecary
token, err := client.CustomToken(u.ID)
if err != nil { ... }
// 4. send token to client- profit!
Remember
The the
CustomToken
method will transparently create a user account with the ID you specify—this is the crux of this guide!While this example uses
u.ID
(the user's email), it could be any unique string. Firebase's built-in providers use UUIDs which don't look like email addresses, so it'd be impossible to have collisions.
Client Integration
Once you've minted the token, you'll need to send it to your client. I'm going to talk about the web, but Firebase's docs detail native clients.
In my projects, I tend to include the token in the first HTML response, so it's immediately available to your code—either via a JS global or an attribute:
<script>window.firebaseToken = '{{TOKEN}}';</script>
<!-- or -->
<body data-firebase-token="{{TOKEN}}">
Then in your JS, you can just wait for the auth to complete:
const token = window.firebaseToken || document.body.dataset.firebaseToken;
await firebase.auth().signInWithCustomToken(token);
const user = firebase.auth().currentUser;
console.info('user is', user);
From here on in, any requests you make to the Firebase APIs—like the awesome, scalable Firestore real-time database—are authenticated against this user account that you've just created. 🎉
Remember
Because this is an account we made up, and not really associated with a Google login, all this token is good for is talking to Firebase. 🔥
This token expires after an hour, so you have to use it to
signInWithCustomToken
immediately. This might affect you if e.g., the token gets cached inside a Service Worker.
That's it—you're done! But there are two extensions:
1️⃣ Multiple Auth Providers
Firebase allows you to link multiple auth providers to one account. This gives you two interesting options:
- You can migrate away from App Engine's auth flows, as users naturally sign in and out again
- You can let users sign in with additional accounts, e.g. Facebook, Twitter: which is a powerful feature of Firebase's auth model.
To read more about account linking, see Google's docs.
2️⃣ Claims
When you create an account using CustomToken
, you can also indicate 'claims'. These are essentially tags you can look up later, perhaps when verifying the Firebase token. Some examples might include 'paidAccount', 'restricted', etc.
See the overall docs on creating Custom Tokens.
Thanks
I hope you've learned how to integrate your existing App Engine auth flow with Firebase. If you have questions, contact me on Twitter or leave a comment below. ✒️👇
Top comments (0)