Currently, I am using Angular 11 for the front-end and Express for the back-end. I am facing an issue with determining if a user is logged in so that I can display the correct header. Even after logging in and setting a cookie in the browser upon redirection to the home page, the header does not update. Similarly, when clicking the logout button, although the cookie is removed, the header remains unchanged. I have tried debugging using Chrome Dev Tools and checking the value of the isLogged$ variable, but it keeps showing as undefined. I suspect there might be an error with the BehaviourSubject.
I am hoping someone can assist me in identifying my mistake, especially since I am relatively new to Angular.
I will provide some files for better context.
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();
}
}
}
};