DEV Community

Cover image for Easy Step by Step Loopback 4 JWT Authentication with Firebase
A Johan
A Johan

Posted on

Easy Step by Step Loopback 4 JWT Authentication with Firebase

The title says it all. The team decided to use Loopback 4 for the APIs as it is easy to get something working really quick. The challenge came when we wanted to integrate the authentication mechanism with our front end which was on VueJs. Firebase was our authentication server since we only needed Social logins and nothing more. Firebase takes a lot of the pain out of getting an web app with authentication up, well done Firebase team!

Back to the matter at hand. Loopback 4 documentation had sections on using JWT as well as custom authentication strategies. However, it was unclear and we were really stuck for many days on how to get it working. I would like to detail out the steps we took to get it working, much as a reference to my future self and hoping to help those in similar situations.

Let's scaffold a Loopback 4 application. I use Ubuntu in WSL 2 as my primary development environment. I also use yarn when the scaffold asks.

$ lb4 mysuperapp
Enter fullscreen mode Exit fullscreen mode

Answer the questions and wait for the scaffold to finish. You then need to add the firebase-admin, @loopback/authentication and @loopback/authentication-jwt package to your Loopback application.

$ cd mysuperapp
$ yarn add firebase-admin @loopback/authentication @loopback/authentication-jwt
Enter fullscreen mode Exit fullscreen mode

Follow the instructions at Add the Firebase Admin SDK to your server (google.com) to finish setting up the admin SDK. You will need to save the JSON file with your private key to your local machine and add it to the Loopback app. I usually save it into a folder under the app root called ./keys/ and I add this folder to my .gitignore file so as to avoid checking in the secret file.

Alt Text

The next step is IMPORTANT to ensure you get your Firebase SDK setup properly. You need to have an environment variable called GOOGLE_APPLICATION_CREDENTIALS defined. The value is the path to the JSON file you downloaded from Firebase earlier. You have to ensure this environment variable is present every time before you run you Loopback app. In Linux you would do (replace the path and file name based on the file you downloaded earlier):

// you can do it this way
$ export GOOGLE_APPLICATION_CREDENTIALS="./keys/my_secret_file.json"
$ yarn start
// or this way before you run the app
$ GOOGLE_APPLICATION_CREDENTIALS="./keys/my_secret_file.json" yarn start
Enter fullscreen mode Exit fullscreen mode

Next step is to initialize Firebase. Open application.ts, import the firebase-admin and loopback-authentication packages in the constructor . Next add the Firebase initialization steps. You will need your Firebase project Id and you can get that from the project settings in the Firebase console.

// application.ts
import * as firebase from "firebase-admin";
import { AuthenticationComponent } from '@loopback/authentication';
import { JWTAuthenticationComponent, TokenServiceBindings } from '@loopback/authentication-jwt';
export class MysuperappApplication extends BootMixin(
  ServiceMixin(RepositoryMixin(RestApplication)),
) {
  constructor(options: ApplicationConfig = {}) {
    super(options);
    // initialize firebase 
    firebase.initializeApp({
      credential: firebase.credential.applicationDefault(),
      projectId: 'my-firebase-project'
    })
Enter fullscreen mode Exit fullscreen mode

We then add the JWT component as shown in the Loopback documentation here How to secure your LoopBack 4 application with JWT authentication:

// application.ts - Add this at the bottom of the constructor
this.component(AuthenticationComponent);
this.component(JWTAuthenticationComponent);
Enter fullscreen mode Exit fullscreen mode

The code above will add the Authentication and JWT component to your Loopback application. That’s it. How cool is that! The last step before actually handling the Firebase code is to tell Loopback where to go for authentication. We do that by binding the TOKEN_SERVICE to our class that will handle decoding the Firebase token.

// application.ts - add this after adding the 2 lines above
this.bind(TokenServiceBindings.TOKEN_SERVICE).toClass(FirebaseTokenService);
Enter fullscreen mode Exit fullscreen mode

At this point you will get an error as we have not defined the class yet. Let’s do that next. Open the terminal in your application folder.

mysuperapp$ lb4 service
? Service type: Local service class bound to application context
? Service name: FirebaseToken
   create src/services/firebase-token.service.ts
   update src/services/index.ts
Service FirebaseToken was/were created in src/services
Enter fullscreen mode Exit fullscreen mode

Import this file in application.ts

import * as firebase from "firebase-admin";
import { FirebaseTokenService } from './services';
Enter fullscreen mode Exit fullscreen mode

Let’s setup the FirebaseTokenService. We have to implement the TokenService interface. Since we won’t be generating any tokens we throw an error when anyone tries to use that function

// firebase-token.service.ts
// Let's define an inline error class to that Loopback 
// can properly inform the user 
class FirebaseTokenError extends Error {
  statusCode: number
  constructor(message: string, statusCode = 403) {
    super(message)
    this.statusCode = statusCode;
  }
}
@injectable({scope: BindingScope.TRANSIENT})
export class FirebaseTokenService implements TokenService {
  constructor( ) { }
  async verifyToken (token: string): Promise<UserProfile> {
     // TODO implement the token decode and verify
  }
  async generateToken (userProfile: UserProfile): Promise<string> {
    throw new FirebaseTokenError("This service is not implemented");
  }
}
Enter fullscreen mode Exit fullscreen mode

The next few steps are straight forward and you can get the details by reading through the Firebase documentation. Let’s decode the token and return the UserProfile expected by Loopback. First add the firebase-admin library to your FirebaseTokenService.

// firebase-token.service.ts
import * as firebaseAdmin from "firebase-admin";
Enter fullscreen mode Exit fullscreen mode

Next implement the function to decode the token and return the UserProfile. Both of these functions should be defined in your FirebaseTokenService class.

// firebase-token.service.ts
async verifyToken (token: string): Promise<UserProfile> {
   // call the admin sdk to decode the token
    const decodedToken = await firebaseAdmin
       .auth()
       .verifyIdToken(token);
   // I cast to Record<string, any> here as I need to make 
   // some changes to the object
   let userProfile: Record<string, any> = decodedToken;
   // call function to return the UserProfile from 
   // decoded token
   return this.tokenToUserProfile(userProfile);
}
/**
 * Function to convert token to UserProfile
 */
tokenToUserProfile (token: Record<string, any>): UserProfile {
   return {
     [securityId]: token.user_id,
     email: token.email,
     name: token.name,
     picture: token.picture,
     uid: token.user_id,
  }
}
Enter fullscreen mode Exit fullscreen mode

With that, you now have a fully functioning integration between your Loopback application and Firebase authentication. You can view the full code at my GitHub (https://github.com/alfonsojohan/loopback4-firebase)

Top comments (0)