首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ABP.IO - MultiTenancy -设置来自外部IDP的租户

ABP.IO - MultiTenancy -设置来自外部IDP的租户
EN

Stack Overflow用户
提问于 2021-11-16 16:44:13
回答 1查看 536关注 0票数 0

我正在尝试将Auth0配置为ABP.IO应用程序中的外部登录提供者(MVC与集成身份服务器)。我已经让它正常工作,这样我就可以很好地登录,但我想不出的是如何将租户设置在ABP一侧。

我想出的是Auth0端的一条规则,将TenantId作为id令牌中的声明填充,以便在GetExternalLoginInfoAsync方法中的自定义SingInManager中解析这一点,如下所示:

代码语言:javascript
复制
string tenantId = auth.Principal.FindFirstValue("https://example.com/tenantId");

我只是很难从那里弄清楚该怎么做。假设用户将被配置为通过Auth0进行身份验证,并且用户将在第一次登录时在本地创建(同样,除了租户部分外,该登录正在工作)。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-11-17 15:57:46

好的,这是我的解决办法,它应该是可以转移到任何外部登录系统,你所依赖的。我不确定这是否是正确的做法,所以如果其他人想要投入一个更有效的系统,我将全神贯注。

无论如何,我的工作流程假设您已经像我一样创建了一个机制,以便从外部IDP发送TenantId。为此,我使用了Auth0中的组织功能,并添加了TenantId作为元数据,然后我在Auth0中创建了一个ActioninAuth0,将该元数据作为一个声明附加到ABP端。

在ABP中,我遵循本文来覆盖SignInManager:https://community.abp.io/articles/how-to-customize-the-signin-manager-3e858753

与本文一样,我重写了管理器中符号的GetExternalLoginInfoAsync方法,并添加了以下行,将TenantId从Auth0声明中提取出来,并使用预定义的AbpClaimTypes.TenantId值将其添加回去。

编辑:我还必须重写ExternalLoginSignInAsync方法来解释多租户(否则它一直试图重新创建用户并抛出重复的电子邮件错误)。我将在下面的全班张贴我添加的内容在评论中:

代码语言:javascript
复制
public class CustomSignInManager : Microsoft.AspNetCore.Identity.SignInManager<Volo.Abp.Identity.IdentityUser>
    {
        private const string LoginProviderKey = "LoginProvider";
        private const string XsrfKey = "XsrfId";
        private readonly IDataFilter _dataFilter;

        public CustomSignInManager(
            IDataFilter dataFilter,
            Microsoft.AspNetCore.Identity.UserManager<Volo.Abp.Identity.IdentityUser> userManager,
            Microsoft.AspNetCore.Http.IHttpContextAccessor contextAccessor,
            Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<Volo.Abp.Identity.IdentityUser> claimsFactory,
            Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Identity.IdentityOptions> optionsAccessor,
            Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Identity.SignInManager<Volo.Abp.Identity.IdentityUser>> logger,
            Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider schemes,
            Microsoft.AspNetCore.Identity.IUserConfirmation<Volo.Abp.Identity.IdentityUser> confirmation)
            : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation)
        {
            _dataFilter = dataFilter;
        }

        /// <summary>
        /// Gets the external login information for the current login, as an asynchronous operation.
        /// </summary>
        /// <param name="expectedXsrf">Flag indication whether a Cross Site Request Forgery token was expected in the current request.</param>
        /// <returns>The task object representing the asynchronous operation containing the <see name="ExternalLoginInfo"/>
        /// for the sign-in attempt.</returns>
        public override async Task<Microsoft.AspNetCore.Identity.ExternalLoginInfo> GetExternalLoginInfoAsync(string expectedXsrf = null)
        {
            var auth = await Context.AuthenticateAsync(IdentityConstants.ExternalScheme);
            var items = auth?.Properties?.Items;
            if (auth?.Principal == null || items == null || !items.ContainsKey(LoginProviderKey))
            {
                return null;
            }

            if (expectedXsrf != null)
            {
                if (!items.ContainsKey(XsrfKey))
                {
                    return null;
                }
                var userId = items[XsrfKey] as string;
                if (userId != expectedXsrf)
                {
                    return null;
                }
            }

            var providerKey = auth.Principal.FindFirstValue(ClaimTypes.NameIdentifier);

            var provider = items[LoginProviderKey] as string;
            if (providerKey == null || provider == null)
            {
                return null;
            }

            var providerDisplayName = (await GetExternalAuthenticationSchemesAsync()).FirstOrDefault(p => p.Name == provider)?.DisplayName
                                      ?? provider;

            /* Begin tenantId claim search */
            string tenantId = auth.Principal.FindFirstValue("https://example.com/tenantId"); //pull the tenantId claim if it exists
            if(!string.IsNullOrEmpty(tenantId))
            {
                auth.Principal.Identities.FirstOrDefault().AddClaim(new Claim(AbpClaimTypes.TenantId, tenantId)); //if there is a tenantId, add the AbpClaimTypes.TenantId claim back into the principal
            }
            /* End tenantId claim search */

            var eli = new ExternalLoginInfo(auth.Principal, provider, providerKey, providerDisplayName)
            {
                AuthenticationTokens = auth.Properties.GetTokens(),
                AuthenticationProperties = auth.Properties
            };



            return eli;
        }

        /// <summary>
        /// Signs in a user via a previously registered third party login, as an asynchronous operation.
        /// </summary>
        /// <param name="loginProvider">The login provider to use.</param>
        /// <param name="providerKey">The unique provider identifier for the user.</param>
        /// <param name="isPersistent">Flag indicating whether the sign-in cookie should persist after the browser is closed.</param>
        /// <param name="bypassTwoFactor">Flag indicating whether to bypass two factor authentication.</param>
        /// <returns>The task object representing the asynchronous operation containing the <see name="SignInResult"/>
        /// for the sign-in attempt.</returns>
        public override async Task<SignInResult> ExternalLoginSignInAsync(string loginProvider, string providerKey, bool isPersistent, bool bypassTwoFactor)
        {
            Volo.Abp.Identity.IdentityUser user = null; //stage the user variable as null
            using (_dataFilter.Disable<IMultiTenant>()) //disable the tenantid filters so we can search all logins for the expected key
            {
                 user = await UserManager.FindByLoginAsync(loginProvider, providerKey); //search logins for the expected key
            }
                
            if (user == null)
            {
                return SignInResult.Failed;
            }

            var error = await PreSignInCheck(user);
            if (error != null)
            {
                return error;
            }
            return await SignInOrTwoFactorAsync(user, isPersistent, loginProvider, bypassTwoFactor);
        }

    }

