1

我在 .NET5 MVC 中有一个项目,它使用AspNet.Security.OAuth.Twitch实现了 Twitch 身份验证。我配置了所有内容,并且运行良好,但我想添加选项以将其他帐户与其他提供商(如 Twitter)链接。我尝试使用Microsoft.AspNetCore.Authentication.Twitter添加 Twitter 身份验证。还配置了一切。

但是当我使用 Twitter 登录时,我当前的会话丢失了,所有来自 Twitch 的声明都被删除并被 Twitter 声明所取代。我想这是预期的行为,但我不知道我是否可以保留这些声明或仅恢复 Twitter 声明而不存储在用户身份中(例如存储在数据库中)。我的主要目标是使用 Twitch 身份验证作为登录应用程序的唯一方式,但必须选择链接来自其他提供商的帐户。

我在我的Startup.cs两个提供商中都添加了(最终可能会在未来某个时候添加其他提供商)

public void ConfigureServices(IServiceCollection services)
{
    // more stuff ...
    
    services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    })
    .AddTwitch(options =>
    {
        options.ClientId = Configuration["Twitch-ClientId"];
        options.ClientSecret = Configuration["Twitch-ClientSecret"];
    })
    .AddTwitter(options =>
    {
        options.ConsumerKey = Configuration["Twitter-ConsumerKey"];
        options.ConsumerSecret = Configuration["Twitter-ConsumerSecret"];
    });
}

在我AuthController.cs的挑战中我有相应的方法。

// Default Login using Twitch
[HttpGet("~/signin")]
public IActionResult Login() => RedirectToAction("Login", "Auth", new { provider = "Twitch" });

[HttpPost("~/signin")]
public IActionResult Login([FromForm] string provider)
{
     string redirect_uri = Url.Action("Index", "Home");

     return Challenge(new AuthenticationProperties() { RedirectUri = redirect_uri }, provider);
}

我不知道是否Challenge可以修改或配置为允许这种行为。我在AuthenticationProperties类中看不到任何可以使用的属性。我最初尝试为其他提供者创建另一个控制器/操作,但结果是相同的。

任何帮助将不胜感激。

4

1 回答 1

1

只要用户的会话 cookie 有效,您就可以使用多个身份验证方案对其进行身份验证并随时访问这些声明。

但是当我使用 Twitter 登录时,我当前的会话丢失了,所有来自 Twitch 的声明都被删除并被 Twitter 声明所取代。

发生这种情况是因为您尝试使用Cookie方案来保存 Twitter 和 Twitch 的会话 cookie。当您使用其中一个登录时,它会覆盖另一个。

要解决此问题,您需要为每个单独的登录选项添加单独的 cookie。

services.AddAuthentication()
    .AddCookie("GoogleSession")
    .AddCookie("GithubSession")
    .AddGoogle(
        options => {
            // set the app credentials
            Configuration.GetSection("Google").Bind(options);
            // save session to this cookie
            options.SignInScheme = "GoogleSession";
        })
    .AddGitHub(
        options => {
            // set the app credentials
            Configuration.GetSection("Github").Bind(options);
            // save session to this cookie
            options.SignInScheme = "GithubSession";
        });

然后发出一个挑战来强制用户登录:

[AllowAnonymous]
[HttpGet("login-google")]
public ActionResult LoginGoogle()
{
    return Challenge(
        new AuthenticationProperties
        {
            RedirectUri = Url.Action("WhoAmI"),
        }, GoogleDefaults.AuthenticationScheme
    );
}

[AllowAnonymous]
[HttpGet("login-github")]
public ActionResult LoginGithub()
{
    return Challenge(
        new AuthenticationProperties
        {
            RedirectUri = Url.Action("WhoAmI"),
        }, GitHubAuthenticationDefaults.AuthenticationScheme
    );
}

然后,您可以随时对用户进行身份验证以读取和解析 cookie 以访问声明:

[AllowAnonymous]
[HttpGet("me")]
public async Task<ActionResult> WhoAmI()
{
    var googleResult = await HttpContext.AuthenticateAsync(GoogleDefaults.AuthenticationScheme);
    if (googleResult.Succeeded)
    {
        var googlePrincipal = googleResult.Principal;
        // ... use google claims
        User.AddIdentity((ClaimsIdentity)googlePrincipal.Identity);
    }

    var githubResult = await HttpContext.AuthenticateAsync(GitHubAuthenticationDefaults.AuthenticationScheme);
    if (githubResult.Succeeded)
    {
        var githubPrincipal = githubResult.Principal;
        // ... use google claims
        User.AddIdentity((ClaimsIdentity)githubPrincipal.Identity);
    }

    return Ok(
        User.Identities.Select(
                id => new
                {
                    id.AuthenticationType, 
                    Claims = id.Claims.Select(c => new { c.Type, c.Value })
                }
            )
            .ToList()
    );

现在,当我访问 时/me,我得到了所有会话中所有声明的列表:

[
  {
    "authenticationType": null,
    "claims": []
  },
  {
    "authenticationType": "Google",
    "claims": [
      {
        "type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
        "value": "123131231231312123123123"
      },
      {
        "type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
        "value": "My Fullname"
      },
      {
        "type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname",
        "value": "MyName"
      },
      {
        "type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname",
        "value": "MyLastname"
      },
      {
        "type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
        "value": "my@gmail.com"
      }
    ]
  },
  {
    "authenticationType": "GitHub",
    "claims": [
      {
        "type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
        "value": "1313123123"
      },
      {
        "type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
        "value": "abdusco"
      },
      {
        "type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
        "value": "my@email.com"
      },
      {
        "type": "urn:github:name",
        "value": "my name"
      },
      {
        "type": "urn:github:url",
        "value": "https://api.github.com/users/abdusco"
      }
    ]
  }
]

使用多种身份验证方案手动对用户进行身份验证有点繁琐。我们可以让 ASP.NET Core 为我们做这件事。

定义一个接受多个身份验证方案的授权策略。

services.AddAuthorization(
    options => options.DefaultPolicy = new AuthorizationPolicyBuilder(
            GoogleDefaults.AuthenticationScheme,
            GitHubAuthenticationDefaults.AuthenticationScheme
        ).RequireAuthenticatedUser()
        .Build()
);

[Authorize]现在,当您使用(并指定策略名称,如果需要)装饰操作时,HttpContext.User将包含所有会话的身份和声明。

[Authorize]
[HttpGet("me")]
public async Task<ActionResult> WhoAmI()
{
    return Ok(
        // user has claims from all sessions
        User.Identities.Select(
                id => new
                {
                    id.AuthenticationType,
                    Claims = id.Claims.Select(c => new { c.Type, c.Value })
                }
            )
            .ToList()
    );
}

这与以前的输出相同,但没有样板。

于 2021-07-29T18:48:04.637 回答