aspnetboilerplate: External Authentication does not work sometime when user does not exist

I using ABP framework for my project. It is AspCore + Angular target Full .Net framework. I am using External Authentication for login. I have followed the steps mentioned in the document. I have configured it to use Windows Authentication which works fine when the user already exists in the DB. I noticed that when it is the first time that user is visiting the website it gently registers the user and then authenticates it into the system. For some reason sometimes when it is the first time that user is visiting the website it throws an error. After like 10-20 refresh it works fine sometimes. Interestingly when it finally goes in after several page refresh the user id jumps from the latest which (e.g. user id 6) to user id 10000 or from 10004 to 20000.

Here is the error in the log:

INFO 2018-05-15 11:19:54,995 [13 ] ore.Mvc.Internal.ControllerActionInvoker - Executing action method SAH.NEO.Controllers.TokenAuthController.Authenticate (SAH.NEO.Web.Core) with arguments (SAH.NEO.Models.TokenAuth.AuthenticateModel) - ModelState is Valid ERROR 2018-05-15 11:19:55,042 [9 ] Mvc.ExceptionHandling.AbpExceptionFilter - There is no such an entity. Entity type: SAH.NEO.Authorization.Users.User, id: 9 Abp.Domain.Entities.EntityNotFoundException: There is no such an entity. Entity type: SAH.NEO.Authorization.Users.User, id: 9 at Abp.Domain.Repositories.AbpRepositoryBase2.<GetAsync>d__21.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.Threading.InternalAsyncHelper.<AwaitTaskWithPostActionAndFinallyAndGetResult>d__51.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.Authorization.Users.AbpUserStore2.<GetUserNameFromDatabaseAsync>d__88.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.Authorization.Users.AbpUserManager2.d__47.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.Authorization.AbpLogInManager3.<CreateLoginResultAsync>d__38.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) at Abp.Authorization.AbpLogInManager3.d__37.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.Authorization.AbpLogInManager3.<LoginAsync>d__36.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.Threading.InternalAsyncHelper.<AwaitTaskWithPostActionAndFinallyAndGetResult>d__51.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Nito.AsyncEx.Synchronous.TaskExtensions.WaitAndUnwrapException[TResult](Task1 task) at Nito.AsyncEx.AsyncContext.<>c__DisplayClass16_01.b__0(Task1 t) at System.Threading.Tasks.ContinuationResultTaskFromResultTask2.InnerInvoke() at System.Threading.Tasks.Task.Execute() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Nito.AsyncEx.Synchronous.TaskExtensions.WaitAndUnwrapException[TResult](Task1 task) at Nito.AsyncEx.AsyncContext.Run[TResult](Func1 action) at Abp.Threading.AsyncHelper.RunSync[TResult](Func1 func) at Abp.Authorization.AbpLogInManagerExtensions.Login[TTenant,TRole,TUser](AbpLogInManager3 logInManager, String userNameOrEmailAddress, String plainPassword, String tenancyName, Boolean shouldLockout) at SAH.NEO.Controllers.TokenAuthController.GetLoginResult(String usernameOrEmailAddress, String password, String tenancyName) in C:\Projects\NEO\Main\SAH.NEO\src\SAH.NEO.Web.Core\Controllers\TokenAuthController.cs:line 197 at SAH.NEO.Controllers.TokenAuthController.d__8.MoveNext() in C:\Projects\NEO\Main\SAH.NEO\src\SAH.NEO.Web.Core\Controllers\TokenAuthController.cs:line 57 — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at lambda_method(Closure , Object ) at Microsoft.Extensions.Internal.ObjectMethodExecutorAwaitable.Awaiter.GetResult() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__12.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__10.MoveNext() — End of stack trace from previous location where exception was thrown — at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.d__14.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.d__23.MoveNext() INFO 2018-05-15 11:19:55,042 [9 ] etCore.Mvc.Internal.ObjectResultExecutor - Executing ObjectResult, writing value Microsoft.AspNetCore.Mvc.ControllerContext. INFO 2018-05-15 11:19:55,042 [9 ] ore.Mvc.Internal.ControllerActionInvoker - Executed action SAH.NEO.Controllers.TokenAuthController.Authenticate (SAH.NEO.Web.Core) in 52.0181ms ERROR 2018-05-15 11:19:55,042 [9 ] Microsoft.AspNetCore.Server.Kestrel - Connection id “0HLDQ0BD8TOQA”, Request id “0HLDQ0BD8TOQA:00000003”: An unhandled exception was thrown by the application. System.InvalidOperationException: StatusCode cannot be set because the response has already started. at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Frame.set_StatusCode(Int32 value) at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.Frame.Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.set_StatusCode(Int32 value) at Microsoft.AspNetCore.Http.Internal.DefaultHttpResponse.set_StatusCode(Int32 value) at Microsoft.AspNetCore.Builder.Internal.ApplicationBuilder.<>c.b__16_0(HttpContext context) at Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.StaticFiles.DefaultFilesMiddleware.Invoke(HttpContext context) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.d__6.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Builder.RouterMiddleware.d__4.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Owin.Mapping.MapMiddleware.d__0.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Owin.WebSocketAcceptAdapter.<>c__DisplayClass6_0.<b__0>d.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.d__4.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.d__6.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at SAH.NEO.Web.Host.Startup.Startup.<>c.<b__4_1>d.MoveNext() in C:\Projects\NEO\Main\SAH.NEO\src\SAH.NEO.Web.Host\Startup\Startup.cs:line 124 — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.d__7.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Abp.AspNetCore.Security.AbpSecurityHeadersMiddleware.d__2.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Server.IISIntegration.IISMiddleware.d__11.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.d__3.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)

