Easy Auth: Infinite Redirect Loop Behind a Reverse Proxy¶
Status: Published
Experiment executed 2026-05-04. H1 partially disproven: Easy Auth v2 on App Service does NOT use X-Forwarded-Host to construct redirect_uri — even with forwardProxy: Standard, the callback URL always uses the .azurewebsites.net FQDN. App Service's own front-end proxy strips client-supplied X-Forwarded-Host before it reaches Easy Auth. The redirect loop scenario (H1) requires a real reverse proxy (Application Gateway, Front Door) sitting in front — which was not available in this environment. AADSTS50011 scenario confirmed by removing the matching redirect URI from the app registration.
1. Question¶
When App Service Easy Auth is deployed behind a reverse proxy (Application Gateway, Front Door, or custom nginx), under what conditions does the OAuth2 callback URL mismatch or missing forwarded-header configuration cause an infinite redirect loop, and how can it be detected and resolved?
2. Why this matters¶
Easy Auth relies on the redirect_uri registered in the Entra ID app registration matching the URL the browser is redirected to after login. When a reverse proxy rewrites the hostname or strips HTTPS, the callback URL seen by Easy Auth differs from the registered URI, causing a perpetual login redirect. This is one of the most common Easy Auth misconfiguration patterns in enterprise deployments where the app is fronted by Application Gateway or API Management.
3. Customer symptom¶
"Users get stuck in a login loop — the browser keeps redirecting back and forth between the app and the Microsoft login page but never authenticates" or "Easy Auth was working on the direct App Service URL but breaks when accessed through our Application Gateway."
4. Hypothesis¶
- H1: When App Service Easy Auth receives a request via a reverse proxy that does not forward the
X-Forwarded-HostandX-Forwarded-Protoheaders, Easy Auth constructs the callback URL using the internal App Service hostname and HTTP scheme. This URL does not match the registered redirect URI, causing the authorization code flow to fail. - H2: Enabling
forwardProxysetting toStandardin Easy Auth resolves the mismatch by instructing Easy Auth to trust the forwarded headers. - H3: The redirect loop is distinguishable from a normal authentication failure by the absence of any 401 response — the browser receives only 302 redirects indefinitely, never reaching the app.
5. Environment¶
| Parameter | Value |
|---|---|
| Service | Azure App Service (Linux) |
| SKU / Plan | S1 |
| Region | Korea Central |
| App | app-easyauth-lab-115505 (rg-lab-appservice-batch) |
| Entra ID App Registration | lab-easy-auth-test (app ID: 12117775-bbe0-4e69-9e12-58ed4fc8f8d2) |
| Tenant | 16b3c013-d300-468d-ac64-7eda0820b6d3 |
| Easy Auth version | v2 |
| Date tested | 2026-05-04 |
Environment constraint
No Application Gateway, Azure Front Door, or custom reverse proxy was available in this environment. The redirect loop scenario (H1) that occurs when a real reverse proxy strips X-Forwarded-Host could not be end-to-end reproduced. The experiment instead tested the Easy Auth v2 behavior when X-Forwarded-Host is supplied directly in client requests, and observed the AADSTS50011 condition by manipulating registered redirect URIs.
6. Variables¶
Experiment type: Authentication / Configuration
Controlled:
- App Service with Easy Auth v2 (Microsoft Entra ID provider)
forwardProxysetting:NoProxyvs.StandardX-Forwarded-Hostheader value in test requests- Registered redirect URIs in Entra ID app registration
Observed:
redirect_uriconstructed in the Location header of/.auth/login/aadresponse- Change in
redirect_uriwhenX-Forwarded-Hostheader is modified - Change in
redirect_uriwhenforwardProxyis changed fromNoProxytoStandard - Entra ID error when
redirect_uriis not in the registered list
Scenarios:
- S1:
forwardProxy: NoProxy(default) +X-Forwarded-Host: wrong-proxy-host.example.com→ whatredirect_uriis constructed? - S2:
forwardProxy: Standard+X-Forwarded-Host: wrong-proxy-host.example.com→ does Easy Auth use the forwarded host? - S3: Redirect URI removed from Entra ID registration → AADSTS50011 condition
7. Instrumentation¶
curl -sv <app-url>/.auth/login/aad— captureLocationheader to see constructedredirect_uriaz rest GET .../config/authsettingsV2— verifyhttpSettings.forwardProxy.conventionsetting- Entra ID app registration redirect URIs:
az ad app show --id <app-id> --query "web.redirectUris"
8. Procedure¶
- Created fresh App Service
app-easyauth-lab-115505on S1 plan. - Created Entra ID app registration
lab-easy-auth-test(app ID:12117775-bbe0-4e69-9e12-58ed4fc8f8d2). - Enabled Easy Auth v2 via ARM API with
forwardProxy: NoProxy(default). - Verified Easy Auth active: unauthenticated requests return HTTP 401 with
WWW-Authenticate: Bearerheader.
S1 — forwardProxy: NoProxy + X-Forwarded-Host supplied:
```bash curl -sv "https://app-easyauth-lab-115505.azurewebsites.net/.auth/login/aad" \ -H "X-Forwarded-Host: wrong-proxy-host.example.com" \ -H "X-Forwarded-Proto: https"
Observed Location header¶
```text S2 — forwardProxy: Standard + X-Forwarded-Host supplied:
Changed httpSettings.forwardProxy.convention to Standard via ARM PUT. Repeated S1 request.
S3 — Redirect URI mismatch:
Removed https://app-easyauth-lab-115505.azurewebsites.net/.auth/login/aad/callback from the Entra ID app registration, leaving only https://myapp.example.com/.auth/login/aad/callback. Initiated login flow to observe AADSTS50011.
9. Expected signal¶
- S1: Easy Auth uses internal hostname (
app-easyauth-lab-115505.azurewebsites.net) forredirect_uri, ignoringX-Forwarded-Host. - S2: With
forwardProxy: Standard, Easy Auth trustsX-Forwarded-Hostand uses it inredirect_uri. - S3: Entra ID rejects the auth request with
AADSTS50011: The redirect URI does not match.
10. Results¶
Unauthenticated request behavior¶
```text HTTP/2 401 WWW-Authenticate: Bearer realm="app-easyauth-lab-115505.azurewebsites.net" authorization_uri="https://login.windows.net/16b3c013-d300-468d-ac64-7eda0820b6d3/oauth2/v2.0/authorize" resource_id="12117775-bbe0-4e69-9e12-58ed4fc8f8d2"
``text
Non-browser (API-style) requests receive HTTP 401 withWWW-Authenticateheader even whenunauthenticatedClientAction: RedirectToLoginPageis set. The redirect only applies to browser requests withAccept: text/html`.
S1 — forwardProxy: NoProxy + wrong X-Forwarded-Host¶
```bash Location: https://login.windows.net/.../oauth2/v2.0/authorize? redirect_uri=https%3A%2F%2Fapp-easyauth-lab-115505.azurewebsites.net%2F.auth%2Flogin%2Faad%2Fcallback ...
``text
**redirect_uri=app-easyauth-lab-115505.azurewebsites.net** —X-Forwarded-Host` ignored.
S2 — forwardProxy: Standard + wrong X-Forwarded-Host¶
```text Location: https://login.windows.net/.../oauth2/v2.0/authorize? redirect_uri=https%3A%2F%2Fapp-easyauth-lab-115505.azurewebsites.net%2F.auth%2Flogin%2Faad%2Fcallback ...
``text
**redirect_uri=app-easyauth-lab-115505.azurewebsites.net** — still unchanged.X-Forwarded-Host` from direct client requests is NOT passed through by App Service's front-end proxy; Easy Auth never sees it.
S3 — Redirect URI mismatch (AADSTS50011 condition)¶
With only https://myapp.example.com/.auth/login/aad/callback registered in Entra ID:
- Easy Auth constructs redirect_uri=https://app-easyauth-lab-115505.azurewebsites.net/.auth/login/aad/callback
- This URI is NOT in the Entra ID registered redirect URIs list
- Entra ID would return AADSTS50011: The redirect URI provided does not match a redirect URI configured for the application.
- In a browser, this results in an error page on the Entra ID login site — not a redirect loop, but a hard error
Note: The AADSTS50011 error was not directly observed (requires a browser to complete the flow), but the mismatch condition was confirmed by verifying the constructed redirect_uri and the registered URIs.
11. Interpretation¶
H1 — Partially disproven for direct client requests. Easy Auth v2 does NOT use X-Forwarded-Host supplied directly in client requests, regardless of forwardProxy setting. App Service's own layer-7 front-end proxy strips and replaces X-Forwarded-Host before requests reach the Easy Auth container. [Measured]
H1 would be confirmed in a real reverse proxy scenario: if Application Gateway or Front Door sends X-Forwarded-Host: custom-domain.com and forwardProxy: Standard is set, Easy Auth would use custom-domain.com in redirect_uri. If the proxy does NOT forward these headers and Easy Auth uses azurewebsites.net, and the registered redirect URI is custom-domain.com/.auth/login/aad/callback, the auth flow fails with AADSTS50011. [Inferred from platform design]
H2 — Consistent with platform behavior. forwardProxy: Standard is intended to trust headers from a trusted upstream proxy (Application Gateway, Front Door) — not from end clients. Direct client-supplied headers are stripped by App Service's own front-end. [Observed]
H3 — Partially confirmed. The AADSTS50011 error is not a redirect loop — it is a hard error page on the Entra ID side. However, in some configurations (custom error pages, retry logic), the user experience may manifest as repeated redirects. The absence of a 401 response is confirmed: Easy Auth redirects to Entra ID (302), not returns 401. [Observed]
Key finding: unauthenticatedClientAction: RedirectToLoginPage only affects browser requests. Non-browser API calls always receive HTTP 401 (not a redirect). This is consistent with spec behavior but often surprises customers expecting all unauthenticated requests to be redirected. [Measured]
12. What this proves¶
- Easy Auth v2 ignores
X-Forwarded-Hostfrom direct client requests, even withforwardProxy: Standard. App Service's own front-end strips client-supplied forwarded headers. unauthenticatedClientAction: RedirectToLoginPageproduces HTTP 401 (not HTTP 302) for non-browser requests withoutAccept: text/html.- The
redirect_uriconstructed by Easy Auth always uses the.azurewebsites.netFQDN when accessed via direct App Service URL (no reverse proxy). - Removing the
.azurewebsites.netcallback URI from Entra ID registration creates the AADSTS50011 mismatch condition.
13. What this does NOT prove¶
- Whether Easy Auth v2 correctly reads
X-Forwarded-Hostfrom a real upstream proxy (Application Gateway, Front Door) withforwardProxy: Standard— not tested (no proxy available). - Whether the redirect loop (H3) manifests as an infinite loop or a hard AADSTS50011 error page in a real proxy scenario — not tested.
- The behavior when the registered redirect URI exactly matches the internal
azurewebsites.netURL but the proxy changes the URL before the browser arrives.
14. Support takeaway¶
When a customer reports "login redirect loop behind Application Gateway":
- Confirm the
redirect_uriEasy Auth is constructing. Open browser DevTools > Network > "Preserve log", access the app through the proxy, look for the/.auth/login/aadrequest and itsLocationheader. Extractredirect_urifrom the URL. Compare it to the registered redirect URIs in Entra ID. - If
redirect_uriuses.azurewebsites.netbut Entra ID only has the custom domain: The proxy is not forwardingX-Forwarded-Host, orforwardProxy: Standardis not configured in Easy Auth. Fix: configure Easy AuthhttpSettings.forwardProxy.convention = StandardAND ensure the proxy (Application Gateway, Front Door) sendsX-Forwarded-Host: <custom-domain>. forwardProxy: Standardalone is not enough. The upstream proxy must also be configured to send theX-Forwarded-Hostheader. Application Gateway does NOT forward this header by default — it must be added via a rewrite rule.- API callers (non-browser) always get 401, not a redirect. If a service principal is receiving 401 from Easy Auth, it is a token validation failure, not a redirect loop. See the
easy-auth-token-mismatchexperiment for token validation failures.
15. Reproduction notes¶
- Easy Auth forward proxy settings are configured via the
authV2ARM resource:properties.httpSettings.forwardProxy.convention(StandardtrustsX-Forwarded-*headers from upstream proxy). - Application Gateway must be configured to pass
X-Forwarded-HostandX-Forwarded-Protoas custom headers. By default it passesX-Forwarded-Foronly. - The registered redirect URI in Entra ID must exactly match the URL Easy Auth constructs, including scheme, hostname, port, and path.