Base solution for your next web application
Open Closed

User Linking Between tenants & host #10470


User avatar
0
maharatha created

Prerequisites

Please answer the following questions before submitting an issue. YOU MAY DELETE THE PREREQUISITES SECTION.

  • What is your product version? - Latest
  • What is your product type (Angular or MVC)? Angular
  • What is product framework type (.net framework or .net core)?Latest

I am trying to link users based on Provider Key and Login provider. Below is my code :

  private async Task LinkUsers(ExternalAuthUserInfo externalUserInfo, User user)
        {
           //Check if the user account has been linked
            if (!_userAccounts.GetAll().Any(p =>
                p.UserLinkId.HasValue && p.UserId == user.Id &&
                p.TenantId == user.TenantId))
            {
                //if not linked then try to link
                using (_unitOfWorkProvider.Current.DisableFilter(AbpDataFilters.MayHaveTenant))
                {
                    //This needs to be joined so that we don't pick the deleted record in User Account tables
                    var useraccountstobeConnected = from useraccounts in _userAccounts.GetAll()
                                                    join userlogin in _userLogins.GetAll() on
                                                        new { A = useraccounts.UserId, B = useraccounts.TenantId } equals new
                                                        { A = userlogin.UserId, B = userlogin.TenantId }
                                                    where (userlogin.LoginProvider == externalUserInfo.Provider &&
                                                           userlogin.ProviderKey == externalUserInfo.ProviderKey &&
                                                           userlogin.TenantId != user.TenantId)
                                                    select new
                                                    {
                                                        useraccounts.TenantId,
                                                        useraccounts.UserId
                                                    };

                    using var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew);
                    foreach (var useraccount in useraccountstobeConnected)
                    {
                        using (_unitOfWorkProvider.Current.SetTenantId(useraccount.TenantId))
                        {
                            var seconduser = await _userManager.FindByIdAsync(useraccount.UserId.ToString());

                            if (seconduser != null && seconduser.IsActive)
                                await _userLinkManager.Link(user, seconduser);
                        }

                    }

                    await uow.CompleteAsync();
                }
            }

        }

I am calling this in 2 places :

case AbpLoginResultType.Success: // Link the users if not linked await LinkUsers(externalUser, loginResult.User) case AbpLoginResultType.UnknownExternalLogin: In this case await LinkUsers(externalUser, loginResult.User) doesnt work as the record is not available in the user account table.

Then I tried something like this :

_unitOfWorkManager.Current.Completed += (sender, args) => { LinkUsers(externalUser, loginResult.User) }

This doesn't work either. Can you suggest a way to link users right after registration.


