OpenID提供程序作为身份验证代理

时间:2013-03-23 15:21:52

标签: c# .net authentication openid dotnetopenauth

我正在创建一个.NET网站,它将为其用户提供特定的服务。让我们称之为service1.com。将来会有service2.com,他们将共享数据库。我还希望通过提供Google,Facebook,Twitter +自定义注册登录来进行用户友好的身份验证,以防用户没有Google等,以便他们注册。我将使用DotNetOpenAuth库。所以场景与SO类似。

现在Google存在问题:它提供different claimed identifier for different realm - 这意味着我无法将其用作用户ID,但必须在图片中添加电子邮件以解析用户(like SO is doing

除此之外,这意味着我必须支持Facebook Connect,Twitter以及每个服务站点上的所有其他登录选项。

所以我想出了OpenID代理的想法:

  • 我使用自己独立的数据库创建OpenID提供程序openid.service.com
  • service1.com和service2.com是其依赖方并共享自己的数据库
  • openid.service.com可以通过各种不同的方式验证用户:
    • openid.service.com的新帐户(basic sign up like SO
    • OpenID(Gmail,Yahoo,...)
    • Facebook Connect
    • 微博
    • ...
  • 登录选项和注册表单将显示在每个服务网站的iframe中(如SO注册)
  • 一旦openid.service.com通过其向依赖方回调提供声明的标识符的任何选项对用户进行身份验证

所以最终结果看起来像这样:enter image description here

优点:

  • 服务可以使用openid.service.com提供的声明标识符来识别登录的用户
  • 服务可以简单地添加到所需的域
  • 添加更多登录选项是微不足道的,因为openid.service.com处理它
  • Google声称标识符不会更改,因为openid.service.com处理身份验证

CONS:

  • 认证链更长(服务不直接与Google等进行身份验证)
  • ???

这是实现这样一个系统的一个不错的方法,还有其他缺点吗?

1 个答案:

答案 0 :(得分:2)

是的,这是解决此问题的合理方法。事实上,我认为 StackOverflow做同样的事情。不使用OpenID的人的自定义用户数据库也位于openid.service.com,所以实际上他们也会在这种情况下使用OpenID,尽管他们不知道。

需要注意的一点是,在您的示例中,openid.service.com将能够从Google和其他人声明原始的声明标识符,因为辅助RP将不接受您的断言中介,因为它对那些声称的ID没有权限。相反,您可以在openid.service.com和每个辅助RP之间执行无身份验证 OpenID。是的,OpenID实际上有一个流程,其中没有声明的标识符从OP传递到RP - 仅扩展。因此,您可以使用DotNetOpenAuth FetchRequestFetchResponse扩展来请求用户在您的辅助RP和您的中央服务之间的实际claim_id。并且辅助RP必须非常小心,只接受来自中央RP的断言。

这是辅助RP和中央RP之间代码的草图:

步骤1:RP从openid.service.com

请求用户身份验证
OpenIdRelyingParty rp;
var request = rp.CreateRequest("https://openid.service.com/");
request.IsExtensionOnly = true;
var fetchRequest = new FetchRequest();
fetchRequest.Attributes.AddRequired("http://openid.service.com/useridentity");
request.AddExtension(fetchRequest);
request.RedirectToProvider();

第2步:openid.service.com,作为OP,接收请求(OP是一个巨大的可复制性......这是一个非常不完整的样本。你应该参考OpenIdProvider samples that are available。 )

OpenIdProvider op;
IRequest request = op.GetRequest();
/// ...
IAnonymousRequest anonRequest = request as IAnonymousRequest;
if (anonRequest != null) {
    // The key part for this sample snippet is that you make sure the RP asking
    // is one of your own, since you're not following the typical OpenID flow.
    if (!IsWhiteListedRealm(hostRequest.Realm)) {
        anonRequest.IsApproved = false; // reject all RPs that aren't in the whitelist
    } else {
        // Perhaps here is where you'll start your double role as an RP
        // to authenticate the user if there isn't already a FormsAuth cookie.
    }
}

return op.PrepareResponse(request).AsActionResult()

步骤3:辅助RP接收来自您的代理OpenID服务的响应。

OpenIdRelyingParty rp;
var response = rp.GetResponse();
if (response != null) {
    if (response.Provider.Uri.Authority != "openid.service.com") {
        throw new Exception(); // only assertions from our own OP are trusted.
    }

    if (response.Status == AuthenticationStatus.ExtensionsOnly) {
        var fetchResponse = response.GetExtension<FetchResponse>();
        string claimedId = fetchResponse.GetAttributeValue("http://openid.service.com/useridentity");
        FormsAuthentication.RedirectFromLoginPage(claimedId);
    }
}

这些样本不完整。并且上述内容不适用于填充这些角色的网站的许多常规安全缓解措施。这只是一个草图,概述了您的应用程序在您的特定情况下应采取的一些额外步骤。