-
Notifications
You must be signed in to change notification settings - Fork 189
fix : waf-private-ingress-auth-hardening #559
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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 | ||||||||||
|
||||||||||
| 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 |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1132,7 +1132,7 @@ module avmContainerApp_API 'br/public:avm/res/app/container-app:0.19.0' = { | |||||||||
| } | ||||||||||
| ] | ||||||||||
| } | ||||||||||
| ingressExternal: true | ||||||||||
| ingressExternal: !enablePrivateNetworking | ||||||||||
|
||||||||||
| 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 |
Copilot
AI
Apr 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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,14 +41,25 @@ 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']; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+49
to
+54
|
||||||||||||||||||||||||||||||||||||||
| 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]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
enablePrivateNetworkingis true, this change makes the API Container App internal-only (ingressExternal: false). However, the Web Container App is still configured withAPP_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 settingAPP_API_BASE_URLto 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.