Authenticate Firestore with Auth0

Reddit
Linkedin

Protecting your data using auth0 and AWS Lambda.

Serveless technologies are no the future, they are the present, period. Whatever as Service is also a very important part of creating a tech business these days and when you realize it your business is built on top a beautiful spaghetti of services, these bring some challenges.

I’m my case, I built Entr on top of several 3rd parties solutions where it’s worth to mention AWS Lambda, Firebase, and Auth0. This means that I decided not to use Firebase built-in auth so how the hell do I protect my collections? 🤷🏻‍♂️

Firebase collections are protected by Securities Rules which is a topic of it owns (I will talk about it on a future post) but (sometimes) we a find a way to identify that someone is authorized to read or write a collection and keep doing whatever roles segregation we need. This post is really about the implementation so I won’t get into the details of how security works but you can find more about it [here](https://auth0.com/blog/what-is-and-how-does-single-sign-on-work/).

Show me the Custom Tokens

Long story made short, we need to rely on [Custom Tokens](https://firebase.google.com/docs/auth/admin/create-custom-tokens) and a piece of Backend, in this case, we are going to assume that you are using AWS Lambda.

Let’s create a small function that provides the Custom Token using the Firebase SDK. You will need:

  • The project Firebase Key
  • An Auth0 account
  • The Firebase SDK
  • AWS Lambda or any Backend process
  • A front end app

##Firebase Key

You will need to generate a Firebase Key, this is a JSON file which provides access to your firebase project, anyone with this file will have full access to your project, make sure you protect it very well.

In order to generate this, you have to go to your project settings and download it as shown in the picture.

sdk firebase 1

AWS Lambda

Our Lambda function should look like this:

'use strict'
const admin = require('firebase-admin')
const jwtDecode = require('jwt-decode')
const serviceAccount = require('path-to-your-firebase-key')

const getCustomToken = async sub => {
  admin.initializeApp({
    credential: admin.credential.cert(serviceAccount)
  })
  const firebaseToken = await admin.auth().createCustomToken(sub)
  return firebaseToken
}

const setCustomTokenFirebase = async (event) => {
  const { headers } = event
  const { Authorization } = headers
  const jtwToken = Authorization.split('Bearer')[1]
  const jwtDecoded = jwtDecode(jtwToken)
  const token = await getCustomToken(jwtDecoded['sub'])
  return { token }
}

module.exports.handler = async (event) => {
  const { token } = await setCustomTokenFirebase(event)
  return { token } 
})

The Serverless Framework

Now we need some API endpoint to reach our function, for the sake of simplicity, let’s use the super cool serverless framework. Our serverless.yml should look like this:

service: firebase-custom-token
plugins: 
  - serverless-pseudo-parameters

provider:
  name: aws
  runtime: nodejs8.10

functions:
  auth:
    handler: firebaseCustomTokenLambda.handler
    events:
      - http:
           path: /auth
           method: post

Disclaimer: For the sake of the post, I’m not suggesting any secure way to protect your keys, you always should really on solid secret management solutions, in case of AWS, I normally use SSM but that implementation is out of the scope of this post.

Front-end

I won’t focus on the Auth0 or Firebase installation/implementation on the client side since depends on the framework you are using, I will provide you a generic solution without getting into an implementation detail.

import firebase from 'firebase/app'
import axios from 'axios'
require('firebase/auth')
const YOUR_BACKEND_PATH = 'https://mylambdaapigatewayendpoint.com/auth'

const config = {
  apiKey: process.env['FIREBASE_API_KEY'],
  authDomain: process.env['FIREBASE_AUTH_DOMAIN'],
  databaseURL: process.env['FIREBASE_DATABASE_URL'],
  projectId: process.env['FIREBASE_PROJECT'],
  storageBucket: process.env['FIREBASE_STORAGE'],
  messagingSenderId: process.env['FIREBASE_MESSAGE_ID'],
}

firebase.initializeApp(config)

const withAuthHeader = () => {
  const token = localStorage.getItem('id_token') // Whereever you are storaging your auth0 token
  const instance = axios.create({
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
  })
  return instance
}

const setCustomToken = () => {
  const axios = withAuthHeader()
  const { data } = await axios.post(YOUR_BACKEND_PATH)
  const firebaseToken = await firebase.auth().signInWithCustomToken(data.token)
  return firebaseToken
}

Deploy your project and you are done! Directly to the point! After calling the setCustomToken function on your client you will be able to query your collections and use a firebase rule like the following:

firebase rules

This means, all your users will need to be authenticated with Auth0 (or your auth solution of choice) in order to read or write doc on the myCollection collection. Firebase rules are a whole topic of it owns, if you want to know more about it, let me know!

Enjoyed this post? Receive the next one in your inbox!

I hand pick all the best resources about Firebase and GCP around the web.


Not bullshit, not spam, just good content, promised 😘.


Reddit
Linkedin