fix cors
Deploy / deploy (push) Successful in 34s

This commit is contained in:
Rayyan
2026-08-03 20:32:17 +07:00
parent 1826bc789e
commit 470396c2f1
3 changed files with 43 additions and 7 deletions
+15 -2
View File
@@ -12,8 +12,21 @@ import { ApplicationModule } from './modules/application/application.module';
@Module({ @Module({
imports: [ imports: [
ConfigModule.forRoot({ 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<string, any>) => {
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, IdentityModule,
AuthModule, AuthModule,
+19 -1
View File
@@ -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<string>('FRONTEND_URL');
const prodFrontend = config.get<string>('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<string>('SWAGGER_ENABLED') === 'true'; const swaggerEnabled = config.get<string>('SWAGGER_ENABLED') === 'true';
+9 -4
View File
@@ -3,11 +3,12 @@ import { Controller, Get, Post, Query, Res, Req, Body, HttpCode, HttpStatus } fr
import { ApiTags, ApiOperation } from '@nestjs/swagger'; import { ApiTags, ApiOperation } from '@nestjs/swagger';
import { AuthService } from './auth.service'; import { AuthService } from './auth.service';
import { Response, Request } from 'express'; import { Response, Request } from 'express';
import { ConfigService } from '@nestjs/config';
@ApiTags('Auth') @ApiTags('Auth')
@Controller('auth') @Controller('auth')
export class AuthController { export class AuthController {
constructor(private readonly authService: AuthService) {} constructor(private readonly authService: AuthService, private readonly config: ConfigService) {}
@Get('login') @Get('login')
@ApiOperation({ summary: 'Start Authorization Code + PKCE login (redirect to Identity Provider)' }) @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) { async callback(@Query('code') code: string, @Query('state') state: string, @Res() res: Response) {
const result = await this.authService.handleCallback(code, state); const result = await this.authService.handleCallback(code, state);
// set cookies // set cookies with environment-aware options
const isProd = this.config.get<string>('NODE_ENV') === 'production' || process.env.NODE_ENV === 'production';
const cookieDomain = this.config.get<string>('RAYLAB_COOKIE_DOMAIN') || (isProd ? '.raylab.site' : undefined);
const cookieOptions: any = { const cookieOptions: any = {
httpOnly: true, httpOnly: true,
secure: process.env.NODE_ENV === 'production', secure: isProd, // secure in production
sameSite: 'lax', sameSite: isProd ? 'none' : 'lax', // cross-site in production
path: '/', path: '/',
}; };
if (cookieDomain) cookieOptions.domain = cookieDomain;
// access token cookie (internal JWT) // access token cookie (internal JWT)
res.cookie('raylab_jwt', result.accessToken, { ...cookieOptions, maxAge: result.expiresIn * 1000 }); res.cookie('raylab_jwt', result.accessToken, { ...cookieOptions, maxAge: result.expiresIn * 1000 });