From 470396c2f175b115d67abf997dd5ebe7f919b0c1 Mon Sep 17 00:00:00 2001 From: Rayyan <60314224+RayyanHermanto@users.noreply.github.com> Date: Mon, 3 Aug 2026 20:32:17 +0700 Subject: [PATCH] fix cors --- src/app.module.ts | 17 +++++++++++++++-- src/main.ts | 20 +++++++++++++++++++- src/modules/auth/auth.controller.ts | 13 +++++++++---- 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/app.module.ts b/src/app.module.ts index b2459ba..d91512d 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -12,8 +12,21 @@ import { ApplicationModule } from './modules/application/application.module'; @Module({ imports: [ ConfigModule.forRoot({ - isGlobal: true, - }), + isGlobal: true, + // Load .env files depending on NODE_ENV. Default to development .env + envFilePath: process.env.NODE_ENV === 'production' ? '.env.production' : '.env', + // Basic validation: ensure expected frontend URLs are present + validate: (env: Record) => { + const errors: string[] = []; + if (!env.FRONTEND_URL) errors.push('FRONTEND_URL is not set'); + if (!env.PRODUCTION_FRONTEND_URL) { + // production frontend URL is recommended but not mandatory for local development + if (process.env.NODE_ENV === 'production') errors.push('PRODUCTION_FRONTEND_URL is not set'); + } + if (errors.length > 0) throw new Error('Environment validation error: ' + errors.join('; ')); + return env; + }, + }), IdentityModule, AuthModule, diff --git a/src/main.ts b/src/main.ts index f6e22bd..71d5c0c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -20,7 +20,25 @@ async function bootstrap() { }), ); - app.enableCors(); + // CORS configuration: only allow configured frontend origins and enable credentials + const allowedOrigins: string[] = []; + const frontend = config.get('FRONTEND_URL'); + const prodFrontend = config.get('PRODUCTION_FRONTEND_URL'); + if (frontend) allowedOrigins.push(frontend); + if (prodFrontend) allowedOrigins.push(prodFrontend); + + app.enableCors({ + origin: allowedOrigins, + credentials: true, + methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], + allowedHeaders: [ + 'Content-Type', + 'Authorization', + 'Accept', + 'Origin', + 'X-Requested-With', + ], + }); const swaggerEnabled = config.get('SWAGGER_ENABLED') === 'true'; diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 5ef7553..79df924 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -3,11 +3,12 @@ import { Controller, Get, Post, Query, Res, Req, Body, HttpCode, HttpStatus } fr import { ApiTags, ApiOperation } from '@nestjs/swagger'; import { AuthService } from './auth.service'; import { Response, Request } from 'express'; +import { ConfigService } from '@nestjs/config'; @ApiTags('Auth') @Controller('auth') export class AuthController { - constructor(private readonly authService: AuthService) {} + constructor(private readonly authService: AuthService, private readonly config: ConfigService) {} @Get('login') @ApiOperation({ summary: 'Start Authorization Code + PKCE login (redirect to Identity Provider)' }) @@ -21,13 +22,17 @@ export class AuthController { async callback(@Query('code') code: string, @Query('state') state: string, @Res() res: Response) { const result = await this.authService.handleCallback(code, state); - // set cookies + // set cookies with environment-aware options + const isProd = this.config.get('NODE_ENV') === 'production' || process.env.NODE_ENV === 'production'; + const cookieDomain = this.config.get('RAYLAB_COOKIE_DOMAIN') || (isProd ? '.raylab.site' : undefined); + const cookieOptions: any = { httpOnly: true, - secure: process.env.NODE_ENV === 'production', - sameSite: 'lax', + secure: isProd, // secure in production + sameSite: isProd ? 'none' : 'lax', // cross-site in production path: '/', }; + if (cookieDomain) cookieOptions.domain = cookieDomain; // access token cookie (internal JWT) res.cookie('raylab_jwt', result.accessToken, { ...cookieOptions, maxAge: result.expiresIn * 1000 });