Protecting Routes in Angular 4+ with Subscriptions

When my scenario requires authentication/login of a Windows user upon route activation, I need to check if the user is authenticated. If not, I make a call to the server to generate a token and expect it to return true in order to activate the route. However, this process does not seem to work as intended.

Here's a snippet of code:

auth.guard.ts

import { Observable } from 'rxjs/Observable';
import { CanActivate, Router, ActivatedRouteSnapshot } from '@angular/router';
import { Injectable } from '@angular/core';
import { AuthService } from './../data/auth.service';
import { TokenService } from '../auth/token.service';
import { of } from 'rxjs/observable/of';

@Injectable()
export class AuthGuard implements CanActivate {

    constructor(
        private loginService: AuthService,
        private tokenService: TokenService,
        private router: Router) {        
    }

    canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
        const operation = route.data.operation;

        if (!this.tokenService.isAuthenticated() && !this.login()) {            
            console.log('canActive1: ', false);
            return of(false);
        }

        console.log('canActive: ', true);
        return of(true);
    }    

    login() {
        this.loginService.getToken().subscribe((user) => {
            console.log('token returned..');
            if (user && user.token) {
                this.tokenService.storeToken(user.token);
                return true;
            }
            return false;
        });
    }
}

Console:

  • canActivate1: false
  • token returned..

Answer №1

Asynchronous code execution results in the console log appearing later in this scenario. Additionally, the issue at hand is attempting to return data from within a subscribe method, which is not a valid operation. Instead, it is necessary to return an Observable.

canActivate(route: ActivatedRouteSnapshot): Observable<boolean> {
    const operation = route.data.operation;

    return this.login().map(bool => {
      if(!bool && !this.tokenService.isAuthenticated()) {
        return false;
      }
      return true;
      }
    })
}    

login() {
  // Use map function instead of trying to directly return from subscribe
  return this.loginService.getToken().map((user) => {
    console.log('token retrieval successful..');
    if (user && user.token) {
      this.tokenService.storeToken(user.token);
      return true;
    }
    return false;
  });
}

Answer №2

Make sure to include the return statement in your login() method so that it returns a boolean value. Here's an updated version.

login(): Observable<boolean> {
    return this.loginService.getToken().take(1).map((user) => {
        console.log('token received..');
        if (user && user.token) {
            this.tokenService.storeToken(user.token);
            return true;
        } else {
            return false;
        }
    });
}

Answer №3

To begin with, it is essential to retrieve the outcomes of the login() operation by utilizing login() : Observable<boolean, as has been noted by others.

Furthermore, the issue seems to lie in this particular line:-

if (!this.tokenService.isAuthenticated() && !this.login())
.

I suspect that when you invoke tokenService.isAuthenticated(), you are likely checking whether a user possesses a valid token or not, isn't that correct?

Hence, if someone does not have a token, then

this.tokenService.isAuthenticated()
would yield false causing your
if (!this.tokenService.isAuthenticated() && !this.login())
statement to also return false due to the application of the logical AND operator.

Once the login() function gets executed and sets the token, however, until then, your tokenServiceisAuthenticated() function will continue to return false, preventing the activation of the routes.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Managing unpredictable fields within a TypeScript interface Let me known if you need further assistance

Currently, I am developing a web application using Angular and encountered an issue with the JSON data returned by a service call. The problem arises when the mapped JSON contains an Object within one of the fields with unpredictable content. How can I han ...

The type 'NodeList' does not include the property 'forEach'

