首页 > 解决方案 > ASP.NET Core 使用没有 Razor 页面的 IdentityUser 绑定外部登录

问题描述

我正在尝试将 GitHub 身份验证添加到我的 ASP.NET Core 应用程序,以便某些用户具有管理员角色。我想要实现的这个角色IdentityRole,用户应该存储为IdentityUsers。

我使用这篇文章中的信息作为起点。

我通过 GitHub 设置身份验证,如下所示:

services
    .AddAuthentication()
    .AddGitHub(options =>
    {
        options.ClientId = "xxx";
        options.ClientSecret = "xxx";
    }) 

我在控制器中的 Sigin 方法在这里:

public IActionResult SignIn(string provider, string returnUrl = null) =>
            Challenge(new AuthenticationProperties { RedirectUri = returnUrl ?? "/" }, provider);

我可以通过以下代码获得 GitHub 声明:

public IActionResult Index()
{
    var vm = new ProfileViewModel
    {
        Claims = User.Claims,
        Name = User.Identity.Name
    };
    return View(vm);
}

一切正常,一切都很好,但事实ClaimsPrincipal并非如此IdentityUser。而且我不明白如何IdentityUser使用此声明创建新的或标识以前存储的。以下是微软关于使用外部登录的一组文章,但他们使用了 Razor 页面并且没有提供太多信息它实际上是如何绑定到IdentityUser.

在不使用 Razor 页面的情况下如何做同样的事情?

标签: asp.net-coreasp.net-identity

解决方案


要将 github 用户映射到身份用户,您需要创建身份用户。

要创建没有 Razor 页面的 IdentityUser,您需要构建请求以将所需信息发送到ExternalLoginConfirmation.

[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{
    if (remoteError != null)
    {
        ErrorMessage = $"Error from external provider: {remoteError}";
        return RedirectToAction(nameof(Login));
    }
    var info = await _signInManager.GetExternalLoginInfoAsync();
    if (info == null)
    {
        return RedirectToAction(nameof(Login));
    }

    // Sign in the user with this external login provider if the user already has a login.
    var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
    if (result.Succeeded)
    {
        _logger.LogInformation("User logged in with {Name} provider.", info.LoginProvider);
        return RedirectToLocal(returnUrl);
    }
    if (result.IsLockedOut)
    {
        return RedirectToAction(nameof(Lockout));
    }
    else
    {
        // If the user does not have an account, then ask the user to create an account.
        ViewData["ReturnUrl"] = returnUrl;
        ViewData["LoginProvider"] = info.LoginProvider;
        var email = info.Principal.FindFirstValue(ClaimTypes.Email);
        return View("ExternalLogin", new ExternalLoginViewModel { Email = email });
    }
}

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ExternalLoginConfirmation(ExternalLoginViewModel model, string returnUrl = null)
{
    if (ModelState.IsValid)
    {
        // Get the information about the user from the external login provider
        var info = await _signInManager.GetExternalLoginInfoAsync();
        if (info == null)
        {
            throw new ApplicationException("Error loading external login information during confirmation.");
        }
        var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
        var result = await _userManager.CreateAsync(user);
        if (result.Succeeded)
        {
            result = await _userManager.AddLoginAsync(user, info);
            if (result.Succeeded)
            {
                await _signInManager.SignInAsync(user, isPersistent: false);
                _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
                return RedirectToLocal(returnUrl);
            }
        }
        AddErrors(result);
    }

    ViewData["ReturnUrl"] = returnUrl;
    return View(nameof(ExternalLogin), model);
}

推荐阅读