Open Closed

Logout does not actually logs out in Angular app #5781


User avatar
0
alexander.nikonov created
  • ABP Framework version: v7.0.1.
  • UI Type: Angular
  • Database System: EF Core
  • Auth Server Separated

Hi. I can't say for sure in which moment it has begun, but now if I try to logout from the page OTHER than Home page (root page) - the logout does not happen. Here is the workflow of what is happening when I'm clicking "Logout" button in my Angular app: First connect/revocation request contains "access_token". Next "connect/revocation" request contains "refresh_token. Eventually I am just redirected to Home page. Also the whole process is very slow and not attractive visually: at first the navigation and top menu gets disappears, so I see only the current page instead of being instantly redirected to Identity Server Login box (where I do not get at all in my case). I don't know where to look at: is it a problem of front-end or back-end. Please point me in right direction.

Additionaly details: we DO have "AuthGuard" in app-routing.module.ts for each page we are trying to logout from.

Marked in red is yet another issue which is still not resolved: the subscribed API calls in the current Component: seems like at some point the user IS getting logged out (and thus is not authorized), so these requests are not allowed (but later the user is kind of logged-in again automatically).

Sorry I cannot share the source code with you. So I will be glad to follow your recommendations in troubleshooting the logout scenario.


12 Answer(s)
  • User Avatar
    0
    alexander.nikonov created

    What I have noticed:

    if I remove [AuthGuard]:

    then logging out from any page brings me to site Login dialog. Which looks like if the user IS logged out:

    If I click "Login" - I am brought to Identity Server Login page (note: I am not automatically logged in!)

    However, using AuthGuard for a default route ('') causes this behavior I've described in the very beginning. Though, I don't want to go to the first login form, so this "workaround" does not suit me.

    I also took a look at Identity Server log (this is the fragment of what is happening after pressing "Logout") and the following looks weird to me: why the authorize request is sent straight after successful token revocation? The configuration of Identity Server looks usual to me, we have not changed it, but probably some configuration aspects are missing?

    2023-09-18 01:10:05.334 -05:00 [DBG] Refresh token revoked 2023-09-18 01:10:06.116 -05:00 [INF] Token revocation complete 2023-09-18 01:10:06.116 -05:00 [INF] {"ClientId":"XXX","ClientName":"XXX","TokenType":"refresh_token","Token":"****6492","Category":"Token","Name":"Token Revoked Success","EventType":"Success","Id":2010,"Message":null,"ActivityId":"800001b8-0004-f900-b63f-84710c7967bb","TimeStamp":"2023-09-18T06:10:06.0000000Z","ProcessId":24860,"LocalIpAddress":"::1:44357","RemoteIpAddress":"::1","$type":"TokenRevokedSuccessEvent"} 2023-09-18 01:10:06.707 -05:00 [INF] Request finished HTTP/1.1 POST https://localhost:44357/connect/revocation application/x-www-form-urlencoded 149 - 200 - - 5005.9323ms 2023-09-18 01:10:06.808 -05:00 [INF] Request starting HTTP/1.1 GET https://localhost:44357/connect/authorize?response_type=code&client_id=XXX&state=YYY&redirect_uri=https%3A%2F%2Flocalhost%3A4200&scope=ZZZ&code_challenge=MMM&code_challenge_method=S256&nonce=NNN&culture=en&ui-culture=en - - 2023-09-18 01:10:06.840 -05:00 [DBG] Request path /connect/authorize matched to endpoint type Authorize 2023-09-18 01:10:06.859 -05:00 [DBG] Endpoint enabled: Authorize, successfully created handler: IdentityServer4.Endpoints.AuthorizeEndpoint 2023-09-18 01:10:06.859 -05:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeEndpoint for /connect/authorize 2023-09-18 01:10:06.862 -05:00 [DBG] Start authorize request

    There is a lot of information here - cut - but finally there is also the error (which does not prevent user logging in):

    2023-09-18 01:10:06.926 -05:00 [DBG] client configuration validation for client XXX succeeded. 2023-09-18 01:10:06.926 -05:00 [DBG] Checking for PKCE parameters 2023-09-18 01:10:07.021 -05:00 [INF] {"Details":"System.Threading.Tasks.TaskCanceledException: A task was canceled.\r\n at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)\r\n at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)\r\n at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenAsync(CancellationToken cancellationToken, Boolean errorsExpected)\r\n at Oracle.EntityFrameworkCore.Storage.Internal.OracleRelationalCommandBuilderFactory.OracleRelationalCommandBuilder.OracleRelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)\r\n at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)\r\n at Oracle.EntityFrameworkCore.Storage.Internal.OracleExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func4 operation, Func4 verifySucceeded, CancellationToken cancellationToken)\r\n at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable1.AsyncEnumerator.MoveNextAsync()\r\n at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken)\r\n at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken)\r\n at Volo.Abp.IdentityServer.IdentityResources.IdentityResourceRepository.GetListByScopeNameAsync(String[] scopeNames, Boolean includeDetails, CancellationToken cancellationToken)\r\n at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)\r\n at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync()\r\n at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)\r\n at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed)\r\n at Volo.Abp.IdentityServer.ResourceStore.<FindIdentityResourcesByScopeNameAsync>b__34_0(String[] keys)\r\n at Volo.Abp.IdentityServer.ResourceStore.GetCacheItemsAsync[TEntity,TModel](IDistributedCache1 cache, IEnumerable1 keys, Func2 entityFactory, Func3 cacheItemsFactory, String cacheKeyPrefix)\r\n at Volo.Abp.IdentityServer.ResourceStore.FindIdentityResourcesByScopeNameAsync(IEnumerable1 scopeNames)\r\n at IdentityServer4.Stores.IResourceStoreExtensions.FindResourcesByScopeAsync(IResourceStore store, IEnumerable1 scopeNames)\r\n at IdentityServer4.Stores.IResourceStoreExtensions.FindEnabledResourcesByScopeAsync(IResourceStore store, IEnumerable1 scopeNames)\r\n at IdentityServer4.Validation.DefaultResourceValidator.ValidateRequestedResourcesAsync(ResourceValidationRequest request)\r\n at IdentityServer4.Validation.AuthorizeRequestValidator.ValidateScopeAsync(ValidatedAuthorizeRequest request)\r\n at IdentityServer4.Validation.AuthorizeRequestValidator.ValidateAsync(NameValueCollection parameters, ClaimsPrincipal subject)\r\n at IdentityServer4.Endpoints.AuthorizeEndpointBase.ProcessAuthorizeRequestAsync(NameValueCollection parameters, ClaimsPrincipal user, ConsentResponse consent)\r\n at IdentityServer4.Endpoints.AuthorizeEndpoint.ProcessAsync(HttpContext context)\r\n at IdentityServer4.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events, IBackChannelLogoutService backChannelLogoutService)","Category":"Error","Name":"Unhandled Exception","EventType":"Error","Id":3000,"Message":"A task was canceled.","ActivityId":"800000d9-0007-ff00-b63f-84710c7967bb","TimeStamp":"2023-09-18T06:10:07.0000000Z","ProcessId":24860,"LocalIpAddress":"::1:44357","RemoteIpAddress":"::1","$type":"UnhandledExceptionEvent"} 2023-09-18 01:10:07.021 -05:00 [INF] {"Details":"System.Threading.Tasks.TaskCanceledException: A task was canceled.\r\n at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)\r\n at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternalAsync(Boolean errorsExpected, CancellationToken cancellationToken)\r\n at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenAsync(CancellationToken cancellationToken, Boolean errorsExpected)\r\n at Oracle.EntityFrameworkCore.Storage.Internal.OracleRelationalCommandBuilderFactory.OracleRelationalCommandBuilder.OracleRelationalCommand.ExecuteReaderAsync(RelationalCommandParameterObject parameterObject, CancellationToken cancellationToken)\r\n at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken)\r\n at Oracle.EntityFrameworkCore.Storage.Internal.OracleExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func4 operation, Func4 verifySucceeded, CancellationToken cancellationToken)\r\n at Microsoft.EntityFrameworkCore.Query.Internal.SplitQueryingEnumerable1.AsyncEnumerator.MoveNextAsync()\r\n at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken)\r\n at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken)\r\n at Volo.Abp.IdentityServer.IdentityResources.IdentityResourceRepository.GetListByScopeNameAsync(String[] scopeNames, Boolean includeDetails, CancellationToken cancellationToken)\r\n at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)\r\n at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync()\r\n at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)\r\n at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed)\r\n at Volo.Abp.IdentityServer.ResourceStore.<FindIdentityResourcesByScopeNameAsync>b__34_0(String[] keys)\r\n at Volo.Abp.IdentityServer.ResourceStore.GetCacheItemsAsync[TEntity,TModel](IDistributedCache1 cache, IEnumerable1 keys, Func2 entityFactory, Func3 cacheItemsFactory, String cacheKeyPrefix)\r\n at Volo.Abp.IdentityServer.ResourceStore.FindIdentityResourcesByScopeNameAsync(IEnumerable1 scopeNames)\r\n at IdentityServer4.Stores.IResourceStoreExtensions.FindResourcesByScopeAsync(IResourceStore store, IEnumerable1 scopeNames)\r\n at IdentityServer4.Stores.IResourceStoreExtensions.FindEnabledResourcesByScopeAsync(IResourceStore store, IEnumerable1 scopeNames)\r\n at IdentityServer4.Validation.DefaultResourceValidator.ValidateRequestedResourcesAsync(ResourceValidationRequest request)\r\n at IdentityServer4.Validation.AuthorizeRequestValidator.ValidateScopeAsync(ValidatedAuthorizeRequest request)\r\n at IdentityServer4.Validation.AuthorizeRequestValidator.ValidateAsync(NameValueCollection parameters, ClaimsPrincipal subject)\r\n at IdentityServer4.Endpoints.AuthorizeEndpointBase.ProcessAuthorizeRequestAsync(NameValueCollection parameters, ClaimsPrincipal user, ConsentResponse consent)\r\n at IdentityServer4.Endpoints.AuthorizeEndpoint.ProcessAsync(HttpContext context)\r\n at IdentityServer4.Hosting.IdentityServerMiddleware.Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events, IBackChannelLogoutService backChannelLogoutService)","Category":"Error","Name":"Unhandled Exception","EventType":"Error","Id":3000,"Message":"A task was canceled.","ActivityId":"800001e1-0004-f500-b63f-84710c7967bb","TimeStamp":"2023-09-18T06:10:07.0000000Z","ProcessId":24860,"LocalIpAddress":"::1:44357","RemoteIpAddress":"::1","$type":"UnhandledExceptionEvent"} 2023-09-18 01:10:07.021 -05:00 [FTL] Unhandled exception: A task was canceled.

  • User Avatar
    0
    sinan created
    Support Team Angular Developer

    i have tested your case but couldn't produce, it worked fine.

    Maybe you can check the following codes from abp

  • User Avatar
    0
    alexander.nikonov created

    Hi. I've used the Guard you provided and put breakpoint in canActivate method. So when I click "Logout" - I'm being redirected... by the moment the breakpoint is fired, the hasValidAccessToken is true and I'm landed to Home page (instead of Login page of Identity Server). If it is the problem of server-side - like the revocation is not done correctly or some token is not cleared or something else - I ask you to help me sort this out. I've put the fragment of the log from Identity Server above if it could come in handy. Unfortunately, I cannot send the project (it's commercial) or reproduce this on some test project (it would take a while trying to create such project). What I can do is to follow your instructions as for what to check and where.

  • User Avatar
    0
    sinan created
    Support Team Angular Developer

    When i have logged out my hasAccesToken is becomes false. Please make sure your in oauthService logout function works properly.

    Unfortunately If i can't produce it locally i cant help you.

  • User Avatar
    0
    alexander.nikonov created

    I understand. I suspect it could be a back-end issue with Identity Server, so that token revocation is not successful. Could you please tell me how to troubleshoot this? I may send our Identity Server log for the period of logging out, so maybe you would notice the root cause. Here are one (sometimes two) requests which triggers after I click the "Logout" button: Afterwards I'm getting API requests from my component which are not supposed to be run, because I'm logging out - so they produce error 401. And eventually I'm landed to Home page without logging out.

  • User Avatar
    0
    alexander.nikonov created

    UPDATE: please have a look at what's going on during logout at Identity Server, probably would ring some bell to you: https://drive.google.com/open?id=12SCQSi6Je4G9cCTwPoxwf4CzFujMz0Rn&usp=drive_fs

    To me it looks like concurrent calls to Task<IdentityUser> FindAsync (which is eventually cancelled as seen from the attached log) from:

        using AbxEps.CentralTools.AbxUsers;
        using AbxEps.CentralTools.Extensions;
        using AbxEps.CentralTools.Jobs;
        using AbxEps.CentralTools.Sessions;
        using AbxEps.CentralTools.Tenants;
        using IdentityServer4.Models;
        using Microsoft.AspNetCore.Http;
        using Microsoft.AspNetCore.Identity;
        using System;
        using System.Collections.Generic;
        using System.Security.Claims;
        using System.Security.Principal;
        using System.Threading.Tasks;
        using Volo.Abp.Identity;
        using Volo.Abp.IdentityServer.AspNetIdentity;
        using Volo.Abp.MultiTenancy;
        using Volo.Abp.PermissionManagement;
        using IdentityUser = Volo.Abp.Identity.IdentityUser;
        
        namespace AbxEps.CentralTools.IdentityServer.Profile
        {
            public class AbxProfileService : AbpProfileService
            {
                private readonly ITenantRepository           _abxTenantRepository;
                private readonly IAbxUserRepository          _abxUserRepository;
                private readonly ISessionRepository          _abxSessionRepository;
                private readonly IJobRepository              _abxJobRepository;
                private readonly IHttpContextAccessor        _httpContextAccessor;
        
                public AbxProfileService
                (
                    IdentityUserManager                       userManager,
                    IAbxUserRepository                        abxUserRepository,
                    IUserClaimsPrincipalFactory&lt;IdentityUser&gt; claimsFactory,
                    ICurrentTenant                            currentTenant,
                    ITenantRepository                         abxTenantRepository,
                    IHttpContextAccessor                      httpContextAccessor,
                    ISessionRepository                        abxSessionRepository,
                    IJobRepository                            abxJobRepository
                )
                    :base(userManager, claimsFactory, currentTenant)
                {
                    _abxTenantRepository        = abxTenantRepository;
                    _abxUserRepository          = abxUserRepository;
                    _httpContextAccessor        = httpContextAccessor;
                    _abxSessionRepository       = abxSessionRepository;
                    _abxJobRepository           = abxJobRepository;
                }
        
                public override async Task GetProfileDataAsync(ProfileDataRequestContext context)
                {
                    using (CurrentTenant.Change(context.Subject.FindTenantId())) //is invoked a couple of times and probably is invoking Task&lt;IdentityUser&gt; FindAsync too below
                    {
                        await base.GetProfileDataAsync(context);
                        ...
                    }
                }
        
                public override async Task IsActiveAsync(IsActiveContext context)
                {
                    using (CurrentTenant.Change(context.Subject.FindTenantId())) //is invoked a couple of times and invoking Task&lt;IdentityUser&gt; FindAsync below
                    {
                        var abpUser = await UserManager.GetUserAsync(context.Subject);
                        var abxUser = abpUser == null ? null : await _abxUserRepository.GetAsync(abpUser.Id);
        
                        await base.IsActiveAsync(context);
        
                        ...
                    }
                }        
            }
        }
        
    

    and the middleware:

        private async Task OnSessionEndRequestAsync(HttpContext httpContext, IdentityUserManager userManager)
        {
            try
            {
                var user = await userManager.GetUserAsync(httpContext.User); // Cancellation exception!
                ...
            }
            catch(Exception ex)
            {
                _logger.LogError(ex, "Session End handling error");
            }
        }
    

    during logout...

  • User Avatar
    0
    alexander.nikonov created

    Not resolved. Reopening.

  • User Avatar
    0
    sinan created
    Support Team Angular Developer

    hi @alexander.nikonov, we still cannot produce your case.

    could you please tell us the steps of producing the problem in a new project. we would like to help you if we can.

  • User Avatar
    0
    alexander.nikonov created

    Hi. The project is a customized version of ABP project. I don't think it would be possible to do what you offer. Neither I can share the source code - by sending it or demonstrating the screen. Could you please at least make some guesses what could go wrong looking at the attached logs and my comments? Thanks.

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    OK, please share all the logs. let's try to check it.

  • User Avatar
    0
    alexander.nikonov created

    The log has been already attached (Google Drive link above).

  • User Avatar
    0
    liangshiwei created
    Support Team Fullstack Developer

    Hi,

    I have checked the log file.

    But it's hard to find something, you seem to have customized a lot of services.

    I can only give some suggestions.

    Try comment some code to narrow down the problem, for example:

     private async Task OnSessionEndRequestAsync(HttpContext httpContext, IdentityUserManager userManager)
    {
        try
        {
            // comment
            //var user = await userManager.GetUserAsync(httpContext.User); // Cancellation exception!
            ...
        }
        catch(Exception ex)
        {
            _logger.LogError(ex, "Session End handling error");
        }
    }
    
Made with ❤️ on ABP v9.1.0-preview. Updated on November 11, 2024, 11:11