How can i prevent from Login component render when Firebase Authentication hasn't yet...

Discussão em 'Angular' iniciado por Francisco Soares, Novembro 8, 2024.

  1. The login component flashes even if the user is authenticated. I believe it is due to the initial state of the user$ observable from Firebase Auth.

    When my application first loads, Firebase Authentication hasn't yet determined whether the user is logged in or not. This means that during the initial load, the user$ observable is null (indicating no user is authenticated), and until Firebase processes the user's authentication state, the guard might redirect to the login page.

    authGuard.ts

    import { CanActivateFn } from '@angular/router';
    import { AuthService } from './auth';
    import { inject } from '@angular/core';
    import { Router } from '@angular/router';
    import { map } from 'rxjs/operators';


    export const AuthGuard: CanActivateFn = (route, state) => {
    const firebaseAuth = inject(AuthService);
    const router = inject(Router);

    return firebaseAuth.user$.pipe(
    map(user => {
    if (user) {
    return true; // Allow access if user is authenticated
    } else {

    router.navigate(['/login'], { queryParams: { returnUrl: state.url } }); // Redirect to login if not authenticated
    return false;
    }
    })
    );
    };


    auth.ts

    import { Injectable, inject, signal } from '@angular/core';
    import {
    Auth,
    createUserWithEmailAndPassword,
    signInWithEmailAndPassword,
    user,
    signOut,
    UserCredential
    } from '@angular/fire/auth';
    import { Observable, from, BehaviorSubject,switchMap } from 'rxjs';
    import { IUser } from '../interfaces/user';
    import { Router } from '@angular/router';
    import { UserService } from './user.service';

    @Injectable({
    providedIn: 'root',
    })
    export class AuthService {
    router = inject(Router);
    user$: Observable<IUser | null>;
    currentUserSig = signal<IUser | null | undefined>(undefined);
    isLoggedInGuard: boolean = false;
    private loadingSubject = new BehaviorSubject<boolean>(true); // Loading state
    loading$ = this.loadingSubject.asObservable();

    userService = inject(UserService);


    constructor(private firebaseAuth: Auth) {
    this.user$ = user(this.firebaseAuth) as Observable<IUser | null>;

    // Listen for user state changes and update loading state
    this.user$.subscribe((user) => {
    console.log(user);
    if (user) {
    this.currentUserSig.set({ email: user.email, uid: user.uid });
    } else {
    this.currentUserSig.set(null);
    }
    this.loadingSubject.next(false); // Authentication state resolved, stop loading
    });
    }

    register(email: string, password: string, nome: string, apelido: string): Observable<void> {
    // Create the user with email and password
    return from(createUserWithEmailAndPassword(this.firebaseAuth, email, password)).pipe(
    // Use switchMap to chain the Firebase user creation to adding user data to Firestore
    switchMap((ref: UserCredential) => {
    // Prepare user data object
    const userData = {
    email: email,
    nome: nome,
    apelido: apelido,
    uid: ref.user.uid, // Access uid here correctly
    };

    // Call addUser in userService and return its observable
    return this.userService.addUser(userData);
    })
    );
    }

    login(email: string, password: string): Observable<void> {
    const promise = signInWithEmailAndPassword(
    this.firebaseAuth,
    email,
    password
    ).then(() => {});
    return from(promise);
    }

    logout(): Observable<void> {
    const promise = signOut(this.firebaseAuth).then(() => {
    this.router.navigate(['/login']);
    this.isLoggedInGuard = false;
    });
    return from(promise);
    }

    }


    app.component.ts

    import { Component,inject,signal } from '@angular/core';
    import { Router, RouterOutlet } from '@angular/router';
    import { OnInit } from '@angular/core';
    import { AuthService } from './services/auth';
    import { IUser } from './interfaces/user';
    import { LoadingComponent } from './layouts/loading/loading.component';
    import { CommonModule } from '@angular/common';

    @Component({
    selector: 'app-root',
    standalone: true,
    imports: [RouterOutlet,LoadingComponent,CommonModule],
    templateUrl: './app.component.html',
    styleUrl: './app.component.css'
    })
    export class AppComponent implements OnInit {
    authService = inject(AuthService);
    router = inject(Router);
    loading:boolean = true;

    ngOnInit(): void{
    this.authService.loading$.subscribe((isLoading) => {
    this.loading = isLoading;
    });
    }
    }


    app.component.html

    @if(loading){
    <app-loading></app-loading>
    }
    @else{
    <router-outlet></router-outlet>
    }


    app.routes.ts

    import { Routes } from '@angular/router';
    import { LoginComponent } from './auth/login/login.component';
    import { RegisterComponent } from './auth/register/register.component';
    import { ForgotPasswordComponent } from './auth/forgot-password/forgot-password.component';
    import { HomeComponent } from './layouts/home/home.component';
    import { AuthGuard } from './services/authGuard';
    import { NotFoundComponent } from './layouts/not-found/not-found.component';

    export const routes: Routes = [
    { path: '', redirectTo: 'home', pathMatch: 'full' }, // Redirect root to 'home' or another default
    { path: 'login', component: LoginComponent },
    { path: 'register', component: RegisterComponent },
    { path: 'forgot-password', component: ForgotPasswordComponent },
    { path: 'home', component: HomeComponent, canActivate: [AuthGuard] },
    { path: '**', component: NotFoundComponent } // Wildcard route for 404 page
    ];


    I tried to create a Loading component so when user is undefined it renders the loading component if not renders the app normally. The problem it is flashing the login and loading component at the same time due to this router.navigate(['/login'], { queryParams: { returnUrl: state.url } }); in authGuard.ts

    Continue reading...

Compartilhe esta Página