ABP 7.0.1 / Angular
My home page shows some information to an authenticated user via API calls. If the user logs out - these methods need not to be invoked anymore.
Seems like I've tried all possible ways - and it still DOES call those methods with "Not authorized (401)" error from server after I click "Logout" button. I also have tried to call Subscription$.unsubscribe()
while logging out, but it still does not work.
Another question: I can logout from any page, not just Home page. There are plenty of API call subscriptions on each of them. How am I supposed to unsubscribe from all such calls with minimal code changes??
Here is the piece of the code of my Home page:
ngOnInit() {
this.oAuthService.events
.pipe(
filter(event => event?.type === 'logout'),
tap(() => {
this.logout$.next(null); //those are called, but API calls are still invoked
this.logout$.complete();
}))
.subscribe();
this.homeService.getNewsForHomePage()
.pipe(filter(() => this.configStateService.getDeep('currentUser.isAuthenticated')), takeUntil(this.destroy), takeUntil(this.logout$))
.subscribe((newsResponse) => {
...
});
this.homeService.getUrlsForHomePage()
.pipe(filter(() => this.configStateService.getDeep('currentUser.isAuthenticated')), takeUntil(this.destroy), takeUntil(this.logout$))
.subscribe((newsUrlParameterResponse) => {
...
});
}
ngOnDestroy(): void {
this.destroy.next(null);
this.destroy.complete();
}
Moreover - when I am already at this page (where this.configStateService.getDeep('currentUser.isAuthenticated')
is supposed to be false
, I guess):
the API calls are still invoked.
36 Answer(s)
-
0
Hello alexander.nikonov ,
Can you please try to add this code
import { SubscriptionService } from '@abp/ng.core'; constructor(private subscriptionService: SubscriptionService) this.subscriptionService.closeAll() // add this line in logout event
Please let me know if this helps you or you may provide some steps to reproduce the issue as I am unable to reproduce it.
Thank you, Anjali
-
0
To avoid compile errors, I had also to add
providers: [SubscriptionService]
to app.component.ts - hope it is a correct string. But anyway -closeAll()
call did not help: i'm still getting the same 401 error on logout. It happens to any method which is obviously being loaded at the moment of logout. The methods are pretty typical://some component this.fineDigitLoading = true; this.fineDigitsService .getById(fineDigit.ordinanceId, fineDigit.number, fineDigit.subdivision, fineDigit.version) .pipe( finalize(() => (this.fineDigitLoading = false)), takeUntil(this.unsubscriber$) ) .subscribe((state: FineDigits.FineDigit) => { ... }); //fineDigitsService getById( ordinanceId: number, number: string, subdivision: string, version: number): Observable<FineDigits.FineDigit> { return this.restService.request<void, FineDigits.FineDigit>({ method: 'GET', url: `/api/md/fine-digits/${ordinanceId}/${number}/${version}/${subdivision === null ? '' : subdivision}`, }, { apiName: this.apiName }); } //base class for any component ngOnDestroy(): void { this.ngOnDestroyInner(); this.unsubscriber$.next(null); this.unsubscriber$.complete(); }
Probably the latter approach is the reason? When the user is logging out - the current component is not being destroyed, so
ngOnDestroy
is not being triggered. If this is the case - please let me know the recommended approach to unsubscribe from all API calls by adjusting the code in base component class - adding 'logout' event handling which is now done in app.component.ts? Suprisingly I did not see any special handling for such scenario in ABP components...UPDATE: I've added this to Base component constructor, but stil keep getting the same 401 errors:
this.oAuthService.events .pipe(filter(event => event?.type === 'logout'), takeUntil(this.unsubscriber$)) .subscribe(() => { this.unsubscriber$.next(null); this.unsubscriber$.complete(); });
The above part IS triggered after I click "Logout" link. But after this my server API calls are still made. How it's possible if I use
takeUntil(this.unsubscriber$)
for all API calls? -
0
Hello alexander.nikonov,
import { AuthService } from '@abp/ng.core'; constructor(private authService: AuthService) ngOnInit() { if (this.authService.isAuthenticated) //like -> this.configStateService.getDeep('currentUser.isAuthenticated') { this.homeService.getNewsForHomePage() this.homeService.getNewsForHomePage() //other code and API which you are calling at initial... } }
Could you please try with this code
.pipe(filter(() => this.configStateService.getDeep('currentUser.isAuthenticated'))
This condition will not work as it's not authorized, so will not execute the next statement.
HomeComponent
get calls whether user logged in or not, so without get authorized it will give error.please do let me know if this helps you.
Thank you, Anjali
-
0
Hi Anjali,
unfortunately, it did not help - still getting 401 on logout in one of two methods. Suprisingly, this method has another problem - it is for some reason invoked twice, even though in debug I just got it triggered once.
I'm attaching the Home module code - probably you will figure out what is wrong. It is actually not my code, so I am not sure I would be able to reply you all the questions. But I'm ready to assist in any possible way.
Thank you. Home page
Actually one more thing confuses me: after I do log out - I'm redirected to this page ("Home"): instead of this: So I need to click "Login" again to be redirected to the second dialog. But the routing in the app is the same as in test ABP app...
-
0
Hello alexander.nikonov,
After logout it will redirect to your landing page i.e. Home and if you are calling any api's inside home component which is not authorized then this 401 error occurs for that particular api's. For reproduce the same Issue I added 1 api in Home component. I am calling that api in ngOnInit ( initial method ) with login it is working fine. But if I logout then we are facing 401 status in network tab the reason is we are calling api's without authorized.
I tried with your code to call api's which is inside ngOnInitInner which was giving me error without
if (this.authService.isAuthenticated) { }
blockplease check the Initiator column for which api's you are getting 401 error and try to add those api calls inside this
if (this.authService.isAuthenticated) { }
blockWhen I tried with
if (this.authService.isAuthenticated) { }
block , that api will not get call and hence not get any error.protected ngOnInitInner() { if (this.authService.isAuthenticated) { this.homeService.getNewsForHomePage() .pipe(take(1)) .subscribe((newsResponse) => {... }); this.homeService.getUrlsForHomePage() .pipe(take(1)) .subscribe((newsUrlParameterResponse) => {... }); } }
If the value by
authService.isAuthenticated
is not givingfalse
on logout then please try thisif (this.configStateService.getDeep('currentUser.isAuthenticated')) {}
please do let me know if it helps you,
Thank you, Anjali
-
0
Hi Anjali,
i've run debug again.
when I click "Logout" on Home page - the
onInit
method IS get triggered with expectedthis.authService.isAuthenticated
== false. But prior to that, I'm already getting 401 error request (sometimes - only this one request, sometimes - another one too): The "initiator" there is shown as "zone.js", later on it's getting cleared. So it's still looking confusing.our users complain about being redirected to landing page "Login" dialog, then - to Identity Server "Login" dialog (as shown on second screenshot). The request is to be redirected to the second "Login" dialog straight away... How to do that?
-
0
Hello alexander.nikonov,
Please try to add this code in
app.component.ts
import { AuthService } from '@abp/ng.core'; import { Router } from '@angular/router'; import { OAuthService } from 'angular-oauth2-oidc'; import { filter, tap } from 'rxjs'; constructor(private oAuthService: OAuthService , private router: Router, private authService: AuthService) { this.oAuthService.events .pipe(filter(event => event?.type === 'logout'), tap(() => { this.authService.logout().subscribe(() => { this.authService.init() this.authService.navigateToLogin(); }); })).subscribe(); }
please do let me know if this helps you.
Thank you, Anjali
-
0
Hi Anjali.
Don't want to upset you, but it's even worse now: the mentioned API call is still GET called after I press 'Logout'. But now after I'm doing this - I never gets logged out: after several chaning screens I'm eventually redirected to the root page, i.e. Home page...
-
0
Hello alexander.nikonov,
Thank you for the update and apologies for the inconvenience.
We understand your problem and looking for better solution, please allow us sometime to get back on the same.
Thank You. Anjali
-
0
Thank you! Will be waiting. Also please suggest me how to logout directly to Identity Server Login box without visiting intermediate root page (Home page). I was trying to find the way (like editing SignOut URLs in Identity Server settings via admin UI page), but it did not bring me anywhere. I believe this used to work properly in some previous ABP version. Maybe we have unintentionally have changed some relevant setting.
-
1
-
0
-
0
Hi
Now after logout i guess your api will also not get called, can you check please?
-
0
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.
-
0
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 becausethis.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.
-
0
Hello alexander.nikonov,
I have looked into your code provided above. I will suggest to add
ngOnDestroy
method to callthis.unsubscriber$.next();
andthis.unsubscriber$.complete();
just like in below code.Please check with this code if find helpful.
this.oAuthService.events .pipe( takeUntil(this.unsubscriber$), // add this line also ) .subscribe(event => { if (event?.type === 'logout') { console.log('Logout event received'); this.unsubscriber$.next(); } }); ngOnDestroy() { this.unsubscriber$.next(); this.unsubscriber$.complete(); }
Please do let us know if anything else needed.
Thank you, Anjali
-
0
Hi Anjali.
First of all,
ngOnDestroy
with the suggested code already present in the base classHomeComponent
is inherited from. Nevertheless, I've addedngOnDestroy
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 actionunsubscriber$
needs to received propernull
value and becompleted
: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$
asReplaySubject
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... -
0
Hi
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.
if (this.configStateService.getDeep('currentUser.isAuthenticated')) { //authenticated code here }
you don't have to place these
if conditions
in any component if you have used authguard in the routing. so you have to put thisif condition
only on app.component.ts. -
0
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.
-
0
Hi
any Component except
app.component.ts
cannot get invoked unless they are being Activated byauthguard
is youComponentHHH
haveauthguard
inapp.routing.module.ts
or in the registered router module? -
0
-
0
Hi
I am not to reproduce this on my end, is you component ngOnit being invoked when you are not logged in?
-
0
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.
-
0
Hello alexander,
I'm confused little bit there are a lot of different questions 🙂 Can you please explain your latest problem step by step what is your goal and what's the current problem to reach your goal ?
-
0
Hi, Masum. I have encountered two issues, which may be related. However, I have created a separate ticket for the second problem. This particular ticket addresses the following concern: I am unable to find a proper method to unsubscribe from API calls within my components after logging out. Despite logging out, the API calls continue to be invoked. I apologize if this thread appears confusing. I have been updating the ticket with additional information as I uncover more details and conduct further experiments in an attempt to resolve the issue.