首页 > 解决方案 > 如何使用 asp.net core 2.2 向经过外部身份验证且未在我的 WebApp 中注册的用户授予访问权限?

问题描述

概括

这是我第一次尝试使用 OAuth2 和外部登录机制。

我正在创建一个 WebApp,它将通过用户友好的 UI 公开 API 功能。

为了进行 API 调用,我需要从 QBO 接收一个访问令牌,该令牌授予对资源的访问权限。

所以,我的 WebApp 有一个外部登录选项,我用它来对 QBO 进行身份验证,然后授权我的应用程序。

一切正常,直到...

加载外部登录信息时出错。

服务配置

基于 GitHub 身份验证教程,我想出了这个。

        services.AddAuthentication(o => {
            o.DefaultAuthenticateScheme = IdentityConstants.ExternalScheme;
            o.DefaultSignInScheme = IdentityConstants.ExternalScheme;
            o.DefaultChallengeScheme = IdentityConstants.ExternalScheme;
        })
            .AddOAuth("qbo", "qbo", o => {
                o.CallbackPath = new PathString("/signin-qbo");
                o.ClientId = Configuration["ecm.qbo.client-id"];
                o.ClientSecret = Configuration["ecm.qbo.client-secret"];
                o.SaveTokens = true;
                o.Scope.Add("openid");
                o.Scope.Add("profile");
                o.Scope.Add("email");
                o.Scope.Add("com.intuit.quickbooks.accounting");

                o.AuthorizationEndpoint = Configuration["ecm.qbo.authorization-endpoint"];
                o.TokenEndpoint = Configuration["ecm.qbo.token-endpoint"];
                o.UserInformationEndpoint = Configuration["ecm.qbo.user-info-endpoint"];

                o.Events.OnCreatingTicket = async context => {
                    var companyId = context.Request.Query["realmid"].FirstOrDefault() ?? throw new ArgumentNullException("realmId");
                    var accessToken = context.AccessToken;
                    var refreshToken = context.RefreshToken;

                    Configuration["ecm.qbo.access-token"] = accessToken;
                    Configuration["ecm.qbo.refresh-token"] = refreshToken;
                    Configuration["ecm.qbo.realm-id"] = companyId;

                    context.Backchannel.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", context.AccessToken);
                    context.Backchannel.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
                    var response = await context.Backchannel.GetStringAsync(context.Options.UserInformationEndpoint);

                    var result = JsonConvert.DeserializeObject<Dictionary<string, string>>(response);

                    var user = (ClaimsIdentity)context.Principal.Identity;
                    user.AddClaims(new Claim[] {
                        new Claim("access_token", accessToken),
                        new Claim("refresh_token", refreshToken),
                        new Claim(ClaimTypes.GivenName, result["givenName"]),
                        new Claim(ClaimTypes.Surname, result["familyName"]),
                        new Claim(ClaimTypes.Email, result["email"]),
                        new Claim(ClaimTypes.Name, result["givenName"]+" "+result["familyName"])
                    });
                };
            });

这行得通。我可以根据用户信息添加我的声明, context.Principal.Identity 表明它已经过身份验证。

由于某些原因,它似乎尝试重定向到`/Identity/Account/Login?returnUrl=%2F。这是为什么?

登录页面重定向

在这里,我不明白为什么我会得到重定向,这让我很困惑。因此,我添加了 AccountController 只是为了尝试将其关闭。

namespace ecm.backoffice.Controllers {
    [Authorize]
    [Route("[controller]/[action]")]
    public class AccountController : Controller {
        [AllowAnonymous]
        [HttpGet]
        public IActionResult Login(string returnUrl = "/") {
            return Challenge(new AuthenticationProperties { RedirectUri = returnUrl });
        }

        [Authorize]
        [HttpGet]
        public async Task<IActionResult> Logout(string returnUrl = "/") {
            await Request.HttpContext.SignOutAsync("qbo");
            return Redirect(returnUrl);
       }
    }
}

实际上,这造成的混乱比它解决的要多。我在这里迷路了...

应用迁移

这个个人用户身份验证 WebApp 似乎使用了 Identity,看起来它创建了很多幕后机制。我首先尝试注册到我的应用程序,并且必须“应用迁移”,这是我完全明白的,因为数据模型没有初始化。

所以,我点击了 Apply Migrations 按钮。我虽然对这个没问题...

实体框架核心

我知道该应用程序正在使用 Entity Framework Core 作为其持久性机制,因此是注册过程等。为了配置它,我需要将这些行添加到服务配置中。

        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));
        services.AddDefaultIdentity<IdentityUser>()
            .AddDefaultUI(UIFramework.Bootstrap4)
            .AddEntityFrameworkStores<ApplicationDbContext>();

看起来它根本行不通。

想法

在这一点上,我认为消息说实体框架无法从其底层数据存储中加载用户信息。我完全理解,我不想注册这个用户。我只是想理所当然地认为,如果 QBO 对用户进行了身份验证,那对我来说很好,即使用户没有注册,我也会授予对 WebApp 功能的开放式访问权限。

如何告诉我的 WebApp?

提问前阅读相关Q/AI

防止在 asp.net core 2.2 中重定向到 /Account/Login ASP.NET Core (2.1) Web API:身份和外部登录提供程序 asp.net core 2.2 在成功登录 Asp.net core 2.1 中的外部登录身份验证后重定向到登录

还有很多...

标签: c#asp.net-coreidentityclaims-based-identityasp.net-core-2.2

解决方案


推荐阅读