Activities of "alexander.nikonov"

Our solution is ALREADY a very customized (and we have not used dynamic permissions so far, I don't know what would turn out from this), so I am not afraid to experiment with static permissions. But anyway, I'd like to know the place where I can assign my per-tenant permissions. It needs to be a part of solution and invoked once after the user has been authorized. It must not happen in Identity Server project. As I see it, using a middleware is a bad choice, because the middleware Invoke method is get invoked on each request. Instead, I need to make it only once.

I have already overridden StaticPermissionDefinitionStore (for other task), so I can extend it with some new functionality. But I need to understand how should I implement the task. The aim is the following: there's configurable parameter in the database which can be different depending on the tenant and other things and in fact it determines the list of permissions. So I need to read this parameter once the user is authenticated and his current tenant is known. After this, I need to add just read permissions to the rest of permissions (and be able to assign them to roles as usual). I know there are dynamic permissions in ABP, but I'd prefer not to make use of them.

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.

I do not have ngOnInit override from component's base class. So I put the breakpoint in the base class ngOnInit method. And this method is only invoked when the user is authenticated.

They have it in the App Routing module: The component routing module does not have it: UPDATE: to save your time a bit, I've duplicated "canActive" in my Component's routing module. But the problem did not go away.

currently app.component.ts is the bootstraped component https://angular.io/guide/bootstrapping so if you place any api call in there it will be called so you have to use conditional operator there to calling api when the state is not authenticated.

Sorry, but i didn't get it. I have a lot of different components like the one below - with a bunch of API calls triggered based on different conditions - button clicks, subscriptions, route parameter values, etc.:

    @Component({ ... })
    export class ComponentHHH extends BaseComponent {
    
        //Condition method can be invoked based on various conditions in unknown moment of time
        ConditionMethodAAA() {
            ....
            this.apiServiceXXX.apiMethodYYY(...).pipe(takeUntil(this.unsubscriber$))
                .subscribe((response) => { ... });
            ....
            this.apiServiceZZZ.apiMethodAAA(...).pipe(takeUntil(this.unsubscriber$))
                .subscribe((response) => { ... });
            ....
        }

        ConditionMethodBBB() {
            ....
            this.apiServiceDDD.apiMethodSSS(...).pipe(takeUntil(this.unsubscriber$))
                .subscribe((response) => { ... });
            ....
        }
    }

If I activate another component in the menu - this.unsubscriber$ (property in "BaseComponent" class) receives null and completes. Due to this and to using "takeUntil", no matter which API calls were active, they all will be removed. I've tried to subcribe to "logout" event in "BaseComponent" and do the same I do with this.unsubscriber$ in "ngOnDestroy". However this does not work - API calls keep being invoked. Even more now: logout works only from Home page (probably these two things are unrelated, but I am not sure). I have not found explanation to this.

So you suggest to summon "app.component.ts" to resolve the problem. But I cannot figure out, how it can help here: there is no connection between AppComponent and another components API calls.

Hi Anjali.

First of all, ngOnDestroy with the suggested code already present in the base class HomeComponent is inherited from. Nevertheless, I've added ngOnDestroy here for test, too.

Also I've replace this.oAuthService.events with your suggestion.

After pressing "Logout" button I'm getting "Logout event received" message. But API call is STILL invoked as always. There has to be explanation WHY it is called though.

P.S. Please hold on - I want to check something...

UPDATE: I made some experiments. Good news: In the given specific case the API call I mentioned before was also invoked by other service - this was the reason it "resurrected" all the time after logout; Bad news: In a common case, only using if (this.authService.isAuthenticated) check or similar like you suggested helps resolving the initial problem. However, it is not a good approach: we have hundreds of API calls from hundreds of components. It is not a way to go to add hundreds of "if". Instead, it is supposed that for both component destroying and user logout action unsubscriber$ needs to received proper null value and be completed:

this.apiService
  .apiCall(...)
  .pipe(
    finalize(() => (...)),
    takeUntil(this.unsubscriber$) //this has to be enough for all cases
  )
  .subscribe((...) => {
      ...
  });

The problem is that for some reason using logout event does not help to resolve this issue. I use this in Component base class (no matter, "tap" or "subscribe" actually) and this block DOES get triggered, however eventually API is still invoked for unknown for me reason:

  ngOnInit(): void {
    this.oAuthService.events
      .pipe
      (
        filter(event => event?.type === 'logout'),
        takeUntil(this.unsubscriber$),
        tap(() => {
          this.unsubscriber$.next(null);
          this.unsubscriber$.complete();
        })
      )
      .subscribe();
      

I've tried to make unsubscriber$ as ReplaySubject and create its new with buffer == 1, but it does not change the things.

I am confused even more now: it appeared that logging out works only for Home page: if I take any page - even the ABP page - after clicking "logout" button the user is eventually redirected to Home page again without landing at Identity Server Login page: and then - all the rest our services required for the page are loaded as usual... And this is workflow when I click "Logout" on Home page: Afterwards I'm redirected to Identity Server Login page as expected. All the relevant pages have canActivate: [AuthGuard] It's absolutely strange given that "Logout" functionality is the same for all pages, i.e. the same code has to be invoked. Do you have any clues? If required, I will create a new ticket for this issue...

I had additional experiment. Please have a look:

I've modified ngOnInit a bit:

this.oAuthService.events
  .pipe(
    filter(event => event?.type === 'logout'),
    tap(() => {
      this.unsubscriber$.next(null);
      this.unsubscriber$.complete();
      console.log('unsubscriber null!');
    }))
    .subscribe();

if (this.authService.isAuthenticated) {
  this.homeService.getNewsForHomePage()
    .pipe(tap(() => console.log('getNewsForHomePage still invoked')), takeUntil(this.unsubscriber$))
    .subscribe((newsResponse) => {
      this.newsForHomePage = newsResponse.items;
    });
  this.homeService.getUrlsForHomePage()
    .pipe(tap(() => console.log('getUrlsForHomePage still invoked')), takeUntil(this.unsubscriber$))
    .subscribe((newsUrlParameterResponse) => {
      this.urls = newsUrlParameterResponse.items;
      this.urlDigimedia = this.urls.filter(i => i.columnValue === 'DM')[0]?.stringValue;
      this.urlMasterData = this.urls.filter(i => i.columnValue === 'MD')[0]?.stringValue;
    });
}

I got 'unsubscriber null' message instantly after clicking "Logout" button. Despite this, shortly after, the method was still being invoked (getUrlsForHomePage matches '/api/ct/central-tool/parameters/values' API route). I cannot explain what's going on: The messages "getNewsForHomePage still invoked" and "getUrlsForHomePage still invoked" were never shown - obviosly because this.authService.isAuthenticated was false by the moment.

I thought it could have something to do with using guards. But even if I remove guard for Home page, the given API call is still invoked.

Hi

Now after logout i guess your api will also not get called, can you check please?

The API is still called though - after I click Logout, but before actual logging out is complete.

Hi,

you can directly redirect to login page by attaching you root page or home component a authguard.

Great! Thank you so much - this part is now resolved.

Showing 81 to 90 of 276 entries
Made with ❤️ on ABP v9.0.0-preview Updated on September 20, 2024, 08:30