OpenIdProvider.GetRequest()返回null

时间:2012-09-20 16:41:06

标签: c# asp.net .net openid dotnetopenauth

作为this问题的延续,我遇到了dotnetopenauth的问题。

我导航到我的依赖方代码并创建请求,但是当我的提供者收到请求时,OpenIdProvider.GetRequest()返回null。我查看了代码,据我所知,这是因为我的依赖方没有提供openid有效负载(request.form);但我无法弄清楚为什么会这样。

代码:

依赖方:

public ActionResult Authenticate(string RuserName = "")
{
UriBuilder returnToBuilder = new UriBuilder(Request.Url);
returnToBuilder.Path = "/OpenId/Authenticate";
returnToBuilder.Query = null;
returnToBuilder.Fragment = null;

Uri returnTo = returnToBuilder.Uri;
returnToBuilder.Path = "/";
Realm realm = returnToBuilder.Uri;

var response = openid.GetResponse();

if (response == null) {
    if (Request.QueryString["ReturnUrl"] != null && User.Identity.IsAuthenticated) {

    } else {

    string strIdentifier = "http://localhost:3314/User/Identity/" + RuserName;
    var request = openid.CreateRequest(
        strIdentifier,
        realm,
        returnTo);

    var fetchRequest = new FetchRequest();
    request.AddExtension(fetchRequest);
    request.RedirectToProvider();
    }
} else {
    switch (response.Status) {
        case AuthenticationStatus.Canceled:
            break;
        case AuthenticationStatus.Failed:
            break;
        case AuthenticationStatus.Authenticated:
            //log the user in
            break;
    }
}

return new EmptyResult();

}

提供者:

public ActionResult Index()
{
    IRequest request = OpenIdProvider.GetRequest();

    if (request != null) {
        if (request.IsResponseReady) {
            return OpenIdProvider.PrepareResponse(request).AsActionResult();
        }

        ProviderEndpoint.PendingRequest = (IHostProcessedRequest)request;
        return this.ProcessAuthRequest();
    } else {
        //user stumbled on openid endpoint - 404 maybe?
        return new EmptyResult();
    }
 }

public ActionResult ProcessAuthRequest()
    {
        if (ProviderEndpoint.PendingRequest == null) {
            //there is no pending request
            return new EmptyResult();
        }

        ActionResult response;
        if (this.AutoRespondIfPossible(out response)) {
            return response;
        }

        if (ProviderEndpoint.PendingRequest.Immediate) {
            return this.SendAssertion();
        }

        return new EmptyResult();
    }

日志:

RP:1)http://pastebin.com/Pnih3ND7 2)http://pastebin.com/eBzGun9y

提供者:http://pastebin.com/YAUTBzHk

有趣的是,RP日志表明localhost是不受信任的......但是我将它添加到我的web.config中列入白名单的主机上,并且它昨天“正常工作”......

编辑:好的,这很奇怪。昨天我试图找出问题所在,试图通过DNOA来源。我启用了log4net,它创建了日志文件并将其留空。今天我再次设置log4net - 它记录正常,但我有一个没有意义的错误(见上文)。我也无法进入DNOA源。我删除并重新添加了对dotnetopenauth.dll的引用,然后我的“原始错误”与列入白名单的主机消失了,我能够进入源代码,但日志文件再次出现空白。我仍然遇到request.form没有被填充的问题......

EDIT2 :我的控制器都被命名为“OpenIdController”(RP和EP都有)。我的RP在localhost:1903上运行,我的端点在localhost:3314上运行。


EDIT3 :在我做出更改后,您建议事情开始有效。 RP执行发现很好,但实际发出请求时我遇到了问题。

IRequest i_request = OpenIdProvider.GetRequest();行正常,但是当我尝试投射它时:IAuthenticationRequest iR = (IAuthenticationRequest)i_request;它会给我以下错误:

 System.InvalidCastException was unhandled by user code
 Message=Unable to cast object of type     'DotNetOpenAuth.OpenId.Provider.AutoResponsiveRequest' to type 'DotNetOpenAuth.OpenId.Provider.IAuthenticationRequest'.
 Source=Portal
 StackTrace:
   at Portal.Controllers.OpenIdController.Index() in Controllers\OpendIdController.cs:line 35
   at lambda_method(Closure , ControllerBase , Object[] )
   at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass42.<BeginInvokeSynchronousActionMethod>b__41()
   at System.Web.Mvc.Async.AsyncResultWrapper.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.End()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass37.<>c__DisplayClass39.<BeginInvokeActionMethodWithFilters>b__33()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass4f.<InvokeActionMethodFilterAsynchronously>b__49()

这个代码在我发现相关的两个样本之间有点混乱。我想建立一个SSO类型的环境,因此我使用的大多数代码来自\DotNetOpenAuth-4.1.0.12182\Samples\OpenIdWebRingSsoProvider\Code\Util.cs(ProcessAuthenticationChallenge函数)。但是,由于该函数需要IAuthenticationRequest但OpenIdProvider.GetRequest返回AutoResponsiveRequest,我认为我可以将其强制转换为使用IAuthenticationRequest类的属性和方法。显然我不对。