Recently, I incorporated ng2-dragula to enable Drag and Drop functionality in my Table. this.dragulaService.drop.subscribe(value => { let questions_group = value[3] as HTMLTableRowElement let SectionTwo:Array<string> = []; l ...

The interface 'HTMLIonIconElement' is not able to extend both 'IonIcon' and 'HTMLStencilElement' types at the same time

After upgrading my Angular Ionic app to use Angular v13 from Angular 12 with the command ng update, I encountered errors preventing me from running the application successfully. [ng] Error: node_modules/ionicons/dist/types/components.d.ts:66:15 - error TS2 ...

The installation of @angular/router seems to have encountered an error

I am attempting to update my @angular/router dependency from version 2.0.0 to 3.0.0-alpha.7. I have included it in my package.json file. { "name": "angular2-quickstart", "version": "1.0.0", "scripts": { "start": "tsc && concurrently &bs ...

Develop a prototype function in ES6/ESNext with a distinct scope (avoid using an inline function)

Consider the following example: class Car { constructor(name) { this.kind = 'Car'; this.name = name; } printName() { console.log('this.name'); } } My goal is to define printName using a differe ...

A step-by-step guide to customizing the Material UI Chips delete SVG icon to appear in white color through

Using a Material UI component, I added a custom class to my chip. Attached is a screenshot showing what I mean. Currently, I am attempting to change the color of the cross button to white. After inspecting the element, I discovered that it is an SVG ico ...

If the user clicks outside of the navigation menu, the menu is intended to close automatically, but unfortunately it

I have a nav file and a contextnav file. I've added code to the nav file to close the navigation when clicking outside of it, but it's not working. How can I ensure that the open navigation closes when clicking outside of it? Both files are in ts ...

Uploading files in Angular application

I'm facing some minor issues with file uploads for the first time. My project consists of an Angular 7 app and a NodeJS (express) backend. I have successfully uploaded images through the Angular page and stored them with the correct format in NodeJS. ...

Interfaces and Accessor Methods

Here is my code snippet: interface ICar { brand():string; brand(brand:string):void; } class Car implements ICar { private _brand: string; get brand():string { return this._brand; } set brand(brand:string) { this. ...

How can I convert Typescript absolute paths to relative paths in Node.js?

Currently, I am converting TypeScript to ES5/CommonJS format. To specify a fixed root for import statements, I am utilizing TypeScript's tsconfig.json paths property. For instance, my path configuration might look like this: @example: './src/&ap ...

Guide on restricting object keys to a specific set of strings in typescript

I am dealing with an API that has the ability to return one of these options: { fill: 'string'} or {stroke: 'string'} or {effect: 'string'} The key type I have established is as follows: type StyleKeyType = | 'fill&a ...

Retrieving Information from an Angular 2 Component

Struggling to figure this out, I am attempting to dynamically add user video data that includes a video URL. My goal is to access the data from the component so I can use it in my HTML. I've attempted the following approach. app.component.ts import ...

Incorrect naming in JSON response from REST API service

Currently, I am in the process of developing a web application using AngularJS and TypeScript with a Netbeans RESTful backend. I have created a TypeScript interface for a vendor which looks like this: interface Vendor { vendorno: number, name: str ...

Find what you're looking for by exploring the search results inside the text box marked

update1: Appreciate your response! However, I am facing an issue where typing 'h' displays a set of values. When selecting a value, it should appear in the text box with a cross symbol, similar to how tags are edited on StackOverflow. I am work ...

Modify the appearance of certain text within an input field

I need some help adding style to specific text within an input field. For example, if the user types in "Hello world" and the special text is "world" I want to achieve this result: https://i.sstatic.net/Ozd6n.png This is what my HTML code looks like: & ...

Working with Yarn and Webpack: Incorporating a TypeScript .ts file from an external Node directory not controlled by Yarn/Webpack

I am currently working on a k6 project for load testing, utilizing Yarn and Webpack. This project is stored within a sub-folder of a larger repository that primarily uses npm Node modules. My goal is to access a secret from AWS's Secrets Manager in t ...

How to include a sub-route in Angular after adding parameters?

In this scenario: 'www.xyz.com/#/indutry/1/subIndustry/2/subSubIndustry/3' I am looking to implement this structure in my Parent route file. How can I achieve this using ForRoot? ...

The benefits of exporting a component from one module and using it in another module

After putting in long hours trying to figure this out on my own, I've finally decided to seek help from the community. Thank you in advance for any assistance! I have a Web Projects Module that utilizes a Webpage Component. Within the Webprojects Mod ...

How can you update state with useState in React and perform additional actions in an onChange event?

I'm facing an issue with a component that includes another component (from headlessui/react) defined like this: export default function MyComponent(props) { const [selectedState, setState] = useState(''); return ( <div> & ...

Storing an array of objects in local storage is not working in Angular 9

I've encountered an issue trying to save an array of JSON objects into local storage, and I'm puzzled as to why it's not functioning correctly. Despite utilizing localStorage.setItem('comparisons', JSON.stringify(setComparisons)), ...