.NET Core Google OAuth(和其他提供商)——我需要存储访问令牌吗?

.NET Core Google OAuth (and other providers) - do I need to store the access tokens?

我不清楚是否需要在此社交登录实现中存储和/或使用访问令牌。

我可以通过如下配置 Startup.cs 来读取访问令牌。我知道在许多 OAuth 流程中,访问令牌会在使用前进行验证。

但是,使用 .NET Core 的身份验证框架,我不清楚已经为我完成了多少这项工作。我查看了 Microsoft.AspNetCore.Authentication.Google 来源,但这并没有使它更清楚。

在下面的 LoginCallback 操作中,我可以使用以下方法读取用户的 ID:

claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier));

据我所知,这就是我所需要的。我可以将该标识符存储在数据库中,如果用户下次通过身份验证提供商(Google、Facebook 等)登录时 ID 匹配,我可以再次登录。

不过,前提是框架在我读取该 ID 之前已经验证了访问令牌,这是我不清楚的。框架是否在 AuthenticateResult 上设置值之前验证访问令牌,以便我可以相信它没有被篡改?

如果是这样,我认为没有必要存储访问令牌。

Startup.cs

        services.AddAuthentication(o =>
        {
            o.DefaultScheme = "Application";
            o.DefaultSignInScheme = "External";
        })
        .AddCookie("Application")
        .AddCookie("External")
        .AddGoogle(o =>
        {
            o.ClientId = "xxxxxxxxxxx";
            o.ClientSecret = "xxxxxxxxxxx";


            // Can access tokens by configuring as follows

            o.SaveTokens = true;

            o.Events.OnCreatingTicket = ctx =>
            {
                List<AuthenticationToken> tokens = ctx.Properties.GetTokens().ToList();

                tokens.Add(new AuthenticationToken()
                {
                    Name = "TicketCreated",
                    Value = DateTime.UtcNow.ToString()
                });

                ctx.Properties.StoreTokens(tokens);

                return Task.CompletedTask;
            };
        }) // Other providers ...

回调处理

    [Route("oauth/google-challenge")]
    public IActionResult GoogleLoginChallenge(string returnUrl)
    {
        return new ChallengeResult(
            GoogleDefaults.AuthenticationScheme,
            new AuthenticationProperties
            {
                RedirectUri = Url.Action(nameof(LoginCallback), new { returnUrl, oauthProvider = OAuthProvider.Google })
            });
    }

    [Route("oauth/facebook-challenge")]
    public IActionResult FacebookLoginChallenge(string returnUrl)
    {
        return new ChallengeResult(
            FacebookDefaults.AuthenticationScheme,
            new AuthenticationProperties
            {
                RedirectUri = Url.Action(nameof(LoginCallback), new { returnUrl, oauthProvider = OAuthProvider.Facebook })
            });
    }

    [Route("oauth/microsoft-challenge")]
    public IActionResult MicrosoftLoginChallenge(string returnUrl)
    {
        return new ChallengeResult(
            MicrosoftAccountDefaults.AuthenticationScheme,
            new AuthenticationProperties
            {
                RedirectUri = Url.Action(nameof(LoginCallback), new { returnUrl, oauthProvider = OAuthProvider.MicrosoftAccount })
            });
    }

    [Route("oauth/login-callback")]
    public async Task<IActionResult> LoginCallback(string returnUrl, OAuthProvider oAuthProvider)
    {
        var authenticateResult = await HttpContext.AuthenticateAsync("External").ConfigureAwait(false);

        if (!authenticateResult.Succeeded)
        {
            return BadRequest();
        }

        var claimsIdentity = new ClaimsIdentity("Application");

        claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier));
        claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.Email));
        claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.Name));


        foreach (KeyValuePair<string,string> item in authenticateResult.Properties.Items)
        {
            // Read access tokens here
        }

        foreach (KeyValuePair<string,string> item in authenticateResult.Ticket.Properties.Items)
        {
            // or here
        }

        string accessToken = await HttpContext.GetTokenAsync("access_token");

        return Redirect(returnUrl);
    }

由于我不清楚框架采取了哪些操作来验证用户信息是否来自身份验证提供程序,因此我设置了来自 .NET 核心应用程序的请求以通过 Fiddler。

"environmentVariables": {
    ...
    "ALL_PROXY": "http://127.0.0.1:8888"
},

在 Google OAuth 提供程序的情况下,在浏览器重定向回 signin-google URI 之后,我能够看到框架调用回:

www.googleapis.com/oauth2/v4/token,发送客户端 ID 密码和一些其他信息以获取访问和 ID 令牌。

然后调用:

www.googleapis.com/oauth2/v4/userinfo 传递访问和 ID 令牌。

{
  "id": "xxxx",
  "email": "xxxx@xxxx.com",
  "verified_email": true,
  "name": "Example",
  "given_name": "Example",
  "family_name": "Example",
  "picture": "https://lh3.googleusercontent.com/a-/xxxxxx",
  "locale": "en",
  "hd": "xxxx.com"
}

很明显,令牌的生命周期很短,因此保留它们毫无意义。

通过在服务器之间使用来获取和验证访问令牌看起来也是安全的。