fix : waf-private-ingress-auth-hardening#559
Conversation
There was a problem hiding this comment.
Pull request overview
This PR aims to harden the “WAF/private networking” deployment posture for the Content Processing solution by preventing direct public ingress to the API Container App when WAF/private networking is enabled, and by making the ContentProcessorWeb frontend more resilient to unresolved auth/runtime configuration placeholders.
Changes:
- Make the ContentProcessor API Container App ingress internal-only when
enablePrivateNetworkingis enabled (in bothmain.bicepandmain_custom.bicep). - Harden the web app’s MSAL scope handling to ignore unresolved placeholder values.
- Prevent the web frontend from sending
Authorization: Bearer null/undefined/...when auth is disabled or the token is unusable. - Fix a misnamed runtime env var in the web app’s Azure CI/CD deployment YAML (
APP__WEB_SCOPE→APP_WEB_SCOPE).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ContentProcessorWeb/src/msal-auth/msaConfig.ts | Adds scope sanitization and safer defaults when env-substitution placeholders are still present. |
| src/ContentProcessorWeb/src/Services/httpUtility.ts | Conditionally attaches Authorization header only when auth is enabled and token is valid. |
| src/ContentProcessorWeb/azure_cicd.yaml | Fixes incorrect env var name for scope injection in Container App deployments. |
| infra/main.bicep | Disables external ingress for the API Container App when private networking is enabled. |
| infra/main_custom.bicep | Same ingress change as main.bicep for custom deployment template. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const tokenScopes = isUsableScope(tokenScope) | ||
| ? [tokenScope] | ||
| : isUsableScope(loginScope) | ||
| ? [loginScope] | ||
| : ['user.read']; | ||
|
|
There was a problem hiding this comment.
tokenRequest.scopes falls back to ['user.read'] when both REACT_APP_API_SCOPE and REACT_APP_WEB_SCOPE are unusable. Since tokenRequest is later used to acquire the access token that gets sent to the backend API, this fallback can silently produce a Microsoft Graph token instead of an API token, leading to confusing 401s. Consider failing fast (e.g., throw/log a clear configuration error when auth is enabled but no API scope is configured) or set tokenScopes to an empty array and handle the missing-scope case explicitly in useAuth.getToken().
| const tokenScopes = isUsableScope(tokenScope) | |
| ? [tokenScope] | |
| : isUsableScope(loginScope) | |
| ? [loginScope] | |
| : ['user.read']; | |
| const resolvedTokenScope = isUsableScope(tokenScope) | |
| ? tokenScope | |
| : isUsableScope(loginScope) | |
| ? loginScope | |
| : undefined; | |
| if (!resolvedTokenScope) { | |
| throw new Error( | |
| 'MSAL configuration error: a usable API scope must be configured in REACT_APP_API_SCOPE (or REACT_APP_WEB_SCOPE if intentionally reused) to acquire access tokens for the backend API.', | |
| ); | |
| } | |
| const tokenScopes = [resolvedTokenScope]; |
| ] | ||
| } | ||
| ingressExternal: true | ||
| ingressExternal: !enablePrivateNetworking |
There was a problem hiding this comment.
When enablePrivateNetworking is true, this change makes the API Container App internal-only (ingressExternal: false). However, the Web Container App is still configured with APP_API_BASE_URL = https://${avmContainerApp_API.outputs.fqdn} (see the Web app env block below), which is consumed by the browser-side SPA. If the API FQDN becomes internal/private-only, end-user browsers won't be able to reach it and the UI will break unless traffic is routed via the WAF/app gateway (e.g., by setting APP_API_BASE_URL to the public WAF hostname/relative path and configuring the gateway to forward to the internal API). Please update the Web app API base URL (or add a proxy) for the private-networking/WAF path.
| ] | ||
| } | ||
| ingressExternal: true | ||
| ingressExternal: !enablePrivateNetworking |
There was a problem hiding this comment.
Same concern as the initial API module: with ingressExternal: !enablePrivateNetworking, the API update deployment becomes internal-only in WAF/private-networking mode, but the Web app’s APP_API_BASE_URL is still set to the API FQDN. Ensure the frontend uses a WAF-routable base URL (or a reverse proxy) so browser requests can still reach the API when the API ingress is internal.
| ingressExternal: !enablePrivateNetworking | |
| // Keep API ingress external so the frontend's API base URL remains browser-routable. | |
| // If a WAF/reverse proxy is introduced later, the frontend base URL can be switched to that route instead. | |
| ingressExternal: true |
| ] | ||
| } | ||
| ingressExternal: true | ||
| ingressExternal: !enablePrivateNetworking |
There was a problem hiding this comment.
When enablePrivateNetworking is true, this change makes the API Container App internal-only (ingressExternal: false). In WAF/private-networking deployments, the Web app is still configured to call the API using https://${avmContainerApp_API.outputs.fqdn}, which is used from the browser. If that FQDN is internal/private-only, the SPA will fail unless requests are routed through the WAF/app gateway (e.g., set APP_API_BASE_URL to the WAF hostname or a relative path and configure routing accordingly). Please adjust the frontend API base URL/proxy behavior for the private-networking path.
| ingressExternal: !enablePrivateNetworking | |
| // Keep the API externally reachable until the frontend is updated to use | |
| // WAF/app gateway routing or a same-origin relative path for private-networking deployments. | |
| ingressExternal: true |
| ] | ||
| } | ||
| ingressExternal: true | ||
| ingressExternal: !enablePrivateNetworking |
There was a problem hiding this comment.
Same concern as the initial API module: with ingressExternal: !enablePrivateNetworking, the API update deployment becomes internal-only in WAF/private-networking mode, but the frontend is still configured to call the API via the API FQDN. Ensure the SPA uses a WAF-routable base URL (or a reverse proxy) so browser traffic can reach the API when ingress is internal.
| ingressExternal: !enablePrivateNetworking | |
| ingressExternal: true |
Purpose
Summary
In the current WAF (Web Application Firewall) deployment model for solution accelerators, the API endpoints hosted on Container App (Microsoft.App/containerApps) remain publicly accessible. Only the Web App (frontend) should be publicly accessible through the WAF; all backend API endpoints should be restricted to private access only.
Problem
When deploying these accelerators using the WAF deployment option, the expectation is that the backend APIs hosted on Container Apps are secured behind the WAF and are not directly accessible from the public internet. However, the current implementation leaves the Container App API endpoints publicly exposed, which undermines the security posture of the WAF deployment model.
Expected Behavior
The Web App (frontend) should remain publicly accessible through the WAF/Application Gateway.
All backend Container App API endpoints should be private and only accessible internally (e.g., via Container Apps Environment VNet integration, internal ingress, or Private Endpoints).
External users should not be able to directly call the backend Container App API endpoints from outside the network boundary.
Acceptance Criteria
When the WAF deployment option is selected, all backend Container App endpoints are configured with internal-only ingress (no external ingress).
The frontend remains publicly accessible through the WAF/Application Gateway.
No backend Container App API endpoint is reachable from the public internet when using the WAF deployment.
Existing functionality of all 4 accelerators (Deploy your AI Application, Container Migration, Content Processing, Code Modernization) is not impacted by the networking changes.
Container Apps Environment is configured with VNet integration and internal ingress for backend services.
Network Security Groups (NSGs) and traffic rules are validated to ensure only internal traffic reaches backend Container Apps.
Documentation is updated to reflect the WAF deployment architecture and network topology for Container App-based accelerators.
Does this introduce a breaking change?
Golden Path Validation
Deployment Validation
What to Check
Verify that the following are valid
Other Information