An Authul Backend

Emlyn O'Regan
Jan 30, 2018 · 9 min read

Adding python appengine APIs to a firebase front end app, which respect firebase authentication

This article is part 2 of Truly Authul:

Ok, so in the last article I did Firebase Authentication in the web front end of a basic app. But the point was to show how we can use Firebase Authentication in a Python App Engine app.

This is a Single Page App approach, so I’m not rendering pages on the back end in the old server-side pages way. Instead, the static frontend, once authed, will talk to APIs exposed by the back end, then update itself as required.

So what we want to do is step 2 of this diagram:

In step 1, our web app communicated with Firebase and Facebook, and authenticated the user with a Facebook login.

In step 2, our web app will make an api call using a token provided by the Firebase auth service in the front end, and the back end will verify that token and service the API call (or not, if verification fails).

I’ll start by copying the codebase from Truly Authul, here:

to this new repo:

and making the edits there.

The API

What did Truly Authul do?

It provides some sign in / sign out stuff, and shows an appropriate message.

Let’s move generation of that message to the back end.

Great. So what will the api call look like?

Let’s call it welcomemessage.

We’ll call it with an HTTP GET.

The token can go in a header, accesstoken.

We don’t need any arguments on the querystring or anything like that.

We’ll allow the API to be called either authenticated or unauthenticated, ie: token or no, and return an appropriate welcome message in each case.

We’ll return the message in the body, in a json message like this:

{
"message": ...
}

using the mime type application/json.

1: Implement the front end

Ok, so instead of calculating the user message in the front end, we’ll call the api. The front end is in src/templates/trulyauthul.html, we’ll stay with this.

Currently the welcome message is set in two places, inside the firebase auth onAuthStateChanged handler:

