Redirecting to login on browser refresh in Angular using Firebase's canActivate feature

An Angular 5 authentication application using angularfire2 and Firebase has been developed. The app functions correctly when navigating through in-app links. However, an issue arises when refreshing the browser, as it redirects back to the Login page even after a successful login with Firebase. The 'allowed' status always returns false despite being true.

AuthInfo Class

export class AuthInfo {  
    constructor(public $uid: string) {}

    isLoggedIn() {
        return !!this.$uid;
    }
}

AuthService Class

import { Injectable } from '@angular/core';
import { Observable, Subject, BehaviorSubject } from 'rxjs/Rx';
import { AngularFireAuth } from 'angularfire2/auth';
import { AuthInfo } from '../security/auth-info';
import { Router } from '@angular/router';
import * as firebase from 'firebase/app';

@Injectable()
export class AuthService {
  static UNKNOWN_USER = new AuthInfo(null);
  authInfo$: BehaviorSubject<AuthInfo> = new BehaviorSubject<AuthInfo>(AuthService.UNKNOWN_USER);

  constructor(private afAuth: AngularFireAuth, private router: Router) {}
    login(email, password): Observable<AuthInfo> {
        return this.fromFirebaseAuthPromise(this.afAuth.auth.signInWithEmailAndPassword(email, password));
    }

    // signUp(email, password) {
    //  return this.fromFirebaseAuthPromise(this.afAuth.auth.createUserWithEmailAndPassword(email, password));
    // }

    fromFirebaseAuthPromise(promise): Observable<any> {
        const subject = new Subject<any>();
        promise
              .then(res => {
                    const authInfo = new AuthInfo(this.afAuth.auth.currentUser.uid);
                    this.authInfo$.next(authInfo);
                    subject.next(res);
                    subject.complete();
                },
                err => {
                    this.authInfo$.error(err);
                    subject.error(err);
                    subject.complete();
                });
        return subject.asObservable();
    }

    logout() {
        this.afAuth.auth.signOut();
        this.authInfo$.next(AuthService.UNKNOWN_USER);
        this.router.navigate(['/login']);
    }

    refresh() {
        const url = window.location.href;
    } 
}

AuthGuard Class

import { tap, take, map } from 'rxjs/operators';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { Observable } from 'rxjs/Rx';
import { Injectable } from '@angular/core';
import { AuthService } from '../security/auth-service';
import { Location } from '@angular/common';

@Injectable()
export class AuthGuard implements CanActivate {
    redirectUrl: string;
    currentURL = '';
    constructor(private authService: AuthService, private router: Router) {}

    canActivate(route: ActivatedRouteSnapshot,
                state: RouterStateSnapshot): Observable<boolean> {

        console.log(this.authService.authInfo$);

        return this.authService.authInfo$.pipe(
            map(authInfo => authInfo.isLoggedIn()),
            take(1),
            tap(allowed => {
                if (!allowed) {
                    console.log(allowed);
                    this.router.navigate(['/login']);
                }
            }),
        );
    }
}

Answer №1

Initially, the authInfo$ is set to UNKNOWN_USER, causing the first value fired to always be false. When reloading, only the default first value is taken using take(1).

An alternative approach is to set the first value to null and then filter out any null values in the Observable used in the guard:

AuthService

...
new BehaviorSubject<AuthInfo>(null);
...

AuthGuard

