Angular 16 HttpClient post request with asynchronous action

Here I am working on integrating JWT in Angular with a .Net Core API. When I start my Angular server and launch the application, I encounter the following scenarios:

  • Attempting with correct credentials initially fails, but retrying allows it to work.
  • Trying with incorrect credentials at first fails, then retrying again causes failure again.
  • Starting with correct credentials fails, trying again with incorrect ones leads to successful login.

I want to clarify that the success of logging in depends on the JWT token already being stored in localStorage as a value. This means that at a certain point, the API call works; it's just not validated correctly at the right time for some reason.

To dig deeper into this issue, here is the relevant code snippet:

This is the AuthService component:

import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { User } from 'src/app/models/User';


const httpOptions = {
  headers: new HttpHeaders({
    'Content-Type':'application/json;charset=UTF-8'
  })
}


@Injectable({
  providedIn: 'root'
})
export class AuthService{

  public loggedIn = false;

  constructor(private httpClient:HttpClient) { }

  login(user: User){
    // Set the User object with the corresponding credentials as the request body
    const body = JSON.stringify(user);
    // Send the post request with its corresponding headers and body
    this.httpClient.post<any>("https://localhost:7054/login", body, httpOptions)
    .subscribe(data => {
      // Here, the data received from the API is known (as I developed it myself):
      // it will only contain the token if successful, and will return a bad request
      // with a blank token if it fails, so the only scenario where the token may
      // be valid is through a HTTP 200 response.
      localStorage.setItem("token", data.token);
    });
  }
}

And this is the HomeComponent.ts file:

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { AuthResponse } from 'src/app/models/AuthResponse';
import { User } from 'src/app/models/User';
import Swal from "sweetalert2";
import { AuthService } from 'src/app/services/auth/auth.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})

export class HomeComponent {
  user: User = new User();

  constructor(private router: Router, private authService:AuthService) {
  }

  token: AuthResponse = new AuthResponse();

  //Login method
  login(username: string, password: string) {

    // - User authentication
    // The verification occurs in the backend for security purposes.

    // Validate credentials are not blank
    if(username != "" || password != ""){

      // Create a new User object, which will be passed to the API as request body
      let user = new User();
      user.email = username;
      user.pass = password;

      // Call the auth service
      this.authService.login(user);

      // If the previous operation went well, the token must be stored, and we should be able to log in
      if(localStorage.getItem("token")){
        this.router.navigate(['/home']);
      }
      // If the request returns nothing, the credentials are incorrect, therefore an error alert will be sent, plus the token is set to blank
      else{
        this.sendError("Credentials are incorrect.")
        localStorage.setItem("token", "");
      }
    }
    else{
      // If the credentials are blank, therefore an error alert will be sent, plus the token is set to blank
      this.sendError("Please type your credentials.")
      localStorage.setItem("token", "");
    }
  }

  sendError(error:string){
    Swal.fire({
      icon: 'error',
      title: 'Error',
      text: error,
    })

  }
}

If someone could offer insight into why this unexpected behavior is occurring, I would greatly appreciate it. There seems to be something missing or incorrect in my implementation, but I can't pinpoint it.

Thank you for any assistance provided.

I've attempted using interceptors to handle the response but they haven't resolved the issue related to the strange behavior.

I've also tried enforcing token validation, but it doesn't seem to have an impact since the token appears to get cached between attempts.

Answer №1

There are multiple issues that need attention.

Starting with this section:

  if(localStorage.getItem("token")){
    this.router.navigate(['/home']);
  }
  // If the request returns nothing, the credentials are incorrect, therefore an error alert will be sent, plus the token is set to blank
  else{
    this.sendError("Credentials are incorrect.")
    localStorage.setItem("token", "");
  }

The problem lies in the else statement where the token is being set. This results in the app redirecting to the home route every time 'login' is clicked, regardless of successful login status.

