NestJS Supabase Auth Passport Strategy

1.0.9 · active · verified Wed Apr 22

nestjs-supabase-auth is a NestJS Passport strategy designed to integrate Supabase authentication into NestJS applications. It leverages `passport-jwt` to validate JWTs issued by Supabase, allowing developers to secure their API routes and GraphQL resolvers. The package is currently at version 1.0.9 and generally maintains a stable release cadence, with updates primarily focused on bug fixes or adapting to changes in Supabase Auth, rather than frequent major breaking changes. Its key differentiator is providing a pre-built, opinionated integration for Supabase's JWT-based authentication within the established NestJS Passport ecosystem, simplifying the process of securing backends compared to implementing a generic JWT strategy and handling Supabase-specific claims manually. Users must extend the provided `SupabaseAuthStrategy` to configure their specific Supabase instance details and JWT secret, enabling flexible environment variable integration and custom user payload validation.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart demonstrates how to define and register a custom Supabase Passport strategy, apply it to a NestJS route using a guard, and access the validated user payload from the request. It includes environment variable placeholders for setup.

import { Injectable, Module } from '@nestjs/common';
import { PassportStrategy, AuthGuard } from '@nestjs/passport';
import { ExtractJwt } from 'passport-jwt';
import { SupabaseAuthStrategy } from 'nestjs-supabase-auth';
import { PassportModule } from '@nestjs/passport';
import { Controller, Get, UseGuards, Request } from '@nestjs/common';

// --- Strategy Definition (supabase.strategy.ts) ---
@Injectable()
export class SupabaseJwtStrategy extends PassportStrategy(
  SupabaseAuthStrategy,
  'supabase',
) {
  public constructor() {
    super({
      supabaseUrl: process.env.SUPABASE_URL ?? 'https://your-project-ref.supabase.co',
      supabaseKey: process.env.SUPABASE_KEY ?? 'YOUR_SUPABASE_ANON_KEY',
      supabaseOptions: {},
      supabaseJwtSecret: process.env.SUPABASE_JWT_SECRET ?? 'YOUR_SUPABASE_JWT_SECRET',
      extractor: ExtractJwt.fromAuthHeaderAsBearerToken(),
    });
  }

  async validate(payload: any): Promise<any> {
    // This method is called after JWT verification. 'payload' contains the decoded JWT.
    // You can perform additional user validation or data fetching here.
    // Ensure the `sub` claim (user ID) is present.
    if (!payload || !payload.sub) {
      throw new Error('Invalid JWT payload: Missing user ID.');
    }
    // IMPORTANT: Call super.validate(payload) if you need the base strategy's validation logic
    // or omit it if you fully override the validation.
    // super.validate(payload); // Base validation might be empty or specific to the original strategy.

    // Return the validated user payload. NestJS will attach this to req.user.
    return { userId: payload.sub, email: payload.email, ...payload };
  }

  authenticate(req: Request) {
    // This method can be overridden for custom authentication logic before validation.
    // In most cases, the default Passport.js flow is sufficient.
    super.authenticate(req);
  }
}

// --- Auth Module (auth.module.ts) ---
@Module({
  imports: [PassportModule],
  providers: [SupabaseJwtStrategy],
  exports: [SupabaseJwtStrategy, PassportModule], // Export PassportModule if other modules need it
})
export class AuthModule {}

// --- Protected Controller (user.controller.ts) ---
const SUPABASE_AUTH_GUARD = 'supabase'; // Define the guard name consistently

@Controller('user')
export class UserController {
  @UseGuards(AuthGuard(SUPABASE_AUTH_GUARD))
  @Get('profile')
  getProfile(@Request() req) {
    // req.user will contain the object returned by the validate method
    return req.user;
  }
}

// --- Main Application (main.ts or app.module.ts, simplified for quickstart) ---
// This setup assumes AuthModule is imported into AppModule.
// You would also need to configure your NestJS application to load environment variables.
// Example App Module might look like:
// @Module({
//   imports: [AuthModule],
//   controllers: [UserController],
// })
// export class AppModule {}

// To run this, you'd typically have a NestJS app initialized with `nest new`,
// then add these files and configure environment variables:
// SUPABASE_URL=https://<your-project-ref>.supabase.co
// SUPABASE_KEY=<your-anon-public-key>
// SUPABASE_JWT_SECRET=<your-jwt-secret-from-supabase-settings>

view raw JSON →