11 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @maharatha

    Sorry, I coudln't understand the problem. Which part exactly doesn't work ? Do you have a problem for inserting the records ?

  • User Avatar
    0
    maharatha created

    Sorry for not being clear enough. So let me explain what I am doing.

    I will adding users at the tenant level using the existing User creation. This will send an invite to the user and they will be redirected to our identity provider, in this case Okta and once registartion is complete they will be send back to the application and the user can login to the tenant.

    However for host the approach is little different, we are allowing the users to self register to the Host through okta.

    Both the things are working as expected, however when I try to link the host user with the tenant user I am having issue.

    The above code i wrote i am trying to call after the registartion process, however i am unable to link the user as the first user which is the host uiser is coming up as null , i think the reason being the user record is not inserted in the useraccounts table.

    Then I changed the code to add the user to user account table as soon as the user is registered :

      using (CurrentUnitOfWork.SetTenantId(null))
                {
                    _userAccountRepository.Insert(new UserAccount
                    {
                        TenantId = user.TenantId,
                        UserName = user.UserName,
                        UserId = user.Id,
                        EmailAddress = user.EmailAddress
                    });
                    await CurrentUnitOfWork.SaveChangesAsync();
                }
    
    

    After adding this I am able to get the first user in the link user however while saving the transaction it's failing.

    I hope this explains what I amtrying to do.

    I think the issue in a nutshell is I am unable to link a newly registered user to another right after registering the user. If you can let me know how can i do that it would be very helpful. I have been trying this for couple of days with zero luck.

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    while saving the transaction it's failing

    Do you have an exception message about this error ? Looking at your code, UserAccount entity should be saved correctly becuase it is a host entity, see https://github.com/aspnetboilerplate/aspnetboilerplate/blob/dev/src/Abp.Zero.Common/Authorization/Users/UserAccount.cs#L13

  • User Avatar
    0
    maharatha created

    I think I am storing the useraccount correctly but i am kind of struggling to find the issue , but below is the error :

    "ERROR 2021-07-27 21:35:22,834 [29 ] Microsoft.EntityFrameworkCore.Update - An exception occurred in the database while saving changes for context type 'CAPSPAY.ONE.EntityFrameworkCore.ONEDbContext'. Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> Microsoft.Data.SqlClient.SqlException (0x80131904): Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding. ---> System.ComponentModel.Win32Exception (258): The wait operation timed out. at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__169_0(Task1 result) at System.Threading.Tasks.ContinuationResultTaskFromResultTask2.InnerInvoke() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location --- at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) --- End of stack trace from previous location --- at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken) ClientConnectionId:0426ad21-c0db-453c-88e2-21c8c684dbe3 Error Number:-2,State:0,Class:11 --- End of inner exception stack trace --- at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList1 entriesToSave, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(DbContext _, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func4 operation, Func4 verifySucceeded, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)

    ClientConnectionId:8143d640-d9fd-411c-a92c-980d60063039 Error Number:-2,State:0,Class:11 --- End of inner exception stack trace --- at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList1 entriesToSave, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(DbContext _, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func4 operation, Func4 verifySucceeded, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) WARN 2021-07-27 21:35:23,814 [30 ] Abp.BackgroundJobs.BackgroundJobManager - Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> Microsoft.Data.SqlClient.SqlException (0x80131904): Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding. ---> System.ComponentModel.Win32Exception (258): The wait operation timed out. at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__169_0(Task1 result) at System.Threading.Tasks.ContinuationResultTaskFromResultTask2.InnerInvoke() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location --- at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) --- End of stack trace from previous location --- at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken) ClientConnectionId:8143d640-d9fd-411c-a92c-980d60063039 Error Number:-2,State:0,Class:11 --- End of inner exception stack trace --- at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList1 entriesToSave, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(DbContext _, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func4 operation, Func4 verifySucceeded, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at Abp.EntityFrameworkCore.AbpDbContext.SaveChangesAsync(CancellationToken cancellationToken) at Abp.Zero.EntityFrameworkCore.AbpZeroCommonDbContext3.SaveChangesAsync(CancellationToken cancellationToken) at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.SaveChangesAsync() at Abp.EntityFrameworkCore.Uow.EfCoreUnitOfWork.CompleteUowAsync() at Abp.Domain.Uow.UnitOfWorkBase.CompleteAsync() at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous[TResult](IInvocation invocation) at Abp.Domain.Uow.UnitOfWorkInterceptor.InternalInterceptAsynchronous(IInvocation invocation) at Abp.BackgroundJobs.BackgroundJobManager.TryUpdateAsync(BackgroundJobInfo jobInfo) Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred while updating the entries. See the inner exception for details. ---> Microsoft.Data.SqlClient.SqlException (0x80131904): Execution Timeout Expired. The timeout period elapsed prior to completion of the operation or the server is not responding. ---> System.ComponentModel.Win32Exception (258): The wait operation timed out. at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__169_0(Task1 result) at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke() at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location --- at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) --- End of stack trace from previous location --- at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken) ClientConnectionId:8143d640-d9fd-411c-a92c-980d60063039 Error Number:-2,State:0,Class:11 --- End of inner exception stack trace --- ----------------------------------------------------------- The code works perfectly fine when the user logs in after registration and I link the user while logging its the issue where the user logs in right after registration.

    If you could please write a sample code how to connect right after registering that would be very helpful. I have spent numerous hours/days trying to figure out the issues

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @maharatha

    The message shows a connection problem but I think it is something different. Is it possible to share your project via email with us ? We can reproduce the problem and help you solve the issue.

    Thanks,

  • User Avatar
    0
    maharatha created

    I have already sent the link and shared with [email protected]

    The google account that's being shared from is [email protected]

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @maharatha

    Thanks, just requested access. I will inform you via email for the progress.

  • User Avatar
    0
    maharatha created

    Any updates?

  • User Avatar
    0
    maharatha created

    I need some attention to this issue please. I have already sent all the details.

  • User Avatar
    0
    maharatha created

    Hi Team -

    I have been following up on this issue more than a week and haven't got any response. It feels like no one is even reading these tickets.

    Is there a way we can expediate this.

    Thanks

  • User Avatar
    0
    musa.demir created

    Hi @maharatha

    I get your project. I tested your codes locally but it works. Can you please check your codes on your local.