{"id":16655,"library":"nestjs-supabase-auth","title":"NestJS Supabase Auth Passport Strategy","description":"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.","status":"active","version":"1.0.9","language":"javascript","source_language":"en","source_url":"https://github.com/hiro1107/nestjs-supabase-auth","tags":["javascript","nestjs","supabase","supabase-auth","passport","passport-jwt","auth","authentication"],"install":[{"cmd":"npm install nestjs-supabase-auth","lang":"bash","label":"npm"},{"cmd":"yarn add nestjs-supabase-auth","lang":"bash","label":"yarn"},{"cmd":"pnpm add nestjs-supabase-auth","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Core Passport.js library, required for authentication middleware.","package":"passport","optional":false},{"reason":"Passport strategy for authenticating with a JSON Web Token, which Supabase uses.","package":"passport-jwt","optional":false},{"reason":"NestJS integration for Passport.js, providing utilities like `PassportStrategy`.","package":"@nestjs/passport","optional":false},{"reason":"TypeScript type definitions for `passport-jwt`.","package":"@types/passport-jwt","optional":true}],"imports":[{"note":"This is the primary class to extend for your custom Supabase Passport strategy. NestJS applications are predominantly ESM/TypeScript-first.","wrong":"const SupabaseAuthStrategy = require('nestjs-supabase-auth');","symbol":"SupabaseAuthStrategy","correct":"import { SupabaseAuthStrategy } from 'nestjs-supabase-auth';"},{"note":"`PassportStrategy` is a factory function provided by `@nestjs/passport` used to create a NestJS-compatible Passport strategy from a base strategy (like `SupabaseAuthStrategy`).","wrong":"import { Strategy } from '@nestjs/passport';","symbol":"PassportStrategy","correct":"import { PassportStrategy } from '@nestjs/passport';"},{"note":"`ExtractJwt` provides helper methods to extract the JWT from the request, commonly `fromAuthHeaderAsBearerToken()`.","wrong":"import * as ExtractJwt from 'passport-jwt';","symbol":"ExtractJwt","correct":"import { ExtractJwt } from 'passport-jwt';"},{"note":"`AuthGuard` is imported from `@nestjs/passport` to create route-level guards that apply the defined Passport strategy.","wrong":"import { AuthGuard } from '@nestjs/common';","symbol":"AuthGuard","correct":"import { AuthGuard } from '@nestjs/passport';"}],"quickstart":{"code":"import { Injectable, Module } from '@nestjs/common';\nimport { PassportStrategy, AuthGuard } from '@nestjs/passport';\nimport { ExtractJwt } from 'passport-jwt';\nimport { SupabaseAuthStrategy } from 'nestjs-supabase-auth';\nimport { PassportModule } from '@nestjs/passport';\nimport { Controller, Get, UseGuards, Request } from '@nestjs/common';\n\n// --- Strategy Definition (supabase.strategy.ts) ---\n@Injectable()\nexport class SupabaseJwtStrategy extends PassportStrategy(\n  SupabaseAuthStrategy,\n  'supabase',\n) {\n  public constructor() {\n    super({\n      supabaseUrl: process.env.SUPABASE_URL ?? 'https://your-project-ref.supabase.co',\n      supabaseKey: process.env.SUPABASE_KEY ?? 'YOUR_SUPABASE_ANON_KEY',\n      supabaseOptions: {},\n      supabaseJwtSecret: process.env.SUPABASE_JWT_SECRET ?? 'YOUR_SUPABASE_JWT_SECRET',\n      extractor: ExtractJwt.fromAuthHeaderAsBearerToken(),\n    });\n  }\n\n  async validate(payload: any): Promise<any> {\n    // This method is called after JWT verification. 'payload' contains the decoded JWT.\n    // You can perform additional user validation or data fetching here.\n    // Ensure the `sub` claim (user ID) is present.\n    if (!payload || !payload.sub) {\n      throw new Error('Invalid JWT payload: Missing user ID.');\n    }\n    // IMPORTANT: Call super.validate(payload) if you need the base strategy's validation logic\n    // or omit it if you fully override the validation.\n    // super.validate(payload); // Base validation might be empty or specific to the original strategy.\n\n    // Return the validated user payload. NestJS will attach this to req.user.\n    return { userId: payload.sub, email: payload.email, ...payload };\n  }\n\n  authenticate(req: Request) {\n    // This method can be overridden for custom authentication logic before validation.\n    // In most cases, the default Passport.js flow is sufficient.\n    super.authenticate(req);\n  }\n}\n\n// --- Auth Module (auth.module.ts) ---\n@Module({\n  imports: [PassportModule],\n  providers: [SupabaseJwtStrategy],\n  exports: [SupabaseJwtStrategy, PassportModule], // Export PassportModule if other modules need it\n})\nexport class AuthModule {}\n\n// --- Protected Controller (user.controller.ts) ---\nconst SUPABASE_AUTH_GUARD = 'supabase'; // Define the guard name consistently\n\n@Controller('user')\nexport class UserController {\n  @UseGuards(AuthGuard(SUPABASE_AUTH_GUARD))\n  @Get('profile')\n  getProfile(@Request() req) {\n    // req.user will contain the object returned by the validate method\n    return req.user;\n  }\n}\n\n// --- Main Application (main.ts or app.module.ts, simplified for quickstart) ---\n// This setup assumes AuthModule is imported into AppModule.\n// You would also need to configure your NestJS application to load environment variables.\n// Example App Module might look like:\n// @Module({\n//   imports: [AuthModule],\n//   controllers: [UserController],\n// })\n// export class AppModule {}\n\n// To run this, you'd typically have a NestJS app initialized with `nest new`,\n// then add these files and configure environment variables:\n// SUPABASE_URL=https://<your-project-ref>.supabase.co\n// SUPABASE_KEY=<your-anon-public-key>\n// SUPABASE_JWT_SECRET=<your-jwt-secret-from-supabase-settings>\n","lang":"typescript","description":"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."},"warnings":[{"fix":"Ensure all peer dependencies are explicitly installed using `npm install passport passport-jwt @nestjs/passport` and `npm install --save-dev @types/passport-jwt`.","message":"This package requires several peer dependencies (`passport`, `passport-jwt`, `@nestjs/passport`) to be installed manually alongside it. Failure to install these will result in runtime errors related to missing modules or `npm install` warnings.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Verify that `process.env.SUPABASE_JWT_SECRET` (or its equivalent) precisely matches the `JWT Secret` found in your Supabase Project Settings under 'API Settings'.","message":"The `supabaseJwtSecret` option is critical for JWT verification. It must exactly match the JWT secret configured in your Supabase project settings. Mismatches will cause all JWTs to be rejected as invalid, leading to 401 Unauthorized errors.","severity":"breaking","affected_versions":">=1.0.0"},{"fix":"Always return the desired user object from the `async validate(payload: any): Promise<any>` method. This object will be available as `req.user`.","message":"When extending `SupabaseAuthStrategy`, the `validate` method is where you determine what user data is attached to `req.user`. If you override `validate` without returning a meaningful user object, `req.user` will be `undefined` in your controllers/resolvers.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"For the `validate` method, focus on implementing your specific user logic (e.g., fetching user details from a database using `payload.sub`) and returning the final user object. Explicitly calling `super.validate()` might be redundant unless a future version adds more base validation. For `authenticate`, typically `super.authenticate(req)` is fine unless advanced customization of the token extraction or error handling is needed before validation.","message":"The `validate` and `authenticate` methods shown in the example README sometimes include `super.validate(payload);` and `super.authenticate(req);`. While technically allowed, the base `SupabaseAuthStrategy`'s `validate` method currently only logs the payload and doesn't perform additional checks beyond what `passport-jwt` already does. `super.authenticate` generally performs the core Passport.js flow.","severity":"deprecated","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Run `npm install passport passport-jwt @nestjs/passport @types/passport-jwt --save-dev` to install all necessary peer dependencies.","cause":"Required peer dependencies for `nestjs-supabase-auth` are not installed.","error":"Error: Cannot find module 'passport-jwt' or '@nestjs/passport'"},{"fix":"Ensure the client is sending a valid, unexpired Supabase access token. Double-check that `supabaseJwtSecret` in your `SupabaseJwtStrategy` exactly matches the 'JWT Secret' found in your Supabase project's API Settings.","cause":"The JWT provided in the 'Authorization: Bearer <token>' header is either missing, malformed, expired, or signed with a different secret than `supabaseJwtSecret` configured in the strategy.","error":"401 Unauthorized: Invalid Token or jwt malformed"},{"fix":"Verify that your `SupabaseJwtStrategy`'s `validate` method explicitly returns an object (e.g., `{ userId: payload.sub, ... }`). Also, ensure `@UseGuards(AuthGuard('supabase'))` is correctly applied to your controller methods or resolvers.","cause":"The `req.user` object is undefined, typically because the Passport strategy's `validate` method did not return a user object or the authentication guard was not correctly applied.","error":"TypeError: Cannot read properties of undefined (reading 'user')"},{"fix":"Ensure `PassportModule` is imported into your `AuthModule`'s `imports` array (`imports: [PassportModule]`). Also, confirm `SupabaseJwtStrategy` has the `@Injectable()` decorator.","cause":"This usually indicates that `PassportModule` is not imported into your `AuthModule`, or `@Injectable()` is missing on your strategy.","error":"Nest can't resolve dependencies of the SupabaseJwtStrategy (?). Please make sure that the argument at index [0] is available in the AuthModule context."}],"ecosystem":"npm"}