From 2e11654b1613aabd10870e8950330b010b238b36 Mon Sep 17 00:00:00 2001 From: "Ashwal Vishwanath (Persistent Systems Inc)" Date: Wed, 22 Apr 2026 14:32:01 +0530 Subject: [PATCH] Fix WAF API ingress and harden web auth configuration --- infra/main.bicep | 4 +-- infra/main_custom.bicep | 4 +-- src/ContentProcessorWeb/azure_cicd.yaml | 4 +-- .../src/Services/httpUtility.ts | 24 ++++++++++++++++-- .../src/msal-auth/msaConfig.ts | 25 +++++++++++++++++-- 5 files changed, 51 insertions(+), 10 deletions(-) diff --git a/infra/main.bicep b/infra/main.bicep index c29e258f..b137a9ec 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -1119,7 +1119,7 @@ module avmContainerApp_API 'br/public:avm/res/app/container-app:0.19.0' = { } ] } - ingressExternal: true + ingressExternal: !enablePrivateNetworking activeRevisionsMode: 'Single' ingressTransport: 'auto' corsPolicy: { @@ -1771,7 +1771,7 @@ module avmContainerApp_API_update 'br/public:avm/res/app/container-app:0.19.0' = } ] } - ingressExternal: true + ingressExternal: !enablePrivateNetworking activeRevisionsMode: 'Single' ingressTransport: 'auto' corsPolicy: { diff --git a/infra/main_custom.bicep b/infra/main_custom.bicep index 3294106b..d86eeb76 100644 --- a/infra/main_custom.bicep +++ b/infra/main_custom.bicep @@ -1132,7 +1132,7 @@ module avmContainerApp_API 'br/public:avm/res/app/container-app:0.19.0' = { } ] } - ingressExternal: true + ingressExternal: !enablePrivateNetworking activeRevisionsMode: 'Single' ingressTransport: 'auto' corsPolicy: { @@ -1804,7 +1804,7 @@ module avmContainerApp_API_update 'br/public:avm/res/app/container-app:0.19.0' = } ] } - ingressExternal: true + ingressExternal: !enablePrivateNetworking activeRevisionsMode: 'Single' ingressTransport: 'auto' corsPolicy: { diff --git a/src/ContentProcessorWeb/azure_cicd.yaml b/src/ContentProcessorWeb/azure_cicd.yaml index 3b4ed6a2..be27aa48 100644 --- a/src/ContentProcessorWeb/azure_cicd.yaml +++ b/src/ContentProcessorWeb/azure_cicd.yaml @@ -91,7 +91,7 @@ steps: --image $(acrLoginServer)/$(imageRepository):$(Build.BuildId) \ --cpu 4.0 \ --memory 8.0Gi \ - --set-env-vars APP_API_BASE_URL=$cpsApiBaseUrl APP_WEB_CLIENT_ID=$(appWebClientId) APP_WEB_AUTHORITY=$(appWebAuthority) APP__WEB_SCOPE=$(appWebScope) APP_API_SCOPE=$(appApiScope) APP_AUTH_ENABLED=false + --set-env-vars APP_API_BASE_URL=$cpsApiBaseUrl APP_WEB_CLIENT_ID=$(appWebClientId) APP_WEB_AUTHORITY=$(appWebAuthority) APP_WEB_SCOPE=$(appWebScope) APP_API_SCOPE=$(appApiScope) APP_AUTH_ENABLED=false else # Create the container app with the new image and registry settings az containerapp create \ @@ -104,7 +104,7 @@ steps: --registry-server $(acrLoginServer) \ --registry-identity $managedIdentityResourceId \ --ingress external \ - --env-vars APP_API_BASE_URL=$cpsApiBaseUrl APP_WEB_CLIENT_ID=$(appWebClientId) APP_WEB_AUTHORITY=$(appWebAuthority) APP__WEB_SCOPE=$(appWebScope) APP_API_SCOPE=$(appApiScope) APP_AUTH_ENABLED=false + --env-vars APP_API_BASE_URL=$cpsApiBaseUrl APP_WEB_CLIENT_ID=$(appWebClientId) APP_WEB_AUTHORITY=$(appWebAuthority) APP_WEB_SCOPE=$(appWebScope) APP_API_SCOPE=$(appApiScope) APP_AUTH_ENABLED=false fi displayName: 'Deploy container to Azure Container App' diff --git a/src/ContentProcessorWeb/src/Services/httpUtility.ts b/src/ContentProcessorWeb/src/Services/httpUtility.ts index a394f114..efbf890d 100644 --- a/src/ContentProcessorWeb/src/Services/httpUtility.ts +++ b/src/ContentProcessorWeb/src/Services/httpUtility.ts @@ -10,6 +10,20 @@ const api: string = process.env.REACT_APP_API_BASE_URL as string; +const isAuthEnabled = (): boolean => + process.env.REACT_APP_AUTH_ENABLED?.toLowerCase() !== 'false'; + +const isUsableToken = (token: string | null): token is string => { + if (!token) return false; + const value = token.trim(); + if (!value) return false; + if (value.toLowerCase() === 'null' || value.toLowerCase() === 'undefined') { + return false; + } + if (value.startsWith('APP_')) return false; + return true; +}; + interface FetchResponse { data: T | null; status: number; @@ -73,11 +87,14 @@ const fetchWithAuth = async ( const token = localStorage.getItem('token'); const headers: Record = { - 'Authorization': `Bearer ${token}`, 'Accept': 'application/json', 'Cache-Control': 'no-cache', }; + if (isAuthEnabled() && isUsableToken(token)) { + headers['Authorization'] = `Bearer ${token}`; + } + let processedBody: BodyInit | null = null; if (body instanceof FormData) { processedBody = body; @@ -132,12 +149,15 @@ const fetchHeadersWithAuth = async ( const token = localStorage.getItem('token'); const headers: Record = { - 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', 'Accept': 'application/json', 'Cache-Control': 'no-cache', }; + if (isAuthEnabled() && isUsableToken(token)) { + headers['Authorization'] = `Bearer ${token}`; + } + if (body instanceof FormData) { delete headers['Content-Type']; } else { diff --git a/src/ContentProcessorWeb/src/msal-auth/msaConfig.ts b/src/ContentProcessorWeb/src/msal-auth/msaConfig.ts index 938c7bbf..fb74f499 100644 --- a/src/ContentProcessorWeb/src/msal-auth/msaConfig.ts +++ b/src/ContentProcessorWeb/src/msal-auth/msaConfig.ts @@ -7,6 +7,16 @@ */ import { Configuration, LogLevel } from '@azure/msal-browser'; +const isUsableScope = (scope?: string): scope is string => { + if (!scope) return false; + const value = scope.trim(); + if (!value) return false; + // Guard against unresolved placeholders from env substitution. + if (value.startsWith('APP_')) return false; + if (value.startsWith('<') && value.endsWith('>')) return false; + return true; +}; + export const msalConfig: Configuration = { auth: { clientId: process.env.REACT_APP_WEB_CLIENT_ID as string, @@ -31,8 +41,19 @@ export const msalConfig: Configuration = { const loginScope = process.env.REACT_APP_WEB_SCOPE as string; const tokenScope = process.env.REACT_APP_API_SCOPE as string; +const loginScopes = ['user.read']; +if (isUsableScope(loginScope)) { + loginScopes.push(loginScope); +} + +const tokenScopes = isUsableScope(tokenScope) + ? [tokenScope] + : isUsableScope(loginScope) + ? [loginScope] + : ['user.read']; + export const loginRequest = { - scopes: ["user.read", loginScope], + scopes: loginScopes, }; export const graphConfig = { @@ -40,5 +61,5 @@ export const graphConfig = { }; export const tokenRequest = { - scopes: [tokenScope], + scopes: tokenScopes, }; \ No newline at end of file