...
return this.authService.authInfo$.pipe(
  filter(_ => _ !== null),
  map(authInfo => authInfo.isLoggedIn()),
  take(1),
...

This method ensures that only relevant values are triggered for the guard.

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

Retrieve information for the designated page exclusively

When retrieving data from the backend using a service, I encounter an issue where the system may slow down if 2000 records are returned in one request. To address this, I would like to display only 10 records per page and fetch the next 10 records with eac ...

What is the best way to fetch multiple values using momentjs from firebase?

After fetching data from Firebase and storing it in an array, I am attempting to retrieve the posted_at values (timestamp of when something was posted) in a time format using Vuejs. However, I am facing difficulty as it only retrieves a single value instea ...

Angular service is continuously throwing the error message "NullInjectorError: No provider for anotherService"

I recently built a basic Angular service and encountered an issue. @Injectable() export class someHandler { constructor( public anotherService: anotherService, ) {} ... The problem arises when I try to use this service in a component, as ...

Discovering React components within a shadow DOM utilizing TypeScript and Protractor [

I am currently faced with the challenge of locating elements within a shadow root from 9-11. Traditional locators like xpath, css, and id have proven unsuccessful in this scenario. However, I was able to successfully locate the element using JavascriptExec ...

Dynamic property access using optional chaining in JavaScript

My attempt to utilize optional chaining, a feature provided by TypeScript for safely accessing dynamic properties, seems to be invalid. export const theme = { headers: { h1: { }, h6: { color: '#828286' }, }, } console.in ...

Tips for finalizing a subscriber after a for loop finishes?

When you send a GET request to , you will receive the repositories owned by the user benawad. However, GitHub limits the number of repositories returned to 30. The user benawad currently has 246 repositories as of today (14/08/2021). In order to workarou ...

Creating a custom type for accepted arguments in a Typescript method

Below is the structure of a method I have: const a = function (...args: any[]) { console.log(args); } In this function, the type of args is any[]. I am looking to create a specific type for the array of arguments accepted by this method in Typescript. ...

Using TypeScript to structure and organize data in order to reduce the amount of overly complex code blocks

Within my TypeScript module, I have multiple array structures each intended to store distinct data sets. var monthlySheetP = [ ['Year', 'Month', 'Program', 'Region', 'Market', 'Country', &apo ...

Mapping data arrays for a particular type of object array

Perhaps a simple query, but I am struggling to find an example for this. Here is my HttpClient call: getItems(dataSourceUrl: string, bindKey: string, bindValue: string): Observable<SelectItem[]> { return this.httpClient.get<Array<any>& ...

Can someone share with me the best practices for implementing @HostListener within a method in my Angular 16 project?

Currently, I've been involved in developing a Single Page Application using Angular 16, TypeScript, and The Movie Database (TMDB). My task at hand is to implement the "infinite scroll" functionality on a particular component. To achieve this, I have ...

What is the method for creating a new array of objects in Typescript with no initial elements?

After retrieving a collection of data documents, I am iterating through them to form an object named 'Item'; each Item comprises keys for 'amount' and 'id'. My goal is to add each created Item object to an array called ' ...

Issue with Pop Up not redirecting correctly after a successful login in Azure AD

I recently integrated Azure AD with my web application. When clicking on the login window, a pop-up appears with the URL . It prompts for the Azure AD username and password. However, after successfully logging in, a code is returned as a parameter but the ...

What is the process for combining two interface declarations into a single interface?

I have a question regarding organizing the properties of an interface: export interface IInvoicesData { invoice: IInvoice; invoiceWithTotals: IInvoice & IInvoiceTotals; } Currently, everything is functioning smoothly and I am able to consolid ...

How to set focus on an input element in Angular 2/5

After clicking, the input element is displayed (it is initially hidden). I want this input to be in focus when it appears. This functionality works well when the input is not hidden by *ngIf (#inputElement2). Is there a way to achieve the same for #inputE ...

Utilizing Arrays in Typescript within the Angular Framework

I have developed a Rest API that provides data to populate two drop-down lists in a form. The information retrieved from the API is grabbed by the Angular backend and assigned to the respective drop-downs. Rather than making separate Get requests for each ...

An issue with the HTTP GET Request method is that it often sends "?" instead of "/"

I have a general method for interacting with my Swagger API: get<ReturnType>(url: string, params?: HttpParams): Observable<ReturnType> { return this.http.get<ReturnType>(environment.apiUrl + url, { params: params, }); } ...

Executing installed packages using npm: A step-by-step guide

Recently, I have encountered a confusing issue in my coding journey. In Python, I got used to installing packages and using them right away without any hiccups. For example, with SpotDL, everything worked seamlessly. However, things took a different turn w ...

A guide to declaring MongoDB models in TypeScript across multiple files

In my Node.js TypeScript project, the structure is as follows: https://i.stack.imgur.com/YgFjd.png The crucial part of the structure lies in mongoModels. I have 2 models where each Category model is connected and contains field category.expertUserIds whi ...

The name "Identifier" has already been declared before

I am currently working on a social network project to enhance my skills in nodejs and reactjs. While debugging the backend code for /signin using Postman, I encountered an error that prevents me from launching the node server. The error message displayed i ...

Is there a way to determine the most recent Typescript target compatibility for every Node version?

When using any version of Node, how can I identify the appropriate Typescript Compiler Option for target that offers the most functionality? I want to eliminate any guesswork. Specify the ECMAScript target version as: "ES3" (default), "ES5", "ES6"/"ES20 ...