完成后,我跟踪GetExternalLoginInfoAsync在哪里被使用,并发现我必须重写登录页面的LoginModel中的CreateExternalUserAsync方法。为此,我遵循了本文中关于创建CustomLoginModel.csLogin.cshtmlhttps://community.abp.io/articles/hide-the-tenant-switch-of-the-login-page-4foaup7p的说明。

因此,我的Auth0LoginModel类如下所示:

代码语言:javascript
复制
public class Auth0LoginModel : LoginModel
    {
        public Auth0LoginModel(IAuthenticationSchemeProvider schemeProvider, IOptions<AbpAccountOptions> accountOptions, IOptions<IdentityOptions> identityOptions) : base(schemeProvider, accountOptions, identityOptions)
        {
        }

        protected override async Task<IdentityUser> CreateExternalUserAsync(ExternalLoginInfo info)
        {
            await IdentityOptions.SetAsync();

            var emailAddress = info.Principal.FindFirstValue(AbpClaimTypes.Email);
            /* Begin TenantId claim check */
            var tenantId = info.Principal.FindFirstValue(AbpClaimTypes.TenantId);
            if (!string.IsNullOrEmpty(tenantId))
            {
                try
                {
                    CurrentTenant.Change(Guid.Parse(tenantId));
                }
                catch
                {
                    await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
                    {
                        Identity = IdentitySecurityLogIdentityConsts.IdentityExternal,
                        Action = "Unable to parse TenantId: " + tenantId
                    }) ;
                }
            }
            /* End TenantId claim check */

            var user = new IdentityUser(GuidGenerator.Create(), emailAddress, emailAddress, CurrentTenant.Id);

            CheckIdentityErrors(await UserManager.CreateAsync(user));
            CheckIdentityErrors(await UserManager.SetEmailAsync(user, emailAddress));
            CheckIdentityErrors(await UserManager.AddLoginAsync(user, info));
            CheckIdentityErrors(await UserManager.AddDefaultRolesAsync(user));

            return user;
        }
    }

添加的代码在注释之间,方法的其余部分是从源中提取的。因此,我查找存在AbpClaimTypes.TenantId声明,如果存在,则尝试在调用创建新IdentityUser之前使用CurrentTenant.Change方法更改租户。

一旦完成,用户就会在正确的租户中创建,一切都会像预期的那样流动。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69992998

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档