PWM stuck in login redirect loop behind Microsoft Entra ID App Proxy

87 views
Skip to first unread message

Jiří Wetter

unread,
Apr 10, 2025, 7:40:47 AMApr 10
to pwm-general

Summary:
When deploying PWM behind Microsoft Entra ID Application Proxy, the application enters an infinite redirect loop between /private/login and /private/login?sessionVerificationKey=Key, and resulting in a “Too Many Redirects” error in the browser.

pwm.PwmApplication, PWM v2.0.8 bb7ed22b

Internal Url and External Url under Microsoft Entra ID Application Proxy are different.


Screenshot 2025-04-10 at 9.04.32.pngScreenshot 2025-04-10 at 9.02.14.png

Currently I don't know how to eliminate the problem. After turning on TRACE logging there is nothing interesting. 

After disabling Sticky Session Verification this problem disappears but I end up on a white page where in the address bar I have https://*.msappproxy.net/private/login. After I press the enter in the address bar, I can successfully get into the application. Which is not correct.

Didn't anyone deal with a similar setup?

Thank you

Jason Rivard

unread,
Apr 10, 2025, 8:22:14 PMApr 10
to pwm-general
I personally haven't tested PWM with the azure proxy, perhaps others have.  PWM is deliberately designed to work well behind an HTTP proxy, having different host names internally and externally should not cause a problem assuming host headers are correctly translated.

PWM's sticky-session verification is designed to verify that the upstream proxy server is both behaving properly and is also keeping sessions sticky with the downstream HTTP server (PWM in this case).  The process is that PWM creates a random value and stores it in its local session for the user, then redirects the user to itself with that same value as an HTTP parameter.  If the proxy is behaving properly, the user returns to the same PWM server and the parameter key on the request matches the in-memory session of the user.  The user session is tied to the JSESSION and other cookies PWM sets.

The fact that this process is failing completely means there is some odd behavior from the proxy server.  This should not be a difficult test to pass.  If you only have a single PWM server it probably means the proxy is doing something to PWM's various session cookies.  If you do have multiple PWM servers this process will eventually succeed when the client session eventually hits a server that has already been visited.  The count of times this happens is tracked as a PWM server statistic and is noted in the logs, but shouldn't otherwise cause a problem to the client.

For the record, having the HTTP session be sticky to a particular PWM server is important because in turn, LDAP is a connection oriented protocol and the  LDAP connection state can not be shared between PWM servers.  In the real world HTTP sessions are never perfectly sticky and PWM can deal with this but it increases some of the odd corner cases that can happen with LDAP. 

I don't think it would cause a problem this early in the session but I'd suggest checking the setting ' Settings ⇨ Application ⇨ Site URL'  is set correctly to the externally viewable URL.  Otherwise I'm not sure what specifically to look at is this would require some debugging into what the azure proxy is actually doing.

Jiří Wetter

unread,
Apr 11, 2025, 8:45:38 AMApr 11
to pwm-general
I've already tried setting the "Settings ⇨ Application ⇨ Site URL" to the externally accessible address, but unfortunately it didn't make any difference. The behavior remains the same – a new session is created on each request. 

