Search code examples
asp.netowinadfsws-federation

OWIN WS-Federation Redirect behind SSL Proxy


I am attempting to author my first ADFS-enabled ASP.Net web app. The app works fine when hosted on my local machine, but when I publish it to IIS, which is behind an SSL proxy, my app ends up redirecting the authenticated user to the http address of the application, instead of the https address. How do I address this?

To clarify where the process breaks down, here is what is happening:

  1. Unauthenticated user browses to home page of app and is redirected to Idp [good]
  2. User authenticates at Idp, and the token is sent via https POST back to my app [good]
  3. My app assigns a cookie, and redirects the user to the http home page [bad]

In my Startup.Auth.cs, I am just using the boilerplate code:

    public partial class Startup
    {
    private static string realm = ConfigurationManager.AppSettings["ida:Wtrealm"];
    private static string adfsMetadata = ConfigurationManager.AppSettings["ida:ADFSMetadata"];

    public void ConfigureAuth(IAppBuilder app)
    {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions());

        app.UseWsFederationAuthentication(
            new WsFederationAuthenticationOptions
            {
                Wtrealm = realm,
                MetadataAddress = adfsMetadata                    
            });
    }
    }

I've read several posts about similar issues with identity/auth behind an ssl proxy and the advice to assign a custom CookieAuthenticationProvider had no affect for me. I'm guessing because the redirect is being performed by the ws-fed middleware rather than the cookie provider.


Solution

  • Got it!

    After reading through the OWIN/Katana source code, I found the hooks in the ws-fed code that allow you to manipulate the URLs. In my case, if I detect the presence of an SSL proxy (by checking the request headers), I rewrite the URLs to implement https instead of http.

    public partial class Startup
    {
        private static string realm = ConfigurationManager.AppSettings["ida:Wtrealm"];
        private static string adfsMetadata = ConfigurationManager.AppSettings["ida:ADFSMetadata"];
    
        public void ConfigureAuth(IAppBuilder app)
        {
            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
    
            app.UseCookieAuthentication(new CookieAuthenticationOptions());
    
            app.UseWsFederationAuthentication(
                new WsFederationAuthenticationOptions
                {
                    Wtrealm = realm,
                    MetadataAddress = adfsMetadata,
                    Notifications = new WsFederationAuthenticationNotifications
                    {
                        SecurityTokenValidated = (notification) =>
                        {
                            notification.AuthenticationTicket.Properties.RedirectUri = ApplySchemeFromSSLProxy(notification.Request, notification.AuthenticationTicket.Properties.RedirectUri);
                            return Task.FromResult(0);
                        },
                        RedirectToIdentityProvider = (notification) =>
                        {
                            if (notification.ProtocolMessage.IsSignOutMessage)
                            {
                                notification.ProtocolMessage.Wreply = ApplySchemeFromSSLProxy(notification.Request, notification.ProtocolMessage.Wreply);
                            }
                            return Task.FromResult(0);
                        }                       
                    }
                });
        }
    
        /// <summary>
        /// If there is evidence of an SSL proxy in use, convert the uri to https.
        /// </summary>
        /// <param name="request"></param>
        /// <param name="uri"></param>
        /// <returns>Converted URI</returns>
        private string ApplySchemeFromSSLProxy(IOwinRequest request, string uri)
        {            
            if (
                Uri.IsWellFormedUriString(uri, UriKind.Absolute) && 
                request.Headers.Any(h => 
                    h.Key.ToLower().StartsWith("x-forwarded-proto") && 
                    h.Value.Contains(Uri.UriSchemeHttps)
                    )
                )
            {
                var uriBuilder = new UriBuilder(uri)
                {
                    Scheme = Uri.UriSchemeHttps,
                    Port = -1 // default port for scheme
                };
                return uriBuilder.ToString();
            }
            else
            {
                return uri;
            }
        }
    
    }