Table of Contents

Where this server is willing to send a browser

Registration decides whether a redirect target is honoured. It does not decide whether the target is a location at all, and the two are different questions once a client registration can come from an administration screen, a registration API or a SAML metadata document. ValidationOptions is where the second question lives. The code on this page is included from samples/Interaction.Checks, whose test sends an authorize request for a client registered with a refused redirect URI and checks the browser is not sent there.

The refused prefixes

// Added to the schemes this server will never send a browser to: these clients are all web applications.
options.Validation.InvalidRedirectUriPrefixes.Add("intent:");

The defaults, listed in the configuration reference, are javascript:, data:, vbscript:, file:, blob:, about: and view-source: — schemes a browser asked to navigate to either executes, or answers with a document carrying its own origin. The list is settable: a deployment can add to it, as above, or take one away with Remove. They are refused wherever this server hands a browser a registered address: redirect_uri at the authorize endpoint (to the error page, never to the client), post_logout_redirect_uri at the end-session endpoint, the front-channel logout iframes, the consent screen's link to Client.ClientUri, and the SAML assertion-consumer and single-logout endpoints. The client configuration validator refuses a registration naming one in its RedirectUris or PostLogoutRedirectUris too, judged by the same check (Duende's messages).

It is a deny list, not an allow list of http and https, because a native client legitimately registers com.example.app:/callback and an allow list would refuse it in order to catch a hostile registration. The comparison is made — ignoring case — against a normalised copy of the URI: tab, carriage return and line feed removed from anywhere, leading control characters and spaces trimmed. That is what a browser does before deciding what scheme it is looking at, so java	script:alert(1) is javascript: to a browser and would otherwise be nothing at all to a StartsWith. IsAllowedRedirectTarget is the check, for a page or endpoint of your own.

Client.LogoUri is deliberately outside this. The consent screen renders it as <img src>, which does not execute a javascript: URI, and a data: logo embedded in a registration is a legitimate thing the page's own CSP already permits. Applying the rule there would break a working feature to prevent nothing.

New in 0.39.0. The sharpest of those sites is SAML. The HTTP-POST binding answers a service provider by returning an HTML form that submits itself on load, with the registered reply address as its action; HTML-encoding stops markup escaping the attribute and leaves javascript: intact, and the page is written by an endpoint rather than served as a Razor page, so the interactive pages' Content-Security-Policy is not over it. The refusal has to happen before the first form is built and leave by the error page — the SSO endpoint's own refusal path posts its NoPassive answer to that same reply address, so a check one line later would have announced the refusal through the address it was refusing.