Pingfederate opentoken模块CORS请求返回302而不是200

时间:2016-04-15 23:12:17

标签: javascript cors iis-7.5 pingfederate preflight

我们正在使用Ping Federate来保护两个Web服务器(IIS和使用IIS集成套件保护的两者,或来自Ping的opentoken模块)。一台服务器托管一个WEB API应用程序,另一台托管一个网页。 Web API应用程序已启用CORS。

该网页将带有json数据的Ajax发布请求发送到API服务器。这会导致浏览器启动预检选项请求。在API服务器上,Ping模块拦截此请求,该请求不包含凭据(规范说预检选项请求不应包含凭据)并返回302重定向,然后Web API代码可以在它返回200时处理它

我目前唯一的猜测是创建一个处理选项请求的自定义模块,并将其安装在opentoken模块之前。还有其他可能/更好的解决方案吗?

2 个答案:

答案 0 :(得分:2)

我没有等待PING,而是将IAuthorizationFilter放在他们的.NET Integration Kit / Agent之上。像这样的自定义过滤器的好处是您可以更好地控制API端点的安全性要求。

编写过滤器时,我使用了以下参考文献:

  • http://www.asp.net/web-api/overview/security/authentication-filters
  • https://msdn.microsoft.com/en-us/magazine/dn781361.aspx

    使用opentoken; 使用PF.SAML.Result; 使用系统; 使用System.Collections.Generic; 使用System.Configuration; 使用System.Linq; 使用System.Net.Http; 使用System.Net.Http.Headers; 使用System.Security.Claims; 使用System.Text; 使用System.Threading; 使用System.Threading.Tasks; 使用System.Web.Http.Filters;

    命名空间PF.SAML.Filters {     public class PingAuthenticationAttribute:IAuthenticationFilter     {         public bool AllowMultiple {get {return false; }}

        // http://www.asp.net/web-api/overview/security/authentication-filters
        // https://msdn.microsoft.com/en-us/magazine/dn781361.aspx
        public async Task AuthenticateAsync( HttpAuthenticationContext context, CancellationToken cancellationToken )
        {
            await Task.Run( () => {
                /*
                 * Look for credentials in the request.
                 * If there are no credentials, do nothing and return (no-op).
                 * If there are credentials but the filter does not recognize the authentication scheme, do nothing and return (no-op). Another filter in the pipeline might understand the scheme.
                 * If there are credentials that the filter understands, try to authenticate them.
                 * If the credentials are bad, return 401 by setting context.ErrorResult.
                 * If the credentials are valid, create an IPrincipal and set context.Principal.
                 */
                var opentoken = context.Request.Headers.GetCookies()
                    .SelectMany( c => c.Cookies )
                    .Where( c => c.Name == "opentoken" )
                    .FirstOrDefault();
    
                if( opentoken == null ) return;
    
                var userInfo = getOpenToken( opentoken.Value );
    
                if( userInfo == null ) {
                    context.ErrorResult = new AuthenticationFailureResult( "Invalid Token", context.Request );
                    return;
                }
    
                var claims = new List<Claim>();
                foreach( var item in userInfo ) {
                    foreach( var value in userInfo[item.Key] ) {
                        claims.Add( new Claim( item.Key, value ) );
                    }
                }
    
                var id = new ClaimsIdentity( claims, "opentoken" );
                var principle = new ClaimsPrincipal( new[] { id } );
    
                context.Principal = principle;
    
            } );
        }
    
        public async Task ChallengeAsync( HttpAuthenticationChallengeContext context, CancellationToken cancellationToken )
        {
            await Task.Run( () => {
                var challenge = new AuthenticationHeaderValue( "SAML" );
                context.Result = new AddChallengeOnUnauthorizedResult( challenge, context.Result );
            } );
        }
    
    
        private MultiStringDictionary getOpenToken( string token )
        {
            MultiStringDictionary attributes = null;
    
            Configuration.Agent agentConfig = (Configuration.Agent) ConfigurationManager.GetSection( "pfConfigurationGroup/agentConfiguration" ); 
            AgentConfiguration config = new AgentConfiguration
            {
                CookieDomain = agentConfig.CookieDomain,
                CookiePath = agentConfig.CookiePath,
                NotBeforeTolerance = agentConfig.NotBeforeTolerance,
                ObfuscatePassword = agentConfig.ObfuscatePassword,
                RenewUntilLifetime = agentConfig.RenewUntilLifetime,
                SecureCookie = agentConfig.SecureCookie,
                SessionCookie = agentConfig.SessionCookie,
                TokenLifetime = agentConfig.TokenLifetime,
                TokenName = agentConfig.TokenName,
                UseCookie = agentConfig.UseCookie,
                UseSunJCE = agentConfig.UseSunJCE,
                UseVerboseErrorMessages = agentConfig.UseVerboseErrorMessages
            };
    
            var str = ( config.ObfuscatePassword
                ? Encoding.UTF8.GetString( Obfuscator.Deobfuscate( agentConfig.Password ) )
                : Encoding.ASCII.GetString( Convert.FromBase64String( agentConfig.Password ) ) );
            config.SetPassword( str, Token.CipherSuite.AES_128_CBC );
    
            // TODO: Check for token expiration
    
            Agent agent = new Agent( config );
            attributes = agent.ReadTokenMultiStringDictionary( token );
    
            return attributes;
        }
    }
    

    }

答案 1 :(得分:0)

这是PING Federate IIS代理/集成套件实现的错误吗?

我同意OP。当前的W3C建议(https://www.w3.org/TR/cors/#preflight-request)在预检/ OPTIONS请求中明确说明排除用户凭据。因此,OPTIONS请求应允许匿名请求。让302指向身份提供者(IdP)并告诉我它没有遵循建议。

(这仅对PING服务器本身有帮助,集成套件仍然需要允许匿名OPTIONS请求。) Ping Federate在jetty配置文件中返回了更多配置设置:

<security-constraint>
    <web-resource-collection>
      <web-resource-name>Disable TRACE OPTIONS HEAD</web-resource-name>
      <url-pattern>/*</url-pattern>
      <http-method>TRACE</http-method>
      <http-method>OPTIONS</http-method> 
    </web-resource-collection>
    <auth-constraint/>
</security-constraint>