Asp.net web api 当OWIN中间件和客户端是不同的主机时,如何处理外部身份验证?

Asp.net web api 当OWIN中间件和客户端是不同的主机时,如何处理外部身份验证?,asp.net-web-api,owin,asp.net-web-api2,katana,Asp.net Web Api,Owin,Asp.net Web Api2,Katana,我正在尝试为移动和常规站点开发WEB API,其中包含一些常见功能,其中之一就是身份验证服务。我采用VS2013 SPA模板的服务器端部分,并尝试基于单个帐户实现安全性。主要要求是能够使用用户名/密码登录并通过facebook登录。当OWIN中间件和客户端在同一个项目中(如VS2013 SPA的示例)时,Tempate for VS2013可以正常工作,但如果没有,则应进行一些修订,我想与大家分享: 应启用作为OWIN中间件的WEB API上的CORS 在UI客户机(这是独立的项目/解决方案)中

我正在尝试为移动和常规站点开发WEB API,其中包含一些常见功能,其中之一就是身份验证服务。我采用VS2013 SPA模板的服务器端部分,并尝试基于单个帐户实现安全性。主要要求是能够使用用户名/密码登录并通过facebook登录。当OWIN中间件和客户端在同一个项目中(如VS2013 SPA的示例)时,Tempate for VS2013可以正常工作,但如果没有,则应进行一些修订,我想与大家分享:

  • 应启用作为OWIN中间件的WEB API上的CORS
  • 在UI客户机(这是独立的项目/解决方案)中,应为每个AJAX请求添加额外的标题“X-request-With”:“XMLHttpRequest”。我注意到,如果没有它,WEB API将返回XML而不是JSON负载,这会导致UI示例中的映射不正确(failJSON无法解析错误消息)
  • VS2013 SPA模板中的UI示例集用于同一主机,因此应为UI客户端中的每个请求添加一些带有基本url(WEB API url)的前缀
  • 因此,我通过实现自定义UserManager、UserStore、IIdentityValidator和IPasswordHasher成功地扩展了解决方案,它与我现有的MongoDB数据库和自定义逻辑一起工作。用户名/密码身份验证工作正常-我从WEB API接收承载令牌,并可以在后续调用中使用它。但我在外部身份验证方面遇到的主要问题是:在我使用facebook的情况下。实际上,所有外部登录流程都进行得很顺利,并预测到最后一步,即:

    授权端点检查外部登录cookie主体并找到关联的应用程序用户,然后将该用户作为承载身份验证类型登录到授权服务器中间件中。由于授权服务器看到请求参数response_type是token(在步骤1中),它将触发隐式流,隐式流将创建对token的访问并将其作为URL片段附加到重定向_uri(步骤1)。例如:

    找到HTTP/1.1 302 缓存控制:没有缓存 Pragma:没有缓存 过期:-1 位置:/#访问#令牌=asd2342SDIUKJdsfjk3234&令牌类型=承载和到期(in=1200&state=06hwltIjvnTn44hc 设置Cookie:.AspNet.External=;路径=/;expires=周四,1970年1月1日00:00:00 GMT 设置Cookie:.AspNet.Cookies=WJgdyZQs9N8TG20EWnik-j0;路径=/;HttpOnly 内容长度:0

    在这里您可以看到为WEB API主机生成的/#access_令牌,完整url如下所示:

    因此,它不会重定向到发起身份验证的客户端(例如[http://localhost:2108]),但在中间件主机本身上(在我的例子中)。显然,它给出了404未找到,因为WEB API只包含服务器端,无法解析这个#访问#令牌-UI客户端应该这样做

    所以我的问题是如何正确拦截重定向并将服务器主机替换为客户端主机?

    另外,我找到了构建这个#访问#令牌字符串的源代码。它位于OAuthAuthorizationServerHandler类的ApplyResponseGrantAsync方法中,该类实例是从OAuthAuthorizationServerMiddleware创建的。它具有内部可访问性。所以为了改变这种行为,我应该重写整个中间件,我认为这不是很好的方法

    另外,我看到的另一个选择是在WEB API上创建全局处理程序,该处理程序拦截所有传入的请求,如果它找到一个从#access_token开始的请求,那么它会从请求中获取refferer主机并重定向到它。但是很难看


    这比我想象的要简单得多。从客户端,您只需将returnUrl设置为所需的URL即可。服务器端没有任何更改

    例如,我在客户端上有一个函数,该函数调用获取外部登录:

    function externalLoginsUrl(returnUrl, generateState) {
        return baseurl + "/api/Account/ExternalLogins?returnUrl=" + (encodeURIComponent(returnUrl)) +
            "&generateState=" + (generateState ? "true" : "false");
    }
    
    self.getExternalLogins = function (returnUrl, generateState) {
        return $.ajax(externalLoginsUrl(returnUrl, generateState), {
            cache: false,
            headers: getAllHeaders()
        });
    };
    
    其中returnUrl:

    self.returnUrl = window.location.origin;
    
    仅此而已:)