SSL 代理后面的 OWIN WS-Federation 重定向

OWIN WS-Federation Redirect behind SSL Proxy

我正在尝试编写我的第一个支持 ADFS 的 ASP.Net 网络应用程序。该应用程序在我的本地计算机上运行时运行良好,但是当我将它发布到 SSL 代理后面的 IIS 时,我的应用程序最终将经过身份验证的用户重定向到应用程序的 http 地址,而不是 https 地址。我该如何解决这个问题?

为了阐明进程在哪里中断,这里是正在发生的事情:

  1. 未经身份验证的用户浏览到应用程序的主页并被重定向到 Idp [好]
  2. 用户在 Idp 进行身份验证,令牌通过 https POST 发送回我的应用程序 [good]
  3. 我的应用分配了一个 cookie,并将用户重定向到 http 主页[坏]

在我的 Startup.Auth.cs 中,我只是使用样板代码:

    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                    
            });
    }
    }

我读过几篇关于 identity/auth 在 ssl 代理背后的类似问题的帖子, 的建议对我没有影响。我猜是因为重定向是由 ws-fed 中间件而不是 cookie 提供程序执行的。

知道了!

通读 OWIN/Katana 源代码后,我在 ws-fed 代码中找到了允许您操作 URL 的挂钩。就我而言,如果我检测到 SSL 代理的存在(通过检查请求 headers),我将重写 URL 以实现 https 而不是 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;
        }
    }

}