HTTP-POST binding failing in CSP-enforcing browsers (SP-initiated SAML sign-in) #616
Unanswered
jwalker-hw
asked this question in
IdentityServer
Replies: 1 comment 2 replies
|
That seems like an issue. Raising this with our devs, and will follow up in this discussion when there's news 👍 |
2 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Has anyone else run into this in Duende 8+ w/ SAML ?
This is the same defect as discussion #425, on a page the 7.4.1 guard tests don't cover. Saml2PostBinding.PostHtmlFormatString (new in v8) carries the same hardcoded-hash-vs-whitespace mismatch. Please extend the existing test to enumerate every inline script including the SAML binding templates, so the next new page can't reintroduce it a third time.
The December fix (7.4.1, Dec 3 2025) was for the check session endpoint (/connect/checksession) -- a different page from Saml2PostBinding. Same root cause exactly: "whitespace changes in the script were not reflected in the hardcoded CSP hash constant." Same symptom & same class of defect.
@maartenba
Issue in:
Duende.IdentityServer.Internal.Saml.Sp.Bindings.Saml2PostBinding.PostHtmlFormatStringThat single const string contains both the CSP meta tag and the script it is meant to authorize:
The mismatch: the script element's content is exactly \ndocument.forms.samlPostBindingSubmit.submit();\n (48 bytes: LF, the statement, LF). Its SHA-256, base64-encoded, is:
IQKtK10TFgRroV/L1+sRadhw5yAEkHE3GlbgJgxr7K4=
The template declares H3SVZBYrbqBt3ncrT/nNmOb6nwCjC12cPQzh5jnW4Y0=. They do not match, so the browser refuses to execute the script that the page depends on to auto-submit.
Verification (independent of any consuming app's configuration):
Chrome's console reports the same required value:
Suggested fix:
Recompute the constant to sha256-IQKtK10TFgRroV/L1+sRadhw5yAEkHE3GlbgJgxr7K4=, or better, compute the hash from the script text at runtime/build so the two can't drift again. (The samlPostBindingSubmit naming and "press the Continue button" copy suggest a Sustainsys.Saml2 lineage -- plausibly the script's whitespace changed during adaptation and the hash constant didn't follow.)
Reproduction:
A. Minimal -- proves the defect with no IdP, no app, no browser
ilspycmd -t "Duende.IdentityServer.Internal.Saml.Sp.Bindings.Saml2PostBinding" Duende.IdentityServer.dll
printf '\ndocument.forms.samlPostBindingSubmit.submit();\n'
| openssl dgst -sha256 -binary | openssl base64 -A
Expected:
The computed hash equals the hash in the template's own CSP.
Actual:
Computed is IQKtK10TFgRroV/L1+sRadhw5yAEkHE3GlbgJgxr7K4=; the template declares H3SVZBYrbqBt3ncrT/nNmOb6nwCjC12cPQzh5jnW4Y0=.
B. Functional -- end-to-end SP-initiated sign-in
Environment: .NET 10, Duende.IdentityServer 8.0.5 or 8.0.6, any current Chrome/Edge/Firefox.
builder.Services.AddIdentityServer(...)
.AddConfigurationStore(...)
.AddOperationalStore(...)
.AddSamlDynamicProvider();
Expected behavior:
IdentityServer returns the HTTP-POST-binding relay page; its inline script auto-submits the form; the browser navigates to the IdP's SSO URL and the user sees the IdP login page.
Actual behavior:
the relay page is returned (HTTP 200, correct SAMLRequest, correct Destination and AssertionConsumerServiceURL), but the browser renders a blank page and never navigates. The form is never submitted. The console shows:
Nothing is logged server-side -- from IdentityServer's perspective the response was written successfully, so the failure is invisible outside the browser.
Workaround confirmed:
switching the provider to BindingType = "Redirect" completes sign-in normally, since Redirect binding carries the AuthnRequest in the query string and needs no inline script. This is only available when the IdP publishes an HTTP-Redirect SSO endpoint.
Scope note
Saml2PostBinding.Bind is generic over the SAML message type, so the same template -- and therefore the same failure -- applies to any POST-bound outbound message, including LogoutRequest for single logout, not only AuthnRequest. An IdP that publishes only POST bindings has no working path at all.
Users with no JavaScript
The page's fallback ("press the Continue button") does not help here: the script isn't absent, it's blocked, so the browser doesn't render the content and the user gets no button and no explanation.
All reactions