将Google idToken转换为本地OpenId token c#

8
我正在使用这个github项目https://github.com/openiddict/openiddict-core,非常好用。但是当用户使用外部身份提供者时,我卡住了,不知道应该采取什么步骤或如何实现它,例如,我将使用谷歌作为示例。
我有一个运行中的angular2应用程序和一个aspnet core webAPI。所有本地登录都很完美,我调用connect/token并返回一个accessToken。
现在我需要将谷歌作为外部身份提供者实现。我按照此处的所有步骤来实现谷歌登录按钮。当用户登录时,这会打开一个弹出窗口。这是我为我的谷歌按钮创建的代码。
// Angular hook that allows for interaction with elements inserted by the
// rendering of a view.
ngAfterViewInit() {
    // check if the google client id is in the pages meta tags
    if (document.querySelector("meta[name='google-signin-client_id']")) {
        // Converts the Google login button stub to an actual button.
        gapi.signin2.render(
            'google-login-button',
            {
                "onSuccess": this.onGoogleLoginSuccess,
                "scope": "profile",
                "theme": "dark"
            });
    }
}

onGoogleLoginSuccess(loggedInUser) {
    let idToken = loggedInUser.getAuthResponse().id_token;

    // here i can pass the idToken up to my server and validate it
}

现在我从谷歌获得了一个idToken。谷歌页面上找到的下一步(此处)说我需要验证谷歌accessToken,这是可行的,但是如何交换我从谷歌获得的accessToken,并创建本地accessToken以在我的应用程序中使用?

我只是想知道为什么需要使用Google客户端库来获取ID令牌,您是否考虑过像https://github.com/openiddict/openiddict-core/blob/dev/samples/Mvc.Server/Startup.cs#L119这样的Google身份验证? - adem caglin
只是因为我有一个客户端应用程序,我需要客户端的accessToken才能使我的应用程序正常工作,因为我将其存储在客户端的localStorage中。我确实尝试过使用那种方法,但是我不知道如何交换令牌以获取客户端令牌? - Gillardo
1个回答

17

编辑:此答案已更新为使用OpenIddict 3.x。


下一步在这里找到的Google页面上说我需要验证Google的访问令牌,我可以做到,但是我如何交换我从Google获得的访问令牌,并创建可以在我的应用程序上使用的本地访问令牌?
你试图实现的流程被称为"断言授权"。你可以阅读这个其他的SO帖子以获取更多信息
OpenIddict完全支持自定义授权,所以这是你可以很容易在你的令牌端点操作中实现的内容。
[HttpPost("~/connect/token"), Produces("application/json")]
public IActionResult Exchange()
{
    var request = HttpContext.GetOpenIddictServerRequest();
    if (request.GrantType is "urn:ietf:params:oauth:grant-type:google_identity_token")
    {
        // Reject the request if the "assertion" parameter is missing.
        if (string.IsNullOrEmpty(request.Assertion))
        {
            return Forbid(
                authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme,
                properties: new AuthenticationProperties(new Dictionary<string, string>
                {
                    [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidRequest,
                    [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] =
                        "The mandatory 'assertion' parameter was missing."
                }));
        }

        // Create a new ClaimsIdentity containing the claims that
        // will be used to create an id_token and/or an access token.
        var identity = new ClaimsIdentity(TokenValidationParameters.DefaultAuthenticationType);

        // Manually validate the identity token issued by Google, including the
        // issuer, the signature and the audience. Then, copy the claims you need
        // to the "identity" instance and call SetDestinations on each claim to
        // allow them to be persisted to either access or identity tokens (or both).
        //
        // Note: the identity MUST contain a "sub" claim containing the user ID.

        // Attach one or more destinations to each claim to allow OpenIddict
        // to persist them in either access and/or identity tokens.
        identity.SetDestinations(claim => claim.Type switch
        {
            "name" => new[]
            {
                Destinations.AccessToken,
                Destinations.IdentityToken
            },

            _ => new[] { Destinations.AccessToken },
        });

        return SignIn(new ClaimsPrincipal(identity), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
    }

    throw new InvalidOperationException("The specified grant type is not supported.");
}

请注意,您还需要在OpenIddict服务器选项中启用它。
services.AddOpenIddict()
    // ...

    .AddServer(options =>
    {
        // ...

        options.AllowCustomFlow("urn:ietf:params:oauth:grant-type:google_identity_token");
    });

发送令牌请求时,请确保使用正确的grant_type,并将您的id_token作为assertion参数发送,这样应该可以正常工作。以下是使用Postman的示例(适用于Facebook访问令牌,但工作方式完全相同):

enter image description here

说到这一点,当你实施令牌验证程序时,必须非常小心,因为这一步骤特别容易出错。验证一切都非常重要,包括受众(否则,你的服务器将容易受到混淆代理攻击)。

3
每次看到你的回答,我都感到惊讶!非常感谢你。 - Gillardo
这个回答还有效吗?在你的回答中第一个链接里,你写道:“新的OAuth2草案可能会在未来帮助改变这一点,但在主要服务开始实施之前,这可能需要一段时间。” 过去几个月有什么变化吗?我有一个Angular2应用程序,我想使用Google登录按钮。当然,我的密码流程与OpenIddict一起工作。 - Makla
3
这个回答仍然有效,OpenIddict仍然支持这种情况。 "OAuth2 token exchange" 草案尚未被标准化,但上个月发布了新版本,因此它肯定在正确的轨道上。尽管如此,大型提供商(例如Google或Facebook)开始实施它可能需要很多年时间(顺便说一句,Facebook仍在使用较旧的OAuth2草案,不符合最终的RFC标准)。 - Kévin Chalet
2
现在它是一个建议的标准,只需跟进:https://tools.ietf.org/html/rfc8693,我猜这不会改变答案的有效性,对吗? - Daniel Leiszen
@kévin-chalet我可以重复使用https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers中现有的验证逻辑而不对该逻辑进行任何修改吗? - Andrii
我已经尝试过了,但身份令牌为空。如何使其返回? - Andrii

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接