我现在还不太确定如何处理事情。我应该使用OpenIdProviderMVC示例中的示例代码吗?关键是登录工作就像单点登录一样,用户实际上从未被提示输入OpenId。我也只会有一个端点(虽然我会有多个RP)。

以下是最新RP日志的链接:http://pastebin.com/enpwYqq3


EDIT4 :我做了你的建议,取得了一些进展。据我所知,我的EP收到了响应并处理它,但是当它重定向回到领域时它会出错。

012-10-10 13:55:01,171 (GMT-4) [25] ERROR DotNetOpenAuth.Messaging - Protocol error: An HTTP request to the realm URL (http://localhost:1903/) resulted in a redirect, which is not allowed during relying party discovery.

Realm相比,ReturnTo的功能究竟是什么?使用示例代码,Realm最终为http://localhost:1903/,ReturnTo最终为http://localhost:1903/OpenId/Authenticate,这看起来很好。为什么EP需要向领域提出请求?我原以为它应该只是在完成处理后将断言发送到returnTo。如果我手动将领域设置为http://localhost:1903/OpenId/Authenticate,则relyingParty.GetResponse()将返回null。

我确实将我的应用程序设置为在有人访问基本URL(http://localhost:1903)时重定向 - 我应该在那里运行什么代码来拦截DNOA EP请求?

新记录:

RP:http://pastebin.com/L9K5Yft4

EP:http://pastebin.com/kBPWiUxp

我还在问题开头更新了代码,以便更好地反映我所做的更改。


EDIT5 :领域是否必须是应用程序的实际基本URL?那就是(http://localhost:1903)?鉴于现有的体系结构,很难删除重定向 - 我尝试将域设置为基础OpenId控制器(http://localhost:1903/OpenId)并手动测试确实生成了XRDS文档。但是,应用程序似乎冻结,EP日志显示以下错误:

2012-10-10 15:17:46,000 (GMT-4) [24] ERROR DotNetOpenAuth.OpenId - Attribute Exchange extension did not provide any aliases in the if_available or required lists.

2 个答案:

答案 0 :(得分:2)

您的RP的代码非常奇怪。虽然return_to和realm都具有相同的Uri权限是正常的(实际上是必需的),但是作为OpenIdRelyingParty.CreateRequest的第一个参数传入的用户提供的标识符具有相同的主机和您的依赖方端口非常奇数。通常,您传入的标识符是由提供程序托管的URL。现在,我不知道端口3314是你的RP还是OP,但无论如何,RP代码中的其中一个端口号看起来不对。

其次,对用户标识符的发现失败,并带有空引用异常(根据RP日志的v2)。这样可以防止登录请求到达您的提供商。您的提供商调用但存在不存在的OpenID请求这一事实表明http://localhost:3314/OpenId/实际上是您的OP端点(您的OpenID提供商的操作方法的URL读取Op​​enID请求)。那是不合适的。您应该传递给OpenIdRelyingParty.CreateRequest方法的第一个参数的URL应该是用户的OpenID URL - 而不是OP端点。查看OpenIdProviderMvc示例的用户控制器,以获取有关如何设置用户OpenID URL的示例。然后使用那个 URL作为CreateRequest的第一个arg,我认为你会很好。

第三,一旦您的提供商收到非空请求,您就不能总是将其转发给IAuthenticationRequest。并非所有OpenID消息都是认证消息。有些是底层OpenID协议的一部分。如果查看OpenIdProviderMvc示例的OpenID控制器,您应该注意到有条件转换来处理不同的消息类型。您应该在控制器中进行类似的消息处理。

由于您要使用SSO方案,因此控制器的显着差异可能是:

  1. 您的控制器永远不会通过重定向到登录页面来响应,而是“神奇地”确定用户是谁。
  2. 您的控制器应根据您的SSO Web环中包含的RP的白名单检查IAuthenticationRequest.Realm属性,并仅在Realm符合条件时提供肯定断言。这可以缓解攻击,一旦您的服务器设置完毕,任何人都可以设置一个网站,静静地使用您的OpenID提供商来识别随机互联网网站的用户,如果他们属于您的组织,这将侵犯他们的隐私。
  3. 第四,OP发送到RP的“领域”URL的HTTP请求是OpenID调用“RP发现”的过程的一部分。它可以缓解“开放重定向器”攻击。您应该将RP的基本URL调整为 not 重定向,而是在RP发现请求进入时返回XRDS文档。您仍然可以重定向到正常的浏览器案例。您可以在OpenIdRelyingPartyMvc示例的HomeController中看到如何执行此操作的示例。

答案 1 :(得分:1)

从依赖方日志中可以看出:

ERROR DotNetOpenAuth.Messaging - Protocol error: The URL 'http://localhost:3314/OpenId/' is rated unsafe and cannot be requested this way.

您的提供商托管在localhost上,该生产服务器上的OpenID不是安全的OpenID。因此,默认情况下,localhost被禁用。您可以通过将localhost添加到您的web.config文件(在顶部使用相应的configSections)将localhost添加到白名单中来允许它进行本地测试:

<dotNetOpenAuth>
    <messaging>
        <untrustedWebRequest>
            <whitelistHosts>
                <add name="localhost" />
            </whitelistHosts>
        </untrustedWebRequest>
    </messaging>
</dotNetOpenAuth>