首页 > 解决方案 > 基于 JWT 的外部 oauth 登录,没有 cookie 和重定向?

问题描述

我只是找不到合适的术语来搜索以找到我需要的答案,所以我只想在这里尝试解释一下。

我正在尝试让基于 JWT 的登录名适用于没有实际 cookie 或浏览器的应用程序(例如电子)。当我想添加诸如不和谐、谷歌等外部登录时,就会出现问题...

我不确定我正在尝试的方法是否根本不是这样做的方法,或者我是否遗漏了一些东西。

作为我正在尝试做的一个例子,我有一个基于 cookie 的身份验证的工作解决方案(全部基于 asp.net Identity 和标准 .net 提供的功能)。

启动.cs

services.ConfigureApplicationCookie(options =>
  {
    options.ExpireTimeSpan = TimeSpan.FromDays(7);
    options.SlidingExpiration = true;
    options.LoginPath = "/login";
    options.AccessDeniedPath = "/access-denied";
    options.LogoutPath = "/logout";
  });

  services.AddAuthentication()
    .AddDiscord(options =>
    {
      options.ClientId = "xxx";
      options.ClientSecret = "xxx";
      options.CallbackPath = "/api/id/external/discord/callback";
      options.Scope.Add("identify");
      options.Scope.Add("email");
    });

身份控制器.cs

public async Task<LoginResult> Login([FromBody] LoginModel model)
{
  if (ModelState.IsValid)
  {
    var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, true, false);
    if (result.Succeeded)
    {
      return new LoginResult { Success = true };
    }
  }
  return new LoginResult();
}

[Route("external/login-discord")]
public IActionResult LoginExternal(string returnUrl = null)
{
  var url = "/api/id/external/discord";
  if (!string.IsNullOrEmpty(returnUrl))
  {
    url += $"?returnUrl={WebUtility.UrlEncode(returnUrl)}";
  }
  var properties = _signInManager.ConfigureExternalAuthenticationProperties("Discord", url);

  return Challenge(properties, "Discord");
}

[HttpGet]
[Route("external/discord")]
public async Task<IActionResult> LoginWithDiscord(string returnUrl = null)
{
  var info = await _signInManager.GetExternalLoginInfoAsync();
  if (info == null)
  {
    var loginUrl = "/login";
    if (returnUrl != null && returnUrl != loginUrl)
    {
      loginUrl += $"?returnUrl={WebUtility.UrlEncode(returnUrl)}";
    }
    return Redirect(loginUrl);
  }
  var discordClaims = info.Principal.Claims.Where(c => c.Type.Contains("discord"));


  var user = await UserManager.FindByLoginAsync(info.LoginProvider, info.ProviderKey);
  var create = user == null;

  if (create)
  {
    user = new User
    {
      EmailConfirmed = true,
      LockoutEnabled = false
    };
  }

  user.DisplayName = info.Principal.FindFirst(ClaimTypes.Name).Value + "#" + info.Principal.FindFirst("urn:discord:user:discriminator").Value;
  user.UserName = info.Principal.FindFirst(ClaimTypes.Email).Value;
  user.Email = info.Principal.FindFirst(ClaimTypes.Email).Value;

  if (create)
  {
    var createResult = await UserManager.CreateAsync(user);
    if (!createResult.Succeeded)
    {
      createResult.Errors.AddToModelState(ModelState);
      return new BadRequestObjectResult(ModelState);
    }


    createResult = await UserManager.AddLoginAsync(user, info);
    if (!createResult.Succeeded)
    {
      createResult.Errors.AddToModelState(ModelState);
      return new BadRequestObjectResult(ModelState);
    }
  }
  else
  {
    var result = await UserManager.UpdateAsync(user);
    if (!result.Succeeded)
    {
      result.Errors.AddToModelState(ModelState);
      return new BadRequestObjectResult(ModelState);
    }
  }


  await UserManager.RemoveClaimsAsync(user, discordClaims);
  await UserManager.AddClaimsAsync(user, discordClaims);

  await _signInManager.UpdateExternalAuthenticationTokensAsync(info);
  await _signInManager.SignInAsync(user, true);
  return Redirect(returnUrl ?? "/");
}

这让我可以处理服务器上的所有身份验证内容并使用重定向。

现在我可以轻松地使用 JWT 进行基于密码的登录,因为我只需要在一个请求中返回令牌。但是我如何让外部登录以相同的方式工作?我不想仅将外部身份提供者用于授权,而是将其用于身份验证并创建一个本地用户,就像我的 cookie 示例中一样。

当我在我的应用程序中并单击“使用 X 登录”时,我将打开一个新窗口,该窗口从外部提供商打开登录,但是我如何从外部提供商所做的重定向转到将 JWT 发送给我的客户?

标签: authentication.net-coreoauthjwtasp.net-identity

解决方案


推荐阅读