【问题标题】:Google OpenId Connect migration: getting the openid_id in ASP.NET appGoogle OpenId Connect 迁移:在 ASP.NET 应用程序中获取 openid_id
【发布时间】:2014-11-04 08:25:04
【问题描述】:

我浏览了大量 Google 文档和 SO Q/A,但没有运气。我想知道是否有人按照 Google 的建议成功使用了 OpenId 到 OpenId Connect 的迁移。

这是我们过去常做的:

IAuthenticationResponse response = _openid.GetResponse();
if (response != null) {
   //omitted for brevity       
} else {
   IAuthenticationRequest req = _openid.CreateRequest("https://www.google.com/accounts/o8/id");
   req.AddExtension(new ClaimsRequest
                    {
                        Country = DemandLevel.Request,
                        Email = DemandLevel.Request,
                        Gender = DemandLevel.Require,
                        PostalCode = DemandLevel.Require,
                        TimeZone = DemandLevel.Require
                    });
   req.RedirectToProvider();
}

这是使用几年前的 DotNetOpenAuth 版本完成的。由于 Google 已弃用 OpenID 身份验证,我们正试图转移到 OpenID Connect。这里的关键问题是:我能否以某种方式使用最新版本的 DotNetOpenAuth 库或通过任何其他方式获得 OpenId 标识符(以https://www.google.com/accounts/o8/id?id=xyz 的形式)?

我尝试了最新的 DotNetOpenAuth,我可以让它工作,但它给了我一个新的 ID(这是预期的)。我还尝试了使用此 URL 的 Javascript 方式(为了便于阅读而换行):

https://accounts.google.com/o/oauth2/auth?
    scope=openid%20profile%20email
    &openid.realm=http://localhost/palkkac/
    &client_id=//here is the client id I created in google developer console
    &redirect_uri=http://localhost/palkkac/someaspxpagehere
    &response_type=id_token%20token

我检查了(使用 Fiddler)我们当前使用旧的 DotNetOpenAuth 代码发送的领域值,它是 http://localhost/palkkac/。我在上面的网址中放置了相同的领域。重定向 url 以领域值开头,但并不完全相同。

当我重定向到解析 id_token 并解密它的简单页面(使用 https://www.googleapis.com/oauth2/v1/tokeninfo?id_token=zyx 端点)时,我得到了这个:

audience    "client id is here"
email   "mikkark@gmail.com"
expires_in  3597
issued_at   //some numbers here
issued_to   "client id is here"
issuer  "accounts.google.com"
user_id     "here is a sequence of numbers, my id in the OpenID Connect format that is"
verified_email  true

因此,这里没有您希望找到的 openid_id 字段的迹象,尽管消息的整个结构似乎与 Google 文档不同,例如,没有标题为 sub 的字段。我想知道我是否真的使用了错误的端点、参数或其他东西?

我一直在阅读的是迁移指南:https://developers.google.com/accounts/docs/OpenID。我跳过了第 2 步,因为它似乎是一个可选步骤。在第 3 步中讨论了 openid_id 字段,我希望首先将其作为概念验证。

我们在 Google 上注册了该应用程序以创建客户端 ID 等。现在 Google 开发控制台中还列出了许多允许的重定向 url 以及 javascript 来源。让我知道这些是否会破坏系统,我会在此处发布以供审核。

旁注:我们应该将我们的应用程序移动到一个严格的防火墙环境之后,我们需要打开端口才能在服务器端执行此操作。因此,访问 Google 的客户端 Javascript 解决方案与 HTTPS 相结合并将结果重定向到服务器将是首选(除非有其他反对这一点的问题)。

关于这个问题还有其他关于 SO 的资源,尽管所有这些资源似乎都在服务器端使用不同的库来完成这项工作,而且似乎没有人尝试过使用 Javascript:

  • 这里 (https://stackoverflow.com/questions/22842475/migrating-google-openid-to-openid-connect-openid-id-does-not-match) 我认为通过将领域设置为与旧 OpenId2.0 流程中的相同来解决问题。这在我的情况下似乎不起作用。
  • here 上,openid_id 字段也丢失了,但这里的问题更多是关于如何使用 DotNetOpenAuth 以外的库从 Google 请求 id_token。
  • here 中似乎有类似的问题让 Google 返回 openid_id 字段。

【问题讨论】:

  • 经过一个多月的搜索,我们也未能找到解决此问题的方法。
  • @spadelives 我看到您终于弄清楚了如何使用 Owin 和 ASP.NET MVC 发送领域参数(参考您的 SO 问题)。但是你仍然没有得到两个id,是这样吗?
  • 是的,我正在使用 cortex93 提供的解决方案获取两个 ID
  • 实际上,我得到了两个 id,但不幸的是,不是同时。 cortex 解决方案提供了 legacy id,但新 id 在 idToken 中不可用,因此无法在 SendAsync 方法中映射新旧 id。我在 ExternalLoginCallback 中有新的 id,但旧 id 在这里不可用。仍在寻找将旧 id 从 WebRequestHandler 传递到 ExternalLoginCallback 或从 SendAsync 方法中获取新 id 的方法。
  • 如果您替换 Debug.WriteLine(ex.ToString()); ,您可以将旧 id 作为会话变量传递给 ExternalLoginCallback是 cortex 的代码,带有 HttpContext.Current.Session.Add("LegacyGoogleId", iclaim.Value);然后从 ExternalLoginCallback 读取会话变量, var legacyGoogleId = Session["LegacyGoogleId"];

标签: asp.net google-oauth dotnetopenauth google-openid


【解决方案1】:

您可以使用 GoogleAuthentication owin 中间件。

app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions
{
    SignInAsAuthenticationType = signAs,
    AuthenticationType = "Google",
    ClientId = "xxx.apps.googleusercontent.com",
    ClientSecret = "xx",
    CallbackPath = PathString.FromUriComponent("/oauth2callback"),
    Provider = new GoogleOAuth2AuthenticationProvider
    {
        OnApplyRedirect = context =>
        {
            context.Response.Redirect(context.RedirectUri + "&openid.realm=https://mydomain.com/"); // DotNetOpenAuth by default add a trailing slash, it must be exactly the same as before
        }
    },
    BackchannelHttpHandler = new MyWebRequestHandler()
}

然后,添加一个名为 MyWebRequestHandler 的新类:

public class MyWebRequestHandler : WebRequestHandler
    {
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            var httpResponse = await base.SendAsync(request, cancellationToken);
            if (request.RequestUri == new Uri("https://www.googleapis.com/plus/v1/people/me")) return httpResponse;

            var configuration = await OpenIdConnectConfigurationRetriever.GetAsync("https://accounts.google.com/.well-known/openid-configuration", cancellationToken); // read the configuration to get the signing tokens (todo should be cached or hard coded)

            // google is unclear as the openid_id is not in the access_token but in the id_token
            // as the middleware dot not expose the id_token we need to parse it again
            var jwt = httpResponse.Content.ReadAsStringAsync().Result;
            JObject response = JObject.Parse(jwt);
            string idToken = response.Value<string>((object)"id_token"); 

            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();

            try
            {
                SecurityToken token;
                var claims = tokenHandler.ValidateToken(idToken, new TokenValidationParameters()
                {
                    ValidAudience = "xxx.apps.googleusercontent.com",
                    ValidIssuer = "accounts.google.com",
                    IssuerSigningTokens = configuration.SigningTokens
                }, out token);

                var claim = claims.FindFirst("openid_id");
                // claim.Value will contain the old openid identifier
                if (claim != null) Debug.WriteLine(claim.Value);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.ToString());
            }
            return httpResponse;
        }
    }

如果你像我一样发现这不是很简单,请通过支持这个问题https://katanaproject.codeplex.com/workitem/359来提供帮助

【讨论】:

  • 如果我没有完全弄错这个答案是 ASP.NET MVC 特定的吗?我们的应用程序是普通的 ASP.NET,尽管仍然必须应用相同的一般思想。
  • 好的,所以这段代码对 MVC 来说并不特别。我对 OWIN 不是很熟悉,所以我弄错了。我可以确认我确实可以使用 cortex 的代码获取旧 ID。现在我想知道如何让这个 Owin 代码在 ASP.NET(非 MVC)上和我们当前的身份验证机制一起“在旁边”工作。此外,我们的应用是 .net 4.0,因此使用 Owin 需要升级框架。
  • 我也设法让它在普通的 ASP.NET 中工作。我们使用了一个基于第三方 SAML 的身份验证库,这似乎有点搞砸了。在我删除它之后,我得到了这个工作。这实际上可以正常工作,因为我们在不同的服务器上有两个安装,其中一个使用 SAML,另一个将使用 OAuth 而没有 SAML。尽管如此,我还是想让他们一起工作,至少要了解为什么他们现在不工作了。
  • 嘿@cortex,看来我们现在可以关注this了?想要更新您的答案?
猜你喜欢
  • 2015-02-09
  • 1970-01-01
  • 1970-01-01
  • 2018-07-18
  • 2015-05-29
  • 1970-01-01
  • 2014-10-21
  • 2014-08-28
  • 2022-07-20
相关资源
最近更新 更多