2025-04-11T14:00:26.585+02:002025-04-11T12:00:26Z, TRACE, http.PwmSession, created new session
2025-04-11T14:00:26.585+02:002025-04-11T12:00:26Z, TRACE, http.PwmSessionWrapper, {OQJGB} setting java servlet session timeout to 50m due to Setting Settings ⇨ Application ⇨ Application ⇨ Idle Timeout Seconds
2025-04-11T14:00:26.814+02:002025-04-11T12:00:26Z, TRACE, filter.RequestInitializationFilter, {OQJGB} user locale set to 'en' [85.86.87.88]
2025-04-11T14:00:26.814+02:002025-04-11T12:00:26Z, TRACE, http.PwmRequest, {OQJGB} GET request for: / (no params)  [85.86.87.88]
2025-04-11T14:00:26.814+02:002025-04-11T12:00:26Z, TRACE, filter.SessionFilter, {OQJGB} http non-secure request headers:  [85.86.87.88]
2025-04-11T14:00:26.814+02:00  host='passwordmanager.dev.example.com'
2025-04-11T14:00:26.814+02:00  user-agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36'
2025-04-11T14:00:26.814+02:00  accept='text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7'
2025-04-11T14:00:26.814+02:00  accept-encoding='gzip, deflate, br, zstd'
2025-04-11T14:00:26.814+02:00  accept-language='cs-CZ,cs;q=0.9'
2025-04-11T14:00:26.814+02:00  cache-control='max-age=0'
2025-04-11T14:00:26.814+02:00  dnt='1'
2025-04-11T14:00:26.814+02:00  referer='https://login.microsoftonline.com/'
2025-04-11T14:00:26.814+02:00  sec-ch-ua='"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"'
2025-04-11T14:00:26.814+02:00  sec-ch-ua-mobile='?0'
2025-04-11T14:00:26.814+02:00  sec-ch-ua-platform='"macOS"'
2025-04-11T14:00:26.814+02:00  sec-fetch-dest='document'
2025-04-11T14:00:26.814+02:00  sec-fetch-mode='navigate'
2025-04-11T14:00:26.814+02:00  sec-fetch-site='cross-site'
2025-04-11T14:00:26.814+02:00  server-timing='intid;desc=20eca66f9043ebb0'
2025-04-11T14:00:26.814+02:00  traceparent='00-000000000000000020eca66f9043ebb0-7aaf783820536a7a-01'
2025-04-11T14:00:26.814+02:00  tracestate='in=20eca66f9043ebb0;7aaf783820536a7a'
2025-04-11T14:00:26.814+02:00  upgrade-insecure-requests='1'
2025-04-11T14:00:26.814+02:00  x-appgw-trace-id='9b4d4661a56aae7aab0ce168b3539241'
2025-04-11T14:00:26.814+02:00  x-forwarded-for='85.86.87.88, 104.45.77.142:4945, 10.143.40.7'
2025-04-11T14:00:26.814+02:00  x-forwarded-host='passwordmanager.dev.example.com'
2025-04-11T14:00:26.814+02:00  x-forwarded-port='443'
2025-04-11T14:00:26.814+02:00  x-forwarded-proto='https'
2025-04-11T14:00:26.814+02:00  x-forwarded-server='traefik-69bd47d458-2hcw4'
2025-04-11T14:00:26.814+02:00  x-instana-l='1'
2025-04-11T14:00:26.814+02:00  x-instana-s='7aaf783820536a7a'
2025-04-11T14:00:26.814+02:00  x-instana-t='20eca66f9043ebb0'
2025-04-11T14:00:26.814+02:00  x-ms-proxy='AzureAD-Application-Proxy'
2025-04-11T14:00:26.814+02:00  x-original-host='passwordmanager.dev.example.com'
2025-04-11T14:00:26.814+02:00  x-original-url='/'
2025-04-11T14:00:26.814+02:00  x-real-ip='10.143.40.7'
2025-04-11T14:00:26.814+02:00  l5d-client-id='traefik-ingress-controller.ingresscontrollers.serviceaccount.identity.linkerd.cluster.local'
2025-04-11T14:00:26.814+02:002025-04-11T12:00:26Z, TRACE, filter.SessionFilter, {OQJGB} session has not been validated, redirecting with verification key to /?stickyRedirectTest=key [85.86.87.88]
2025-04-11T14:00:26.815+02:002025-04-11T12:00:26Z, TRACE, state.CryptoCookieLoginImpl, {OQJGB} wrote LoginInfoBean={"a":false,"p":"*hidden*","t":"UNAUTHENTICATED","af":[],"rq":"2025-04-11T12:00:26Z","g":"m9cqjj6hYKXUV2RXmyFbUbNTNazRhNs0ya9OkHHUCKxtusmWdplOeQupBV7HKDEKpc0Wr64D","c":0,"lf":[]} [85.86.87.88]
2025-04-11T14:00:26.815+02:002025-04-11T12:00:26Z, TRACE, http.PwmResponse, {OQJGB} sending 302 redirect to /?stickyRedirectTest=key [85.86.87.88]