Moreover, the code for login is asynchronous while the check is synchronous. This means the token check could occur before the login function finishes execution.

 // Call the auth service
  this.authService.login(user);
  
  // If the previous operation went well, the token must be stored, and we should be able to log in
  if(localStorage.getItem("token")){

A potential solution could involve having the authService return an Observable and processing the token using a pipe operator instead of subscribe() method.

login(user: User){
    const body = JSON.stringify(user);
    return this.httpClient.post<any>("https://localhost:7054/login", body, httpOptions)
    .pipe(tap(data => {
      localStorage.setItem("token", data.token);
    }));
  }

You can enhance this by subscribing to the Observable in your component code to ensure proper handling after login() completes:

  // Call the auth service
  this.authService.login(user).subscribe(() ==> {
     if(localStorage.getItem("token")){
       this.router.navigate(['/home']);
     } else{
       this.sendError("Credentials are incorrect.")
       localStorage.setItem("token", "");
     }

  });
  

It's advisable to handle errors based on server-received error codes rather than token existence checks.

Note: The code shared is hypothetical and untested but offers an improved approach to address the identified issues.

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

An array that solely needs a single element to conform to a specific type

While I was pondering API design concepts, a thought crossed my mind. Is it feasible to define a type in this manner? type SpecialArray<Unique, Bland> = [...Bland[], Unique, ...Bland[]]; However, the error message "A rest element cannot follow anoth ...

Issue encountered while attempting to utilize the useRef function on a webpage

Is it possible to use the useRef() and componentDidMount() in combination to automatically focus on an input field when a page loads? Below is the code snippet for the page: import React, { Component, useState, useEffect } from "react"; import st ...

Angular Service Worker enhancements across various domains

Scenario Our team is currently developing an Angular application that is accessible through multiple domains. Depending on the domain, the app will display different colors and content, but it is essentially the same Angular application. To enhance perfo ...

Issue: Incorrect hook usage. Hooks are designed to be used within the body of a function component. This error may occur due to one of the following reasons: 1

I've reviewed similar questions and attempted to apply the solutions provided, but it seems I'm missing something specific to my situation. My goal is to streamline my code by importing headers from a utils file and using them across different AP ...

res.redirect() endlessly seeking but never loading

Currently, I am facing an issue with redirecting a user to a page after verifying their credentials using a post method. It seems that the page continues to attempt redirection but never actually loads. app.post('/login', function (req, res) { ...

What is the significance of setting multi:true in Angular 4 providers?

Utilizing HTTP_INTERCEPTORS in angular4 involves creating a HttpServiceInterceptor class that implements the HttpInterceptor interface and defines the intercept method. To register the provider for HTTP_INTERCEPTORS, the following code is used: providers: ...

Guide to Setting Up "Remember Me" Login for Users in Angular

I am having trouble setting the 'Remember Me' option on my Login page. I tried using localstorage session but it seems like something is missing in the service file that's causing it not to respond properly. I attempted to follow a guide on ...

Is it possible to align the radio-button label above the radio-button in Angular Material 2?

Currently, I am utilizing angular material 2 (material.io) and attempting to position the label of the md-radio-button above the radio button itself. Here's an illustration: View Radio Buttons The official documentation mentions that you can easily ...

define a variable within a v-for loop

Example of Code <div v-for="item in dataItems"> <div v-if="enableEdit"> <input type="text" v-model="name"> </div> <div v-else> {{name}} </div> <button @click="enableEdit = true">click</button> This ...

Guide to populating a list in a resolver during navigation and storing the initial item in the ngrx store

I recently incorporated ngrx into my application to manage the data centrally. However, I am facing challenges with loading the initial data and navigating to a default value. This has raised questions regarding the best approach to utilizing ngrx while re ...

The Angular ViewportScroller feature appears to be malfunctioning in the latest release of Angular,

TestComponent.ts export class TestComponent implements OnInit, AfterViewInit { constructor( private scroller: ViewportScroller, ) {} scrollToAnchor() { this.scroller.scrollToAnchor('123456789'); } } HTM ...

The parameter type 'IScriptEditorProps' does not accept arguments of type 'string'

After trying numerous solutions, I decided to integrate this script editor into a SharePoint site. However, when attempting to implement it, I encountered an issue with the constructor lacking arguments. Despite my efforts to troubleshoot, I have been unab ...

Steps for sending data from Angular2 or Angular4 to a Node.js server and saving it in a MySQL database:1. In your

I've scoured the depths of the internet on Google but have come up empty-handed in finding a reliable working example. Any assistance would be greatly appreciated as I am relatively new to Angular2 (angular4). My goal is to have my angular2 applicati ...

In Angular, the process of duplicating an array by value within a foreach function is not

I have been attempting to duplicate an array within another array and make modifications as needed. this.question?.labels.forEach((element) => { element["options"] = [...this.question?.options]; // I've tried json.stringify() as wel ...

Structuring an Angular 2 Project

Embarking on a new project in Angular2, I find myself pondering the optimal structure for an Angular2 application. Imagine I have various pages such as home, auto-galleries, nearest-galleries, brands, cars, and selected-car. The navigation sequence could b ...

Refreshing the Mat Dialog content when removing items in Angular Material

I have successfully implemented a mat dialog table with 3 columns - name, email, and delete icon. When the user clicks on the delete icon, it prompts a confirmation message to confirm the deletion. Upon confirming, the item is removed from the database. Ho ...

Is there a method to incorporate a scroll event into the ng-multi-selectdropdown, a npm package?

Query: I need to incorporate a scroll event in the given html code that triggers when the div is scrolled. However, I am facing issues with implementing a scroll event that works when the elements are being scrolled. <ng-mult ...

The 'asObservable' property is not present on the type '() => any'.ts(2339)

Error: Property 'asObservable' does not exist on type '() => any'.ts(2339) Error: Property 'subscribe' does not exist on type 'Subscription'. Did you mean 'unsubscribe'?ts(2551) Error: Property 'sub ...

Issue occurs where the system is unable to recognize a defined variable, despite it being clearly defined

I keep encountering an error message stating that my variable is not defined, even though I have clearly defined it just a few lines above where the error occurs. The reason behind this error is baffling to me, as I cannot identify any potential triggers ...

Return either the promise or the actual object inside the switchMap function

From the coding tutorial on Angular, my goal is to utilize the hero-details.component for updating and creating hero objects. To achieve this, I added a route without an ID specified. Route { path:'detail/:id', component:HeroDetailCo ...