ankit
ankit

Reputation: 61

Unable to unsubscribe observable in app-component

I have created an authentication service in which I check the presence of access token in a local storage. For that I have created an observable which calls a hasValidAccessToken function. The function emits the boolean value.

I make the subscription to that observable in app-component. The issue is I am not able to unsubscribe the subscription in app-component because it never gets destroyed.

authentication.service.ts

 public isAuthenticatedSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(this.hasValidAccessToken());
    public isAuthenticated$: Observable<boolean>;

 isTokenExpired(): boolean {
        const expiresAt = localStorage.getItem("expires_at");
        const now = new Date();
        if (
            expiresAt &&
            parseInt(expiresAt, 10) < Math.floor(now.getTime() / 1000)
        ) {
            return false;
        }
        return true;
    }

    hasValidAccessToken(): boolean {
        if (this.hasAccessToken()) {
            return this.isTokenExpired();
        }
        return false;
    }

    hasAccessToken(): boolean {
        return !!localStorage.getItem("access_token");
    }

    decodeToken(token: string) {
        if (token) {
            return jwt_decode(token);
        }
    }

app.component.ts

this.isAuthenticatedSubscription = this.oauthService.isAuthenticated$.subscribe(
                data => {
                    if (data) {
                        this.router.navigate(["home"]);
                    } else {
                        this.router.navigate(["login"], {
                            state: { data: "session has been expired" }
                        });
                    }
                }
            );

Upvotes: 2

Views: 356

Answers (2)

nash11
nash11

Reputation: 8650

You can use take to make your Observable complete after a set number of emissions.

this.isAuthenticatedSubscription = this.oauthService.isAuthenticated$
    .pipe(
        take(1)
    )
    .subscribe(
        data => {
            if (data) {
                this.router.navigate(["home"]);
            } else {
                this.router.navigate(["login"], {
                    state: { data: "session has been expired" }
                });
            }
        }
    );

Edit: Although this would solve your question, @Dino is right, you shouldn't be unsubscribing from that observable as the login state of your application depends on it. You should rather move this logic to an AuthGuard.

Upvotes: 3

Dino
Dino

Reputation: 8292

You should never unsubscribe from that observable. You are basically recreating the Authentication Guard. Imagine if you do unsubscribe from it, and at one point in your application you lose your authentication token (Logout, or it expires...). Since you've unsubscribed from it, you will still be able to browse the application as this code will never trigger:

 else {
         this.router.navigate(["login"], {
             state: { data: "session has been expired" }
         });
      }

My suggestion is to replace the logic by creating the Authentication Guard I mentioned before. That way you will be able to secure your routes with canActivate, which is also the best practice.

Upvotes: 3

Related Questions