2025-04-11T14:00:26.936+02:002025-04-11T12:00:26Z, TRACE, http.HttpEventManager, new http session created
2025-04-11T14:00:26.936+02:002025-04-11T12:00:26Z, TRACE, http.PwmSession, created new session
2025-04-11T14:00:26.936+02:002025-04-11T12:00:26Z, TRACE, http.PwmSessionWrapper, {vZJeP} setting java servlet session timeout to 50m due to Setting Settings ⇨ Application ⇨ Application ⇨ Idle Timeout Seconds
2025-04-11T14:00:27.048+02:002025-04-11T12:00:27Z, TRACE, filter.RequestInitializationFilter, {vZJeP} user locale set to 'en' [85.86.87.88]
2025-04-11T14:00:27.048+02:002025-04-11T12:00:27Z, TRACE, http.PwmRequest, {vZJeP} GET request for: /  [85.86.87.88]
2025-04-11T14:00:27.048+02:00  stickyRedirectTest='key'
2025-04-11T14:00:27.048+02:002025-04-11T12:00:27Z, TRACE, filter.SessionFilter, {vZJeP} http non-secure request headers:  [85.86.87.88]
2025-04-11T14:00:27.048+02:00  host='passwordmanager.dev.example.com'
2025-04-11T14:00:27.048+02:00  user-agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36'
2025-04-11T14:00:27.048+02:00  accept='text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7'
2025-04-11T14:00:27.048+02:00  accept-encoding='gzip, deflate, br, zstd'
2025-04-11T14:00:27.048+02:00  accept-language='cs-CZ,cs;q=0.9'
2025-04-11T14:00:27.048+02:00  cache-control='max-age=0'
2025-04-11T14:00:27.048+02:00  dnt='1'
2025-04-11T14:00:27.048+02:00  referer='https://login.microsoftonline.com/'
2025-04-11T14:00:27.048+02:00  sec-ch-ua='"Google Chrome";v="135", "Not-A.Brand";v="8", "Chromium";v="135"'
2025-04-11T14:00:27.048+02:00  sec-ch-ua-mobile='?0'
2025-04-11T14:00:27.048+02:00  sec-ch-ua-platform='"macOS"'
2025-04-11T14:00:27.048+02:00  sec-fetch-dest='document'
2025-04-11T14:00:27.048+02:00  sec-fetch-mode='navigate'
2025-04-11T14:00:27.048+02:00  sec-fetch-site='cross-site'
2025-04-11T14:00:27.048+02:00  server-timing='intid;desc=47d6a5a4a6f0af2e'
2025-04-11T14:00:27.049+02:00  traceparent='00-000000000000000047d6a5a4a6f0af2e-1b3815e4bab6a985-01'
2025-04-11T14:00:27.049+02:00  tracestate='in=47d6a5a4a6f0af2e;1b3815e4bab6a985'
2025-04-11T14:00:27.049+02:00  upgrade-insecure-requests='1'
2025-04-11T14:00:27.049+02:00  x-appgw-trace-id='0b10d4dd0b2ac2231bf805b36ca1a169'
2025-04-11T14:00:27.049+02:00  x-forwarded-for='85.86.87.88, 104.45.77.142:4945, 10.143.40.7'
2025-04-11T14:00:27.049+02:00  x-forwarded-host='passwordmanager.dev.example.com'
2025-04-11T14:00:27.049+02:00  x-forwarded-port='443'
2025-04-11T14:00:27.049+02:00  x-forwarded-proto='https'
2025-04-11T14:00:27.049+02:00  x-forwarded-server='traefik-69bd47d458-2hcw4'
2025-04-11T14:00:27.049+02:00  x-instana-l='1'
2025-04-11T14:00:27.049+02:00  x-instana-s='1b3815e4bab6a985'
2025-04-11T14:00:27.049+02:00  x-instana-t='47d6a5a4a6f0af2e'
2025-04-11T14:00:27.049+02:00  x-ms-proxy='AzureAD-Application-Proxy'
2025-04-11T14:00:27.049+02:00  x-original-host='passwordmanager.dev.example.com'
2025-04-11T14:00:27.049+02:00  x-original-url='/?stickyRedirectTest=key'
2025-04-11T14:00:27.049+02:00  x-real-ip='10.143.40.7'
2025-04-11T14:00:27.049+02:00  l5d-client-id='traefik-ingress-controller.ingresscontrollers.serviceaccount.identity.linkerd.cluster.local'
2025-04-11T14:00:27.049+02:002025-04-11T12:00:27Z, TRACE, filter.SessionFilter, {vZJeP} session validated, redirecting to original request url: / [85.86.87.88]
2025-04-11T14:00:27.049+02:002025-04-11T12:00:27Z, TRACE, state.CryptoCookieLoginImpl, {vZJeP} wrote LoginInfoBean={"a":false,"p":"*hidden*","t":"UNAUTHENTICATED","af":[],"rq":"2025-04-11T12:00:27Z","g":"m9cqjjg8EajYOFTykgrd3oDXq0lMI2VnDjWWzbgvizbQC54WGyLPjLHf5HChNULE9pOElzY1","c":0,"lf":[]} [85.86.87.88]
2025-04-11T14:00:27.049+02:002025-04-11T12:00:27Z, TRACE, http.PwmResponse, {vZJeP} sending 302 redirect to / [85.86.87.88]

