{"_id":"@dotted-labs/nestjs-supabase-auth","_rev":"4-f3705c3ad2e47de9be5c0cbb675b4103","name":"@dotted-labs/nestjs-supabase-auth","dist-tags":{"latest":"0.0.3"},"versions":{"0.0.1":{"name":"@dotted-labs/nestjs-supabase-auth","version":"0.0.1","keywords":["nestjs","supabase","authentication","auth"],"author":{"name":"DottedLabs"},"license":"MIT","_id":"@dotted-labs/nestjs-supabase-auth@0.0.1","maintainers":[{"name":"luismdev","email":"lm.torresrivera@gmail.com"}],"homepage":"https://github.com/dotted-labs/nestjs-supabase-auth#readme","bugs":{"url":"https://github.com/dotted-labs/nestjs-supabase-authissues"},"dist":{"shasum":"b2a7935e1d4fc143f27cbbf8ae9c707158f11b69","tarball":"https://registry.npmjs.org/@dotted-labs/nestjs-supabase-auth/-/nestjs-supabase-auth-0.0.1.tgz","fileCount":52,"integrity":"sha512-x1CcejEk+JdMg6evTWMxhQa1f7HvxI0rPCk2miAwc/qhWuhWcvfp7BSvXH2ss74I0gMwGtWs6oipfTLstwiUeQ==","signatures":[{"sig":"MEUCIAS/a2yyzmhQjTCso+dT/p0lXRf9Cm3BxT43tkI0jYllAiEA/ZKqXphqeyahvo6xF++OWUsg1KuLirCOq2SOLaLC8gs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":202305},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"796d28a3495b6f0c17da2dfaaf89206a6535184b","private":false,"scripts":{"lint":"eslint \"src/**/*.ts\" --fix","test":"jest","build":"tsc -p tsconfig.json","format":"prettier --write \"src/**/*.ts\"","test:cov":"jest --coverage","prepublish":"npm run build","test:debug":"node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand","test:watch":"jest --watch"},"_npmUser":{"name":"luismdev","email":"lm.torresrivera@gmail.com"},"repository":{"url":"git+https://github.com/dotted-labs/nestjs-supabase-auth.git","type":"git"},"_npmVersion":"10.8.2","description":"NestJS library for Supabase authentication","directories":{},"_nodeVersion":"20.18.3","dependencies":{"rxjs":"7.8.2","express":"5.1.0","passport":"^0.7.0","@nestjs/core":"11.0.14","passport-jwt":"^4.0.1","cookie-parser":"1.4.7","@nestjs/common":"11.0.14","@nestjs/passport":"11.0.5","reflect-metadata":"0.2.2","@types/passport-jwt":"^4.0.1","@supabase/supabase-js":"2.49.4"},"_hasShrinkwrap":false,"devDependencies":{"jest":"29.7.0","eslint":"9.24.0","ts-jest":"29.3.1","typescript":"5.8.3","@types/jest":"29.5.14","@types/node":"22.14.0","@types/express":"^5.0.1","@nestjs/testing":"11.0.14","@types/cookie-parser":"^1.4.8","@typescript-eslint/parser":"8.29.1","@typescript-eslint/eslint-plugin":"8.29.1"},"peerDependencies":{"rxjs":"7.8.2","@nestjs/core":"11.0.14","@nestjs/common":"11.0.14","reflect-metadata":"0.2.2"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-supabase-auth_0.0.1_1746627072586_0.13766037199263326","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@dotted-labs/nestjs-supabase-auth","version":"0.0.2","keywords":["nestjs","supabase","authentication","auth"],"author":{"name":"DottedLabs"},"license":"MIT","_id":"@dotted-labs/nestjs-supabase-auth@0.0.2","maintainers":[{"name":"luismdev","email":"lm.torresrivera@gmail.com"}],"homepage":"https://github.com/dotted-labs/nestjs-supabase-auth#readme","bugs":{"url":"https://github.com/dotted-labs/nestjs-supabase-authissues"},"dist":{"shasum":"ec460cf9a5a34ebf0572321df6b2c5e452e4231b","tarball":"https://registry.npmjs.org/@dotted-labs/nestjs-supabase-auth/-/nestjs-supabase-auth-0.0.2.tgz","fileCount":52,"integrity":"sha512-K00UVl1nWCtkRK86bRI2CZm3+YNMc+eUjA/XgP7RIMESEmfg1gQGbZI6gE5TBtIJKwG+HKiVih4BOQJ+B+YNaA==","signatures":[{"sig":"MEUCIFzboprW1mTqzRV2E1LGcs8ViyS0tK6ZRuC6UGTzUykeAiEAqqNvDofZD8gL053NNhFyzoJfTwNCf8dDT5xl0+oMHQI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":201603},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"e63a1a6e2f0c90d1d2b44957bb5383982cc39518","private":false,"scripts":{"lint":"eslint \"src/**/*.ts\" --fix","test":"jest","build":"tsc -p tsconfig.json","format":"prettier --write \"src/**/*.ts\"","test:cov":"jest --coverage","prepublish":"npm run build","test:debug":"node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand","test:watch":"jest --watch"},"_npmUser":{"name":"luismdev","email":"lm.torresrivera@gmail.com"},"repository":{"url":"git+https://github.com/dotted-labs/nestjs-supabase-auth.git","type":"git"},"_npmVersion":"10.8.2","description":"NestJS library for Supabase authentication","directories":{},"_nodeVersion":"20.19.1","dependencies":{"rxjs":"7.8.2","express":"5.1.0","passport":"^0.7.0","@nestjs/core":"11.0.14","passport-jwt":"^4.0.1","cookie-parser":"1.4.7","@nestjs/common":"11.0.14","@nestjs/passport":"11.0.5","reflect-metadata":"0.2.2","@types/passport-jwt":"^4.0.1","@supabase/supabase-js":"2.49.4"},"_hasShrinkwrap":false,"devDependencies":{"jest":"29.7.0","eslint":"9.24.0","ts-jest":"29.3.1","typescript":"5.8.3","@types/jest":"29.5.14","@types/node":"22.14.0","@types/express":"^5.0.1","@nestjs/testing":"11.0.14","@types/cookie-parser":"^1.4.8","@typescript-eslint/parser":"8.29.1","@typescript-eslint/eslint-plugin":"8.29.1"},"peerDependencies":{"rxjs":"7.8.2","@nestjs/core":"11.0.14","@nestjs/common":"11.0.14","reflect-metadata":"0.2.2"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-supabase-auth_0.0.2_1746627173162_0.9212954207958242","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@dotted-labs/nestjs-supabase-auth","version":"0.0.3","keywords":["nestjs","supabase","authentication","auth"],"author":{"name":"DottedLabs"},"license":"MIT","_id":"@dotted-labs/nestjs-supabase-auth@0.0.3","maintainers":[{"name":"luismdev","email":"lm.torresrivera@gmail.com"}],"homepage":"https://github.com/dotted-labs/nestjs-supabase-auth#readme","bugs":{"url":"https://github.com/dotted-labs/nestjs-supabase-authissues"},"dist":{"shasum":"cc978f82e631303b3d14923cf1d7e07dba8368ad","tarball":"https://registry.npmjs.org/@dotted-labs/nestjs-supabase-auth/-/nestjs-supabase-auth-0.0.3.tgz","fileCount":52,"integrity":"sha512-Pc+w/Pcizb1TqxktsWTX+QOTCdEAJAHHa8PV1Lhgj7XbziEbE067rdmSAqNrmB2J6nKNWFdwMxLohoJe0FoBZg==","signatures":[{"sig":"MEUCIQCGe3f2uPVYRBKE1vh2tEf/2fTq8CVKi5aXpzeqJ8K12gIgSAYleuObPK40PRiQ0zWB6lS6YVbyj6QPnaCxdybO9Ak=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":205097},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"c2633ef813895da0e71702805930155e81d5c35c","private":false,"scripts":{"lint":"eslint \"src/**/*.ts\" --fix","test":"jest","build":"tsc -p tsconfig.json","format":"prettier --write \"src/**/*.ts\"","test:cov":"jest --coverage","prepublish":"npm run build","test:debug":"node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand","test:watch":"jest --watch"},"_npmUser":{"name":"luismdev","email":"lm.torresrivera@gmail.com"},"repository":{"url":"git+https://github.com/dotted-labs/nestjs-supabase-auth.git","type":"git"},"_npmVersion":"10.8.2","description":"NestJS library for Supabase authentication","directories":{},"_nodeVersion":"20.19.1","dependencies":{"rxjs":"7.8.2","express":"^5.1.0","passport":"^0.7.0","@nestjs/core":"^11.0.14","passport-jwt":"^4.0.1","cookie-parser":"^1.4.7","@nestjs/common":"^11.0.14","@nestjs/passport":"11.0.5","reflect-metadata":"0.2.2","@types/passport-jwt":"^4.0.1","@supabase/supabase-js":"^2.49.4","@nestjs/platform-express":"^11.1.0"},"_hasShrinkwrap":false,"devDependencies":{"jest":"29.7.0","eslint":"9.24.0","ts-jest":"29.3.1","typescript":"5.8.3","@types/jest":"29.5.14","@types/node":"22.14.0","@types/express":"^5.0.1","@nestjs/testing":"11.0.14","@types/cookie-parser":"^1.4.8","@typescript-eslint/parser":"8.29.1","@typescript-eslint/eslint-plugin":"8.29.1"},"peerDependencies":{"rxjs":"7.8.2","reflect-metadata":"0.2.2"},"_npmOperationalInternal":{"tmp":"tmp/nestjs-supabase-auth_0.0.3_1746631698683_0.40452174062129087","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2025-05-07T14:11:12.478Z","modified":"2026-08-04T10:34:19.435Z","0.0.1":"2025-05-07T14:11:12.844Z","0.0.2":"2025-05-07T14:12:53.366Z","0.0.3":"2025-05-07T15:28:18.930Z"},"bugs":{"url":"https://github.com/dotted-labs/nestjs-supabase-authissues"},"author":{"name":"DottedLabs"},"license":"MIT","homepage":"https://github.com/dotted-labs/nestjs-supabase-auth#readme","keywords":["nestjs","supabase","authentication","auth"],"repository":{"url":"git+https://github.com/dotted-labs/nestjs-supabase-auth.git","type":"git"},"description":"NestJS library for Supabase authentication","maintainers":[{"email":"lm.torresrivera@gmail.com","name":"luismdev"},{"email":"sergio.gundin.rodriguez@gmail.com","name":"gunsr"}],"readme":"# NestJS Auth Supabase\n\n[![npm version](https://badge.fury.io/js/%40dotted-labs%nestjs-supabase-auth.svg)](https://badge.fury.io/js/%40dotted-labs%nestjs-supabase-auth)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\nA simplified NestJS library for authenticating with Supabase.\n\n## Installation\n\n```bash\nnpm install nestjs-supabase-auth\n```\n\n## Usage\n\n### Register the module\n\n```typescript\nimport { Module } from \"@nestjs/common\";\nimport { SupabaseAuthModule } from \"nestjs-supabase-auth\";\nimport { AppController } from \"./app.controller\";\nimport { AppService } from \"./app.service\";\n\n@Module({\n  imports: [\n    SupabaseAuthModule.forRoot({\n      supabaseUrl: \"YOUR_SUPABASE_URL\",\n      supabaseKey: \"YOUR_SUPABASE_ROLE_KEY\",\n    }),\n  ],\n  controllers: [AppController],\n  providers: [AppService],\n})\nexport class AppModule {}\n```\n\nOr async configuration:\n\n```typescript\nimport { Module } from \"@nestjs/common\";\nimport { ConfigModule, ConfigService } from \"@nestjs/config\";\nimport { SupabaseAuthModule } from \"nestjs-supabase-auth\";\nimport { AppController } from \"./app.controller\";\nimport { AppService } from \"./app.service\";\n\n@Module({\n  imports: [\n    ConfigModule.forRoot(),\n    SupabaseAuthModule.forRootAsync({\n      imports: [ConfigModule],\n      useFactory: (configService: ConfigService) => ({\n        supabaseUrl: configService.get(\"SUPABASE_URL\"),\n        supabaseKey: configService.get(\"SUPABASE_ROLE_KEY\"),\n      }),\n      inject: [ConfigService],\n    }),\n  ],\n  controllers: [AppController],\n  providers: [AppService],\n})\nexport class AppModule {}\n```\n\n### Use the service\n\n```typescript\nimport { Injectable } from \"@nestjs/common\";\nimport { SupabaseAuthService } from \"nestjs-supabase-auth\";\n\n@Injectable()\nexport class AuthService {\n  constructor(private readonly supabaseAuthService: SupabaseAuthService) {}\n\n  async signIn(email: string, password: string) {\n    return this.supabaseAuthService.signIn(email, password);\n  }\n\n  async getUser(token: string) {\n    return this.supabaseAuthService.getUser(token);\n  }\n\n  async refreshSession(refreshToken: string) {\n    return this.supabaseAuthService.refreshSession(refreshToken);\n  }\n\n  async signOut(token?: string) {\n    return this.supabaseAuthService.signOut(token);\n  }\n\n  async generateMagicLink(token: string) {\n    return this.supabaseAuthService.generateMagicLink(token);\n  }\n}\n```\n\n#### Example controller implementation\n\n```typescript\nimport { Controller, Post, Body, Get, Headers, Req, Res } from \"@nestjs/common\";\nimport { Request, Response } from \"express\";\nimport { AuthService } from \"./auth.service\";\nimport { Public } from \"nestjs-supabase-auth\";\n\n@Controller(\"auth\")\nexport class AuthController {\n  constructor(private readonly authService: AuthService) {}\n\n  @Public()\n  @Post(\"login\")\n  async login(@Body() credentials) {\n    return this.authService.signIn(credentials.email, credentials.password);\n  }\n\n  @Get(\"profile\")\n  async getProfile(@Headers(\"authorization\") authHeader: string) {\n    // Extract token from 'Bearer TOKEN'\n    const token = authHeader.split(\" \")[1];\n    return this.authService.getUser(token);\n  }\n\n  @Public()\n  @Post(\"refresh\")\n  async refresh(@Body(\"refresh_token\") refreshToken: string) {\n    return this.authService.refreshSession(refreshToken);\n  }\n\n  @Post(\"magic-link\")\n  async generateMagicLink(@Headers(\"authorization\") authHeader: string) {\n    const token = authHeader.split(\" \")[1];\n    return this.authService.generateMagicLink(token);\n  }\n\n  @Post(\"logout\")\n  async logout(@Req() req: Request, @Res({ passthrough: true }) res: Response) {\n    // For local sign out (just this session)\n    const token = req.headers.authorization?.split(\" \")[1];\n    await this.authService.signOut(token);\n\n    // Clear cookies if using cookie-based auth\n    res.clearCookie(\"access_token\");\n    res.clearCookie(\"refresh_token\");\n\n    return { success: true, message: \"Signed out successfully\" };\n  }\n\n  @Post(\"logout-all\")\n  async logoutAll(@Res({ passthrough: true }) res: Response) {\n    // Global sign out (all user's sessions)\n    await this.authService.signOut();\n\n    // Clear cookies if using cookie-based auth\n    res.clearCookie(\"access_token\");\n    res.clearCookie(\"refresh_token\");\n\n    return { success: true, message: \"Signed out from all sessions\" };\n  }\n}\n```\n\n### Protect routes with guard\n\n```typescript\nimport { Module } from \"@nestjs/common\";\nimport { APP_GUARD } from \"@nestjs/core\";\nimport { SupabaseStrategyAuthGuard } from \"nestjs-supabase-auth\";\n\n@Module({\n  providers: [\n    {\n      provide: APP_GUARD,\n      useClass: SupabaseStrategyAuthGuard,\n    },\n  ],\n})\nexport class AppModule {}\n```\n\n### Make routes public\n\n```typescript\nimport { Controller, Get } from \"@nestjs/common\";\nimport { Public } from \"nestjs-supabase-auth\";\n\n@Controller(\"auth\")\nexport class AuthController {\n  @Public()\n  @Get(\"public\")\n  public() {\n    return { message: \"This is a public route\" };\n  }\n\n  @Get(\"protected\")\n  protected() {\n    return { message: \"This is a protected route\" };\n  }\n}\n```\n\n### Get user in controllers\n\n```typescript\nimport { Controller, Get } from \"@nestjs/common\";\nimport { User } from \"nestjs-supabase-auth\";\nimport { SupabaseAuthUser } from \"nestjs-supabase-auth\";\n\n@Controller(\"user\")\nexport class UserController {\n  @Get(\"profile\")\n  getProfile(@User() user: SupabaseAuthUser) {\n    return { user };\n  }\n\n  @Get(\"email\")\n  getEmail(@User(\"email\") email: string) {\n    return { email };\n  }\n}\n```\n\n## Cookie Management and Token Handling\n\n### Usage with Cookies\n\nTo store the refresh token in cookies after login:\n\n```typescript\n@Post('login')\nasync login(@Body() credentials) {\n  // The interceptor will handle setting the cookie\n  return await this.authService.signIn(credentials.email, credentials.password);\n}\n```\n\n### Automatic Cookie Handling with Interceptor\n\nFor easier cookie handling, use the provided `TokenInfoInterceptor`:\n\n```typescript\nimport { Module } from \"@nestjs/common\";\nimport { APP_INTERCEPTOR } from \"@nestjs/core\";\nimport { TokenInfoInterceptor } from \"nestjs-supabase-auth\";\n\n@Module({\n  providers: [\n    {\n      provide: APP_INTERCEPTOR,\n      useClass: TokenInfoInterceptor,\n    },\n  ],\n})\nexport class AppModule {}\n```\n\nThis interceptor:\n\n- Automatically stores refresh tokens in HTTP-only cookies\n- Removes refresh tokens from response bodies for better security\n- Adds token expiration information to responses\n\nWith this interceptor, you can simplify your login controllers:\n\n```typescript\n@Post('login')\nasync login(@Body() credentials) {\n  // The interceptor will handle setting the cookie\n  return await this.authService.signIn(credentials.email, credentials.password);\n}\n```\n\n### Sign Out with Cookie Clearing\n\nTo properly sign out users when using cookies:\n\n```typescript\n@Post('logout')\nasync logout(@Res({ passthrough: true }) response: Response) {\n  await this.authService.signOut();\n\n  // Clear auth cookies\n  response.clearCookie('access_token');\n  response.clearCookie('refresh_token');\n\n  return { success: true };\n}\n```\n\n## Refresh Token Middleware\n\nThis module includes a middleware that automatically refreshes expired access tokens.\n\n### Setup\n\n1. First, install the required dependency:\n\n```bash\nnpm install cookie-parser\n```\n\n2. Configure cookie-parser in your main.ts file:\n\n```typescript\nimport { NestFactory } from \"@nestjs/core\";\nimport { AppModule } from \"./app.module\";\nimport * as cookieParser from \"cookie-parser\";\n\nasync function bootstrap() {\n  const app = await NestFactory.create(AppModule);\n  app.use(cookieParser()); // Add this line to enable cookie parsing\n  await app.listen(3000);\n}\nbootstrap();\n```\n\nThe middleware is automatically applied to all routes when you import the SupabaseAuthModule.\n\n### How It Works\n\n1. The middleware checks if the request includes an access token\n2. If the token is valid, the request proceeds normally\n3. If the token has expired but a refresh token is available (in cookies or x-refresh-token header):\n   - The middleware automatically refreshes the session\n   - Updates the request with the new access token\n   - Sets a new refresh token cookie\n   - Allows the request to proceed with the new valid token\n\n## API Reference\n\n### SupabaseAuthService\n\n- `signIn(email: string, password: string)`: Sign in with email and password\n- `getUser(token: string)`: Get the current user\n- `refreshSession(refreshToken: string)`: Refresh the session\n- `signOut(token?: string)`: Sign out the current user. If token is provided, only the current session is invalidated; otherwise, all user sessions are invalidated (global sign out)\n- `generateMagicLink(token: string)`: Generate a magic link for the authenticated user. Returns a hashed token that can be used for passwordless authentication.\n\n## Error Handling\n\nThis module includes error handling with custom exceptions:\n\n- `InvalidCredentialsException`: Thrown when login credentials are incorrect\n- `TokenExpiredException`: Thrown when a JWT token has expired\n- `InvalidTokenException`: Thrown when a token is invalid\n- `MissingTokenException`: Thrown when no token is provided\n- `RefreshTokenException`: Thrown when refresh token is invalid or expired\n\n### Using the Global Exception Filter\n\nFor a consistent error handling approach, you can use the included exception filter:\n\n```typescript\nimport { Module } from \"@nestjs/common\";\nimport { APP_FILTER } from \"@nestjs/core\";\nimport { AuthExceptionFilter } from \"nestjs-supabase-auth\";\n\n@Module({\n  providers: [\n    {\n      provide: APP_FILTER,\n      useClass: AuthExceptionFilter,\n    },\n  ],\n})\nexport class AppModule {}\n```\n\nThe filter formats error responses consistently:\n\n```json\n{\n  \"statusCode\": 401,\n  \"message\": \"Token has expired\",\n  \"error\": \"TokenExpiredException\",\n  \"timestamp\": \"2023-08-15T10:30:45.123Z\",\n  \"path\": \"/api/protected-resource\"\n}\n```\n\nFor token expiration errors, the response includes a `shouldRefresh: true` field that clients can use to trigger token refresh.\n\n### Handling Exceptions in Controllers\n\nYou can also catch these exceptions directly in your controllers:\n\n```typescript\nimport { Controller, Post, Body, HttpCode, Catch } from \"@nestjs/common\";\nimport { AuthService } from \"./auth.service\";\nimport { InvalidCredentialsException } from \"nestjs-supabase-auth\";\n\n@Controller(\"auth\")\nexport class AuthController {\n  constructor(private authService: AuthService) {}\n\n  @Post(\"login\")\n  @HttpCode(200)\n  async login(@Body() credentials) {\n    try {\n      return await this.authService.signIn(\n        credentials.email,\n        credentials.password\n      );\n    } catch (error) {\n      if (error instanceof InvalidCredentialsException) {\n        // Handle the specific error\n        throw error;\n      }\n      // Handle other errors\n      throw error;\n    }\n  }\n}\n```\n\n## Development\n\n### Testing\n\nThis library includes comprehensive unit tests. To run the tests:\n\n```bash\nnpm test\n```\n\nTo run tests with coverage:\n\n```bash\nnpm run test:cov\n```\n\nSee [test/README.md](test/README.md) for more details on the test structure.\n\n### Building\n\nTo build the library:\n\n```bash\nnpm run build\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md"}