Open Closed

Configuring Abp project for exclusive external login and implementing custom actions on first-time user login #6193


User avatar
0
ademaygun created
  • ABP Framework version: v5.3.3
  • UI Type: Angular
  • Database System: EF Core (SQL Server, Oracle, MySQL, PostgreSQL, etc..)
  • Tiered (for MVC) or Auth Server Separated (for Angular): no
  • Exception message and full stack trace: No exception message
  • Steps to reproduce the issue:
  1. I want only external login to be enabled and all other login methods to be disabled in my Abp project. How can I achieve this?
  2. If the external login is successful, and the user is coming to my system for the first time, I want to perform some checks and custom updates. How can I achieve this? I couldn't determine if I need to implement coding similar to the this solution provided after my research.

Note : External login provider is an another Abp project


8 Answer(s)
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    You can check this: https://docs.abp.io/en/commercial/latest/modules/account#local-login

    2.

    You can override the login and register page, for example:

    [ExposeServices(typeof(LoginModel))]
    public class MyLoginModel : LoginModel
    {
        protected virtual async Task<IdentityUser> CreateExternalUserAsync(ExternalLoginInfo info)
        {
            var user = await base.CreateExternalUserAsync(info);
            //.. first login
        }
    }
    
    [ExposeServices(typeof(RegisterModel))]
    public class MyRegisterModel : RegisterModel
    {
        protected virtual async Task<IdentityUser> RegisterExternalUserAsync(ExternalLoginInfo externalLoginInfo, string emailAddress)
        {
            var user = await base.RegisterExternalUserAsync(externalLoginInfo, emailAddress);
        //.. first login
        }
    }
    
  • User Avatar
    0
    ademaygun created

    Hi, After a successful login from an external login provider, I want to manually check whether the user exists in my system and then when I find the appropriate user, log them into my system (especially for tenant users)

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    You can try

    [ExposeServices(typeof(LoginModel))]
    public class MyLoginModel : LoginModel
    {
        protected virtual async Task<IdentityUser> CreateExternalUserAsync(ExternalLoginInfo info)
        {
            var email = loginInfo.Principal.FindFirstValue(AbpClaimTypes.Email) ?? loginInfo.Principal.FindFirstValue(ClaimTypes.Email);
            var user = await UserManager.FindByEmailAsync(email);
            // just a demo, You can check if the user exists using any way
            
            if(user == null)
            {
                user = await base.CreateExternalUserAsync(info);
            }
        }
    }
    
    [ExposeServices(typeof(RegisterModel))]
    public class MyRegisterModel : RegisterModel
    {
        protected virtual async Task<IdentityUser> RegisterExternalUserAsync(ExternalLoginInfo externalLoginInfo, string emailAddress)
        {
            var email = loginInfo.Principal.FindFirstValue(AbpClaimTypes.Email) ?? loginInfo.Principal.FindFirstValue(ClaimTypes.Email);
            var user = await UserManager.FindByEmailAsync(email);
            // just a demo, You can check if the user exists using any way
            
            if(user == null)
            {
                user = await base.RegisterExternalUserAsync(externalLoginInfo, emailAddress);
            }
            //.. first login
        }
    }
    
  • User Avatar
    0
    ademaygun created

    Hi Liangshiwei,

    Thank you for your response, I appreciate your answer and I understand it very well. However, it seems like a workaround solution. I want to override the place where the decision to trigger this method is made. Even if I override the CreateExternalUserAsync method, it will still attempt to find the user every time and not be able to find it

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    it will still attempt to find the user every time and not be able to find it

    Sorry, I didn't get it.

  • User Avatar
    0
    ademaygun created

    Hi liangshiwei, If I write the example code exactly as below, it creates a user in the AbpUsers table. If I log in again with the same user through an external provider, this time the CreateExternalUserAsync method is not triggered. Your suggestion works and is effective, but it means that the method (CreateExternalUserAsync) will always check whether the user exists before being called

    [ExposeServices(typeof(LoginModel))]
    public class MyLoginModel : LoginModel
    {
        protected virtual async Task<IdentityUser> CreateExternalUserAsync(ExternalLoginInfo info)
        {
            var user = await base.CreateExternalUserAsync(info);
        }
    }
    

    adding external Login provider :

    private void ConfigureExternalProviders(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddAuthentication()
     .AddOpenIdConnect("oidc", options =>
     {
         options.Authority = "https://localhost:44366/";
         options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]); ;
         options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
         options.ClientId = "Client1";
         options.ClientSecret = "mysecret";
    
         options.UsePkce = true;
         options.SaveTokens = true;
         options.GetClaimsFromUserInfoEndpoint = true;
         options.Scope.Add("role");
         options.Scope.Add("email");
         options.Scope.Add("phone");
         options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
     }
        );
    
  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    HI,

    Ok, you can try override the OnGetExternalLoginCallbackAsync method

    
    [ExposeServices(typeof(LoginModel))]
    public class MyLoginModel : LoginModel
    {
        [UnitOfWork]
        public override async Task<IActionResult> OnGetExternalLoginCallbackAsync(string returnUrl = "", string returnUrlHash = "", string remoteError = null)
        {
            if (remoteError != null)
            {
                Logger.LogWarning($"External login callback error: {remoteError}");
                return RedirectToPage("./Login");
            }
    
            await IdentityOptions.SetAsync();
    
            var loginInfo = await SignInManager.GetExternalLoginInfoAsync();
            if (loginInfo == null)
            {
                Logger.LogWarning("External login info is not available");
                return RedirectToPage("./Login");
            }
    
            IsLinkLogin = await VerifyLinkTokenAsync();
    
            var result = await SignInManager.ExternalLoginSignInAsync(
                loginInfo.LoginProvider,
                loginInfo.ProviderKey,
                isPersistent: true,
                bypassTwoFactor: true
            );
    
            if (!result.Succeeded)
            {
                await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
                {
                    Identity = IdentitySecurityLogIdentityConsts.IdentityExternal,
                    Action = "Login" + result
                });
            }
    
            if (result.IsLockedOut)
            {
                Logger.LogWarning($"Cannot proceed because user is locked out!");
                return RedirectToPage("./LockedOut", new {
                    returnUrl = ReturnUrl,
                    returnUrlHash = ReturnUrlHash
                });
            }
    
            if (result.IsNotAllowed)
            {
                Logger.LogWarning($"External login callback error: User is Not Allowed!");
    
                var user = await UserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey);
                if (user.IsActive)
                {
                    await StoreConfirmUser(user);
                    return RedirectToPage("./ConfirmUser", new {
                        returnUrl = ReturnUrl,
                        returnUrlHash = ReturnUrlHash
                    });
                }
    
                return RedirectToPage("./Login");
            }
    
            if (result.Succeeded)
            {
                var user = await UserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey);
                if (IsLinkLogin)
                {
                    using (CurrentPrincipalAccessor.Change(await SignInManager.CreateUserPrincipalAsync(user)))
                    {
                        await IdentityLinkUserAppService.LinkAsync(new LinkUserInput
                        {
                            UserId = LinkUserId.Value,
                            TenantId = LinkTenantId,
                            Token = LinkToken
                        });
    
                        await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
                        {
                            Identity = IdentitySecurityLogIdentityConsts.Identity,
                            Action = IdentityProSecurityLogActionConsts.LinkUser,
                            UserName = user.UserName,
                            ExtraProperties =
                                {
                                    { IdentityProSecurityLogActionConsts.LinkTargetTenantId, LinkTenantId },
                                    { IdentityProSecurityLogActionConsts.LinkTargetUserId, LinkUserId }
                                }
                        });
    
                        using (CurrentTenant.Change(LinkTenantId))
                        {
                            var targetUser = await UserManager.GetByIdAsync(LinkUserId.Value);
                            using (CurrentPrincipalAccessor.Change(await SignInManager.CreateUserPrincipalAsync(targetUser)))
                            {
                                await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
                                {
                                    Identity = IdentitySecurityLogIdentityConsts.Identity,
                                    Action = IdentityProSecurityLogActionConsts.LinkUser,
                                    UserName = targetUser.UserName,
                                    ExtraProperties =
                                        {
                                            { IdentityProSecurityLogActionConsts.LinkTargetTenantId, targetUser.TenantId },
                                            { IdentityProSecurityLogActionConsts.LinkTargetUserId, targetUser.Id }
                                        }
                                });
                            }
                        }
                    }
                }
    
                await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
                {
                    Identity = IdentitySecurityLogIdentityConsts.IdentityExternal,
                    Action = result.ToIdentitySecurityLogAction(),
                    UserName = user.UserName
                });
    
                return RedirectSafely(returnUrl, returnUrlHash);
            }
    
            var email = loginInfo.Principal.FindFirstValue(AbpClaimTypes.Email);
            if (email.IsNullOrWhiteSpace())
            {
                return RedirectToPage("./Register", new {
                    IsExternalLogin = true,
                    ExternalLoginAuthSchema = loginInfo.LoginProvider,
                    ReturnUrl = returnUrl
                });
            }
            
            //-------- this is the source code, you can change it.------------
            var externalUser = await UserManager.FindByEmailAsync(email);
            if (externalUser == null)
            {
                externalUser = await CreateExternalUserAsync(loginInfo);
            }
            else
            {
                if (await UserManager.FindByLoginAsync(loginInfo.LoginProvider, loginInfo.ProviderKey) == null)
                {
                    CheckIdentityErrors(await UserManager.AddLoginAsync(externalUser, loginInfo));
                }
            }
    
            if (await HasRequiredIdentitySettings())
            {
                Logger.LogWarning($"New external user is created but confirmation is required!");
    
                await StoreConfirmUser(externalUser);
                return RedirectToPage("./ConfirmUser", new {
                    returnUrl = ReturnUrl,
                    returnUrlHash = ReturnUrlHash
                });
            }
    
            await SignInManager.SignInAsync(externalUser, false);
    
            if (IsLinkLogin)
            {
                using (CurrentPrincipalAccessor.Change(await SignInManager.CreateUserPrincipalAsync(externalUser)))
                {
                    await IdentityLinkUserAppService.LinkAsync(new LinkUserInput
                    {
                        UserId = LinkUserId.Value,
                        TenantId = LinkTenantId,
                        Token = LinkToken
                    });
    
                    await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
                    {
                        Identity = IdentitySecurityLogIdentityConsts.Identity,
                        Action = IdentityProSecurityLogActionConsts.LinkUser,
                        UserName = externalUser.UserName,
                        ExtraProperties =
                            {
                                { IdentityProSecurityLogActionConsts.LinkTargetTenantId, LinkTenantId },
                                { IdentityProSecurityLogActionConsts.LinkTargetUserId, LinkUserId }
                            }
                    });
    
                    using (CurrentTenant.Change(LinkTenantId))
                    {
                        var targetUser = await UserManager.GetByIdAsync(LinkUserId.Value);
                        using (CurrentPrincipalAccessor.Change(await SignInManager.CreateUserPrincipalAsync(targetUser)))
                        {
                            await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
                            {
                                Identity = IdentitySecurityLogIdentityConsts.Identity,
                                Action = IdentityProSecurityLogActionConsts.LinkUser,
                                UserName = targetUser.UserName,
                                ExtraProperties =
                                    {
                                        { IdentityProSecurityLogActionConsts.LinkTargetTenantId, targetUser.TenantId },
                                        { IdentityProSecurityLogActionConsts.LinkTargetUserId, targetUser.Id }
                                    }
                            });
                        }
                    }
                }
            }
    
            await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext
            {
                Identity = IdentitySecurityLogIdentityConsts.IdentityExternal,
                Action = result.ToIdentitySecurityLogAction(),
                UserName = externalUser.Name
            });
    
            return RedirectSafely(returnUrl, returnUrlHash);
        }
    }
    
  • User Avatar
    0
    ademaygun created

    Hi liangshiwei, Thanks for your support!

Made with ❤️ on ABP v9.1.0-preview. Updated on November 11, 2024, 11:11