What is not clear to me is that if I click in the address bar and press enter during these endless redirections, I can get into the application.
Dne pátek 11. dubna 2025 v 2:22:14 UTC+2 uživatel Jason Rivard napsal:

Jason Rivard

unread,
Apr 11, 2025, 5:42:01 PMApr 11
to pwm-general
To me this is very suspicious:

2025-04-11T14:00:26.815+02:002025-04-11T12:00:26Z, TRACE, http.PwmResponse, {OQJGB} sending 302 redirect to /?stickyRedirectTest=key [85.86.87.88]
2025-04-11T14:00:26.936+02:002025-04-11T12:00:26Z, TRACE, http.HttpEventManager, new http session created
2025-04-11T14:00:26.936+02:002025-04-11T12:00:26Z, TRACE, http.PwmSession, created new session

This shows the redirect happening, but then the next request coming in for "/" is creating a new session.    This is almost certainly because PWM isn't receiving back the cookies it set on the "/?stickyRedirectTest=key" response.  I'd guess the proxy is either mangling or stripping the cookies set by PWM, otherwise PWM wouldn't e creating a new session.  The HttpEventManager log entry is showing tomcat missing it's session cookie, so this is even before PWM code executes.  Tomcat is looking specifically for the JSESSIONID cookie.

Paul Hodgdon

unread,
Apr 22, 2025, 9:08:28 PMApr 22
to pwm-general
I wonder if you try http.cookie.sameSite.value=Lax or http.cookie.sameSite.value=None in the App Property overrides if it resolves it. I've found that is needed for SSO with Entra using OAuth.
-Paul

Jiří Wetter

unread,
Apr 23, 2025, 5:13:18 AMApr 23
to pwm-general
Microsoft Entra ID App Proxy doesn't have such options (pictures provided previously) and I didn't find anything in the app too. If so, please tell me where this can be set.
Dne středa 23. dubna 2025 v 3:08:28 UTC+2 uživatel Paul Hodgdon napsal:

Jason Rivard

unread,
Apr 24, 2025, 11:17:12 PMApr 24
to pwm-general
The settings mention by paul are PWM settings that control cookie flags set by PWM in the Settings -> App  section.   I'm not sure how they might change the situation but they are worth a shot.  I'm don't have any better ideas.

Does the MS proxy give you any debug/tracing options?  Can you see what happens to the cookie headers as the proxy works?
Reply all
Reply to author
Forward
0 new messages