I have changed the GetLoginResultAsync to sync method as I thought it might be this that sometime works sometime does not but it was not it.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 37 (12 by maintainers)

Most upvoted comments

I have taken a deeper look at the issue. This seems to be a problem when using UnitOfWork.IsTransactional with PostgreSQL

When a new user is created via AbpLoginManager.TryLoginFromExternalAuthenticationSources(). https://github.com/aspnetboilerplate/aspnetboilerplate/blob/cf87dd654bab699dbb5962003efd55ac945e3e2c/src/Abp.ZeroCore/Authorization/AbpLoginManager.cs#L307-L323

The user entity is being tracked in Microsoft.EntityFrameworkCore.ChangeTracker.Entries() after SaveChangesAsync() is called followed by await UserManager.CreateAsync(user); .

However, the user is then updated in CreateLoginResultAsync() after the entity creation while it is still under the same transaction. https://github.com/aspnetboilerplate/aspnetboilerplate/blob/cf87dd654bab699dbb5962003efd55ac945e3e2c/src/Abp.ZeroCore/Authorization/AbpLoginManager.cs#L170-L201

https://github.com/aspnetboilerplate/aspnetboilerplate/blob/cf87dd654bab699dbb5962003efd55ac945e3e2c/src/Abp.ZeroCore/Authorization/AbpLoginManager.cs#L205-L234

The exception is thrown when UserManager.UpdateAsync(user) checks for old username from database. https://github.com/aspnetboilerplate/aspnetboilerplate/blob/cf87dd654bab699dbb5962003efd55ac945e3e2c/src/Abp.ZeroCore/Authorization/Users/AbpUserManager.cs#L613

And a new scope is used to retrieve old username https://github.com/aspnetboilerplate/aspnetboilerplate/blob/cf87dd654bab699dbb5962003efd55ac945e3e2c/src/Abp.ZeroCore/Authorization/Users/AbpUserStore.cs#L1221-L1232

By default, EfCoreUnitOfWork uses IsolationLevel.ReadUncommitted but it is treated as IsolationLevel.ReadCommitted in PostgreSQL

PostgreSQL’s Read Uncommitted mode behaves like Read Committed.

https://github.com/aspnetboilerplate/aspnetboilerplate/blob/cf87dd654bab699dbb5962003efd55ac945e3e2c/src/Abp.EntityFrameworkCore/EntityFrameworkCore/Uow/DbContextEfCoreTransactionStrategy.cs#L38

However, this is the behaviour that currently not supported by PostgreSQL as stated below.

Read Committed is the default isolation level in PostgreSQL. When a transaction uses this isolation level, a SELECT query (without a FOR UPDATE/SHARE clause) sees only data committed before the query began; it never sees either uncommitted data or changes committed during query execution by concurrent transactions.

https://www.postgresql.org/docs/current/static/transaction-iso.html#XACT-READ-COMMITTED

I would suggest to disable IsTransactional for UnitOfWork if you are using PostgreSQL.

public override void PreInitialize() {
    // more code ..
    Configuration.UnitOfWork.IsTransactional = false;
    // more code ..
}

Replying to myself, I just need to do that in TokenAuthController.cs :

        [UnitOfWork(isTransactional: false)]
        public async Task<AuthenticateResultModel> Authenticate([FromBody] AuthenticateModel model)

@mohammad-shadmehr IIRC, I already did earlier when I first encountered this problem (in July) and it did not help but I’ll test again tomorrow.

@humanww The only difference I can see are the auto generated column … at least it may explain why I feel so alone with this problem. Choosing Postgres was a challenging choice 😦.