Secure a typescript-rest API with Firebase JWTs

Update: I have created a npm package that offers this functionality. Check it out here, or to install it directly:

npm install typescript-rest-fireauth

Original post:

I’ve been using the typescript-rest library recently to build backend APIs for few different projects. It’s quick to setup and uses annotations (decorators), which help to keep code clean.

I needed to secure one of the APIs, only allowing users who have authenticated with Firebase on the client side to make certain API requests. Typescript-rest has a built-in @Security decorator, but I struggled to understand how to use this passport-based decorator in conjunction with Firebase.

I decided to write my own decorator that would intercept requests, inspect headers for a Firebase JWT, and validate it using the Firebase Admin sdk. It utilizes typescript-rest’s ServiceContext, which contains a reference to the request (express.Request) object, and thus the headers. Here’s the decorator:

import { ServiceContext, Errors } from 'typescript-rest';
import * as admin from 'firebase-admin';

/**
 * Add authorization with Firebase ID token to a route
 *
 * @param target The prototype of the class
 * @param propertyKey The name of the method
 * @param descriptor The descriptor
 */
export function Auth () {
    return function (target: any, propertyKey: string, descriptor: TypedPropertyDescriptor) {
        const originalMethod = descriptor.value;
        let serviceContext = null;
        descriptor.value = function () {
            return new Promise((resolve, reject)=>{
                //locate the ServiceContext parameter
                for (var i=0;i {
                                var result = originalMethod.apply(this, arguments);
                                resolve(result);
                            })
                            .catch((err) => {
                                reject(new Errors.UnauthorizedError('Invalid Firebase token.'));
                            })
                    } else {
                        reject(new Errors.UnauthorizedError('Authorization required for this endpoint.'));
                    }
                } else {
                    console.log('Error: Authenticated decorator requires an instance of ServiceContext passed into the decorated method.');
                    reject(new Errors.InternalServerError());
                }
            });
        };
        return descriptor;
    }
}

You can then use the Auth decorator on any controller method/route, passing in the ServiceContext as a param, like this:

/**
 * Retrieve a User.
 * @param userId: The UID (Firebase) of the User
 */
@Auth()
@Path(':uid')
@GET
get(@PathParam('uid') uid: string, @Context context: ServiceContext): Promise {
    return new Promise((resolve, reject)=>{
        this.myService.getUser(uid)
            .then(function(user){
                resolve(user);
            })
            .catch(function(err){
                reject(err);
            });
    });
}

One thing I still need to do is to figure out how to send back a WWW-Authenticate header (per the HTTP Auth spec) when I reject with a 401. I tested and the Errors.UnauthorizedError does not do it automatically.

5 thoughts on “Secure a typescript-rest API with Firebase JWTs”

  1. I tried to use, but i got this error “TS1238: Unable to resolve signature of class decorator when called as an expression.”

    I’m using “es2015”, “es2017” libs

Leave a comment