DEV Community

Cover image for Integrating Firebase Authentication into NestJS with nestjs-firebase-auth
Tomás Alegre Sepúlveda
Tomás Alegre Sepúlveda

Posted on

Integrating Firebase Authentication into NestJS with nestjs-firebase-auth

Introduction

Managing authentication in a modern web app can be challenging, especially when dealing with various identity providers and securing endpoints. Firebase Authentication simplifies this process by providing robust authentication out of the box, and NestJS offers a powerful framework for building scalable applications.

In this post, I'll introduce you to @alpha018/nestjs-firebase-auth, a library that makes integrating Firebase Authentication into NestJS simple and flexible. Whether you need basic authentication or role-based access control (RBAC) using Firebase custom claims, this library has you covered.

What Does nestjs-firebase-auth Do?

This library extends NestJS’s authentication capabilities using Firebase's Admin SDK and Passport strategy. It provides a variety of tools that make it easier to:

  • Authenticate users via Firebase tokens.
  • Validate user roles using Firebase custom claims.
  • Customize token extraction and handle revoked tokens.
  • Secure routes with minimal boilerplate code.

Let’s dive into how to set up the library and explore its key features.

Installation

To get started, install the package along with the Firebase Admin SDK:

npm i @alpha018/nestjs-firebase-auth firebase-admin
Enter fullscreen mode Exit fullscreen mode

You will also need to configure Firebase in your project by downloading the service account key from your Firebase Console.

Core Features and Components

1. Module Configuration

The FirebaseAdminModule allows you to configure Firebase for your NestJS app. You can pass a base64-encoded service account JSON string or use regular options to configure Firebase.

Here's an example of how to import and configure the module:

import { Module } from '@nestjs/common';
import { FirebaseAuthGuard, FirebaseAdminModule } from '@alpha018/nestjs-firebase-auth';
import { ConfigModule, ConfigService } from '@nestjs/config';

@Module({
  imports: [
    ConfigModule.forRoot(),
    FirebaseAdminModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        base64: configService.get('FIREBASE_SERVICE_ACCOUNT_BASE64'),
        options: {}, // Optionally, provide Firebase configuration here
        auth: {
          config: {
            extractor: ExtractJwt.fromAuthHeaderAsBearerToken(), // Extract JWT from the header
            checkRevoked: true, // Optionally check if the token is revoked
            validateRole: true, // Enable role validation if needed
          },
        },
      }),
      inject: [ConfigService],
    }),
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

2. Auth Guards and Role Validation

The FirebaseAuthGuard is at the heart of securing your endpoints. You can use it to protect routes and validate user tokens. If your app needs role-based access control, the library integrates Firebase custom claims to enforce RBAC seamlessly.

Protecting Routes Without Role Validation

If you only need basic authentication, the FirebaseAuthGuard will ensure that only authenticated users can access your routes.

import { Controller, Get, UseGuards } from '@nestjs/common';
import { FirebaseAuthGuard } from '@alpha018/nestjs-firebase-auth';

@Controller()
export class AppController {
  @UseGuards(FirebaseAuthGuard)
  @Get()
  getProtectedData(): string {
    return 'This route is protected by Firebase Auth!';
  }
}
Enter fullscreen mode Exit fullscreen mode

Role-Based Access Control (RBAC)

For more advanced use cases, you can use Firebase custom claims to implement role-based access. The library supports setting and validating these roles with ease.

Here’s how you can set a user’s role:

import { Controller, Get } from '@nestjs/common';
import { FirebaseProvider } from '@alpha018/nestjs-firebase-auth';

enum Roles {
  ADMIN = 'ADMIN',
  USER = 'USER',
}

@Controller()
export class AppController {
  constructor(private readonly firebaseProvider: FirebaseProvider) {}

  @Get('/set-role')
  async setAdminRole() {
    await this.firebaseProvider.setClaimsRoleBase<Roles>('FirebaseUID', [Roles.ADMIN]);
    return { status: 'Admin role assigned!' };
  }
}
Enter fullscreen mode Exit fullscreen mode

Then, use the RolesGuard to enforce role validation for specific endpoints:

import { Controller, Get, UseGuards } from '@nestjs/common';
import { FirebaseAuthGuard, RolesGuard } from '@alpha018/nestjs-firebase-auth';

enum Roles {
  ADMIN = 'ADMIN',
  USER = 'USER',
}

@Controller()
export class AppController {
  @RolesGuard(Roles.ADMIN, Roles.USER) // This line checks the custom claims of the Firebase user to protect the endpoint
  @UseGuards(FirebaseGuard)
  @Get('/admin')
  getAdminData(): string {
    return 'Admin-only data!';
  }
}
Enter fullscreen mode Exit fullscreen mode

3. User from token and Claims Extraction

The library makes it easy to retrieve user details and claims from Firebase tokens. Using the @FirebaseUser() and @FirebaseUserClaims() decorators, you can extract important information from the authenticated user.

For example:

import { Controller, Get } from '@nestjs/common';
import { FirebaseUser, FirebaseUserClaims } from '@alpha018/nestjs-firebase-auth';
import { auth } from 'firebase-admin';

enum Roles {
  ADMIN = 'ADMIN',
  USER = 'USER',
}

@Controller()
export class AppController {
  @Get('/user-info')
  getUserInfo(
    @FirebaseUser() user: auth.DecodedIdToken,
    @FirebaseUserClaims() claims: Roles[],
  ) {
    return { user, claims };
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Handling Token Revocation

Firebase supports token revocation, which allows you to invalidate tokens after they’ve been issued. This is a useful security feature in case a user is compromised. You can configure the guard to check for revoked tokens automatically by enabling checkRevoked.

@Module({
  imports: [
    ConfigModule.forRoot(),
    FirebaseAdminModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        base64: configService.get('FIREBASE_SERVICE_ACCOUNT_BASE64'),
        auth: {
          config: {
            checkRevoked: true, // Ensure revoked tokens are invalidated
          },
        },
      }),
      inject: [ConfigService],
    }),
  ],
})
Enter fullscreen mode Exit fullscreen mode

Additional Utilities

The library also provides utility methods for managing claims and handling role-based logic. With FirebaseProvider, you can set, get, and validate claims easily, allowing you to manage complex access control systems with minimal effort.

Why Use nestjs-firebase-auth?

The beauty of this library is its simplicity and flexibility. With a few configurations, you can have a robust authentication system ready to go. Whether you’re building a small project or scaling up with more complex security needs, nestjs-firebase-auth provides all the tools you need.

Key benefits include:

  • Seamless Firebase Integration: Leverage the full power of Firebase Auth within NestJS.
  • Role-Based Access Control: Secure your routes with custom claims and easily manage roles.
  • Token Revocation Support: Automatically handle revoked tokens to keep your app secure.
  • Custom Token Extractors: Flexibility in how you extract and validate tokens from requests.

Conclusion

If you’re looking to add Firebase Authentication to your NestJS project, nestjs-firebase-auth is the perfect library to get you started quickly and securely. It simplifies the process of adding authentication and role validation, making your API secure with minimal setup.

Feel free to check out the full source code and contribute to the project on GitHub. Your feedback and contributions are always welcome!

Happy coding!

Top comments (0)