Boyan Petrov
Boyan Petrov

Reputation: 105

Header stays the same after I check if a user is logged in

I'm currently using Angular 11 on the front-end and Express on the back-end. I'm trying to get if a user is logged in, so I can show the correct header. After I login the browser redirects me to the home page and a cookie is set in the browser, but nothing changes in the header. After I click the logout button the cookie is then removed and the header remains the same. I tried debugging it using chrome dev tools and logging the isLogged$ variable, but it keeps showing me that it's undefined. I think I'm doing something wrong with the BehaviourSubject.

I really hope someone can help me find my mistake, since I just started with Angular.

I will attach some of the files for a better explanation.

auth.service.ts

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { Observable } from 'rxjs/internal/Observable';
import { catchError, tap, map } from 'rxjs/operators';
import { IUser } from '../shared/interfaces';

@Injectable()
export class AuthService {

  currentUser: IUser | null;

  private _currentUser: BehaviorSubject<IUser | null> = new BehaviorSubject(undefined);
  currentUser$ = this._currentUser.asObservable();
  isLogged$ = this.currentUser$.pipe(map(user => !!user));

  constructor(private http: HttpClient) { }

  login(data: any): Observable<any> {
    return this.http.post(`/user/login`, data).pipe(
      tap((user: IUser) =>
        this._currentUser.next(user))
    );
  }

  signup(data: any): Observable<any> {
    return this.http.post(`/user/signup`, data).pipe(
      tap((user: IUser) => this._currentUser.next(user))
    );
  }

  logout(): Observable<any> {
    return this.http.post(`/user/logout`, {}).pipe(
      tap((user: IUser) => this._currentUser.next(null))
    );
  }

  authenticate(): Observable<any> {
    return this.http.get(`/user/verify`).pipe(
      tap((user: IUser) => this._currentUser.next(user)),
      catchError(() => {
        this._currentUser.next(null);
        return [null];
      })
    );
  }
}

navigation.component.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../auth.service';

@Component({
  selector: 'app-navigation',
  templateUrl: './navigation.component.html',
  styleUrls: ['./navigation.component.css']
})
export class NavigationComponent implements OnInit {

  isLogged$ = this.authService.isLogged$;

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

  ngOnInit(): void {
  }
  
  logoutHandler(): void {
    this.authService.logout().subscribe(() => this.router.navigate(['/']));
  }
}

navigation.component.html

<nav class="navigation-wrapper">
    <article>
        <ul>
            <li class="home">
                <a href="/">Home</a>
            </li>
            <li *ngIf="!isLogged$" class="login">
                <a href="/login">Login</a>
            </li>
            <li *ngIf="!isLogged$" class="sign-up">
                <a href="/signup">Sign Up</a>
            </li>
            <li *ngIf="isLogged$" class="my-cities">
                <a href="/my-cities">My Cities</a>
            </li>
            <li *ngIf="isLogged$" class="account">
                <a href="/account">Account</a>
            </li>
            <li *ngIf="isLogged$" class="logout">
                <a (click)="logoutHandler()">Logout</a>
            </li>
        </ul>
    </article>
</nav>

auth.interceptor.ts

import { Injectable, Provider } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Observable } from 'rxjs';

import { environment } from '../../environments/environment';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
    apiUrl = environment.apiURL;

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        req = req.clone({
            url: `${this.apiUrl}${req.url}`,
            withCredentials: true
        });
        return next.handle(req);
    }
}

export const authInterceptorProvider: Provider = {
    provide: HTTP_INTERCEPTORS,
    useClass: AuthInterceptor,
    multi: true
};

user.js (back-end controller)

const User = require('../models/User');

const utils = require('../utils');
const authCookieName = process.env.authCookieName;

module.exports = {
    get: async (req, res, next) => {
        try {
            const { id } = req.params;

            const user = await User.findById(id).populate('followedCities').lean();

            res.send(user);
        } catch (e) {
            console.error(e);
            next();
        }
    },

    post: {
        signup: async (req, res, next) => {
            const { name, email, password } = req.body;
            console.log(req.body);
            console.log(name, email, password);
            const hashedPassword = await utils.auth.hashPassword(password);

            try {
                const newUser = await User.create({
                    name,
                    email,
                    password: hashedPassword
                });

                const user = await User.findOne({ email }).lean();
                const token = utils.jwt.createToken({ id: user._id });

                res.cookie(authCookieName, token).send(user);
            } catch (e) {
                console.error(e);
                next();
            }
        },

        login: async (req, res, next) => {
            const { email, password } = req.body;
            console.log(req.body);

            try {
                const status = await utils.auth.checkPassword(email, password);

                if (!status) {
                    res.status(401).send('Invalid username or password!');
                }

                const user = await User.findOne({ email }).lean();
                const token = utils.jwt.createToken({ id: user._id });

                res.cookie(authCookieName, token).send(user);
            } catch (e) {
                console.error(e);
                next();
            }
        },

        verifyLogin: (req, res, next) => {
            console.log(req.headers);
            const token = req.cookie.authCookieName || '';

            Promise.all([
                utils.jwt.verifyToken(token)
            ])
                .then(([data]) => {

                    User.findById(data.id)
                        .then((user) => {
                            return res.send({
                                status: true,
                                user
                            })
                        });
                })
                .catch(err => {
                    if (['token expired', 'jwt must be provided'].includes(err.message)) {
                        res.status(401).send('UNAUTHORIZED!');
                        return;
                    }

                    res.send({
                        status: false
                    })

                    next();
                })
        },

        logout: async (req, res, next) => {
            try {
                res.clearCookie(authCookieName).send('Logout Successful!');
            } catch (e) {
                console.error(e);
                next();
            }
        }
    }
};

Upvotes: 0

Views: 756

Answers (1)

Evgeniya
Evgeniya

Reputation: 88

isLogged$ ist Observable.

You can`t write in navigation.component.html <li *ngIf="!isLogged$" class="login">

Please try <li *ngIf="!(isLogged$ | async)" class="login">

The async pipe subscribes to an Observable or Promise and returns the latest value it has emitted.

Upvotes: 2

Related Questions