if (user) {
...
user.getIdToken().then(function(accessToken) {
document.getElementById('sign-in-status').textContent = 'Hi ' + user.displayName + ", welcome to Truly Authul!";
...
} else {
...
document.getElementById('sign-in-status').textContent = 'Hello there! Please sign in to access Truly Authul';
...

Let’s refactor that a bit.

if (user) {
...
user.getIdToken().then(function(accessToken) {
document.getElementById('sign-in-status').textContent = "loading...";
getWelcomeMessage(accessToken, function(message)
{
document.getElementById('sign-in-status').textContent = message;
}
);
...
} else {
...
document.getElementById('sign-in-status').textContent = "loading...";
getWelcomeMessage(null, function(message)
{
document.getElementById('sign-in-status').textContent = message;
}
);
...

Notice that now we’re going to set the welcome message asynchronously, by calling a function called getWelcomeMessage(), passing it the token if we’ve got it, and a handler that can actually set a resulting message into the right element. Also notice that because this is asynchronous, we set the message text to “loading…” until the real value is available.

Ok, so how do we write getWelcomeMessage()? I guess we need an AJAX call. When I write scrappy stuff like this I usually use jquery here, but let’s resist that. Instead, for chuckles, let’s do it the vanilla way.

In later articles I’ll be adopting an actual framework, vue.js likely, then we’ll do something more sensible.

var getWelcomeMessage = function(accessToken, onwelcomemessage)
{
var xmlhttp = new XMLHttpRequest();

xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == XMLHttpRequest.DONE)
{
if (xmlhttp.status == 200)
{
response = JSON.parse(xmlhttp.responseText);
onwelcomemessage(response.message || "no message");
}
else {
onwelcomemessage(
xmlhttp.statusText || "Error " + xmlhttp.status
);
}
}
};

xmlhttp.open("GET", "/api/welcomemessage", true);
if (accessToken)
{
xmlhttp.setRequestHeader("accesstoken", accessToken)
};
xmlhttp.send();
}

Ok, I want to see this go. I’m expecting a call to the back end that throws an error (because no api), and I should see some error indication instead of a welcome message.

Deployment is interesting. I want to use the same project that I used to do trulyauthul1, because I’ve already done all this setup work with firebase and facebook. But, I want to leave the first version intact so the previous article still makes sense.

The best answer is to use a different app engine version. I’m going to call this v2, and upload it using this command (in scripts/deploy.sh):

gcloud app deploy app.yaml --project trulyauthul --version v2 --verbosity=info

Now I’ve got two versions, which I can see in the google cloud console:

This new codebase isn’t the default, so we get to it via

https://v2-dot-trulyauthul.appspot.com/

And what’s it look like?

Yup, as expected.

2: Implement the back end

Ok, so now let’s make this API work.

I’m using Flask, so I need a new function to implement the API. I’ll add it to the existing trulyauthul.py:

from flask import render_template

def get_trulyauthul(app):
@app.route('/', methods=["GET"])
def trulyauthul():
return render_template("trulyauthul.html")

@app.route('/api/welcomemessage', methods=["GET"])
def welcomemessage():
# implement me...

(If you’re using webapp2, no shame in that, just read this stuff like a RequestHandler get() method)

Ok, how do we implement welcomemessage() ?

The steps involved are:

1 — Get the access token, if it exists, from the request header

2 — If we have an access token, do firebase stuff to validate it and get the user

3 — If we have a user, return an appropriate message tailored to the user. Otherwise, return a message suitable for an anonymous caller.

So let’s do these things.

2.1: Get the access token from the request header

In flask, we just need to import the request object, grab the headers dictionary from it, and retrieve our header, accesstoken, from that:

from flask import render_template, request
import logging
...@app.route('/api/welcomemessage', methods=["GET"])
def welcomemessage():
accessToken = request.headers.get("accesstoken")
logging.debug("accessToken: %s" % accessToken)

2.2: Validate the token, get the user

Firebase calls this ID Token Verification, and you can read about it here:

This is a pretty simple thing to do, but first I need the Firebase Admin SDK set up. The Admin SDK lets me talk to Firebase from App Engine.

To do this, I add firebase-admin to my requirements.txt, and update my libraries using scripts/setup.sh.

Also, the Python Standard Environment on App Engine needs some crazy extra setup. Here’s the link:

It’s a bit odd because it’s not targeted at Firebase, but at something called Google Auth, which surely means there’s some shared something back there. But in any case, these are the steps I’ve done:

  • Vendored requests-toolbelt, ie: added it to requirements.txt, which now looks like this:
flask
firebase-admin
requests-toolbelt
  • Enabled ssl and sockets in app.yaml, which now looks like this:
runtime: python27
api_version: 1
threadsafe: true
automatic_scaling:
max_idle_instances: 2

libraries:
- name: ssl
version: latest

env_variables:
GAE_USE_SOCKETS_HTTPLIB : 'true'


handlers:
- url: /static
static_dir: static
- url: /.*
script: main.app

That’s all just witchcraft, as far as I’m concerned. Also, the sockets thing seems to stop your app from working in the local development environment; I’m not concerned with that because it’s already basically impossible to run it locally because of the dependencies on external services, so hey.

So, given all this, we can now add some utility code to get the firebase app. I’ve added the file src/firebaseutils.py:

import firebase_admin
from firebase_admin import credentials, auth
def initFirebaseApp():
retval = None
try:
# just see if the app's already initialised
retval = firebase_admin.get_app()
except:
# nope. Let's initialise it
cred = credentials.Certificate('config/firebasekey.json')
retval = firebase_admin.initialize_app(cred)
return retval

def decodeFirebaseToken(accesstoken):
initFirebaseApp()
return auth.verify_id_token(accesstoken)

The Firebase admin sdk manages a singleton default app object for you, once it’s constructed. Most commands don’t require explicit passing of this app object, they’ll use the default.

The method I’ll want to call is decodeFirebaseToken. This uses the auth service to verify the access token and return a dictionary of info about the user (which isn’t all in the token, it also goes and talks to firebase). But, you’ll also see it calls initFirebaseApp() to initialise the connection to firebase.

You’ll see initFirebaseApp() performs initialisation only if necessary. It returns the app just in case, but I wont actually use the result anywhere.

You’ll also notice I’m loading a firebase private key from a file src/config/firebasekey.json in that method, which the admin sdk will use to talk to firebase.

Where do we find this key? In the firebase console:

If you click that and generate a key, you’ll notice it’s a whole json dict full of interesting stuff about your project, some of which should be kept secret. Pro tip: pop this file in your .gitignore!

How deep does it go?

Ok, that was quite the rabbit hole. What was I trying to do again?

Oh yes, I want to validate the token, and get the user. Here’s the implementation detail in src/handlers/trulyauthul.py

from flask import render_template, request#, redirect
from firebaseutils import decodeFirebaseToken
import json
import logging

def get_trulyauthul(app):
@app.route('/', methods=["GET"])
def trulyauthul():
return render_template("trulyauthul.html")

@app.route('/api/welcomemessage', methods=["GET"])
def welcomemessage():
# get the access token if there is one
accessToken = request.headers.get("accesstoken")
logging.debug("accessToken: %s" % accessToken)

# now decode the access token if possible
decodedToken = None
if accessToken:
decodedToken = decodeFirebaseToken(accessToken)
logging.debug(
"decodedToken: %s" % json.dumps(decodedToken, indent=2)
)

That’s pretty simple.

When I run this, log in, then look in the logs at the decoded token, I get this:

{
"picture": "https://scontent.xx.fbcdn.net/v/t1.0-1/c0.17.100.100/p100x100/14484686_10153934347333806_1115450374071104722_n.jpg?oh=c9e03683d235ff84a52e9964134da117&oe=5AE5E753",
"sub": "4iWEHhlWnPTGbZsceFbpdxDy0z82",
"user_id": "4iWEHhlWnPTGbZsceFbpdxDy0z82",
"name": "Emlyn O'Regan",
"iss": "https://securetoken.google.com/trulyauthul",
"email_verified": false,
"firebase": {
"sign_in_provider": "facebook.com",
"identities": {
"facebook.com": [
"10155326924778806"
],
"email": [
"emlynoregan@gmail.com"
]
}
},
"exp": 1517298726,
"auth_time": 1517295126,
"uid": "4iWEHhlWnPTGbZsceFbpdxDy0z82",
"iat": 1517295126,
"email": "emlynoregan@gmail.com",
"aud": "trulyauthul"
}

That’s a lot of cool information!

2.3 — Construct and return the message

I’m on the home straight. Let’s just use the name from the decoded token. Here’s the final code, in src/handlers/trulyauthul.py :

from flask import render_template, request#, redirect
from firebaseutils import decodeFirebaseToken
import json
import logging

def get_trulyauthul(app):
@app.route('/', methods=["GET"])
def trulyauthul():
return render_template("trulyauthul.html")

@app.route('/api/welcomemessage', methods=["GET"])
def welcomemessage():
# get the access token if there is one
accessToken = request.headers.get("accesstoken")
logging.debug("accessToken: %s" % accessToken)

# now decode the access token if possible
decodedToken = None
if accessToken:
decodedToken = decodeFirebaseToken(accessToken)
logging.debug(
"decodedToken: %s" % json.dumps(decodedToken, indent=2)
)

# lastly, construct and return the welcome message
if decodedToken:
name = decodedToken.get("name", "<can't get name>")
lresponse = "Hi %s, welcome to Truly Authul" % name
else:
lresponse = "Hello there! Please sign in to access Truly Authul"

return json.dumps(
{
"message": lresponse
},
indent=2
)

That’s pretty straightforward code to construct the response and return it.

So can we run it?

Sure we can. Here goes:

That’s ugly, but it works, and this time it’s calling the back end and validating the caller.

Note of course that the back end could just reject any callers without a valid token.

In the next article, I’m going to look at making this app look a bit better. We’ll start with a javascript framework, and probably a material design component library. I want to get to a point where I’ve got a skeleton app with Firebase Authentication and a decent front end library for knocking up serviceable UIs without too much thought.

The Infinite Machine

Idiosyncratic Incantations in Python for Google App Engine

Emlyn O'Regan

Written by

I make things out of bits. Great and terrible things, tiny bits.

The Infinite Machine

Idiosyncratic Incantations in Python for Google App Engine