'canActivate is blocking all routes when logged in, Angular 13
I am struggling to figure out what is going on with my canActivate Method. Currently, I am able to log in and see that my user credentials are being saved and used in both localStorage and a console.log() so I know that I am creating them. I can also use an autologin feature. The problem is when I try to protect routes with AuthGuard, my canActivate method is (I'm guessing) returning false every time. I have tried to use console.log to see what is being output in canActivate but I cannot log anything past the first return.
Here is my auth.guard.ts file:
import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree, Router } from "@angular/router";
import { Observable } from "rxjs";
import { AuthService } from "./auth.service";
import { map, take } from 'rxjs/operators';
@Injectable({providedIn: 'root'})
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router){}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean
| UrlTree
| Observable<boolean | UrlTree>
| Promise<boolean | UrlTree> {
console.log(this.authService.user);
console.log('In canActivate Method');
return this.authService.user.pipe(
take(1),
map( user => {
const isAuth = !!user
console.log('Deeper In canActivate Method');
console.log(isAuth);
console.log('Deeper In canActivate Method');
if(!isAuth){
return true;
}
else {
this.router.createUrlTree(['']);
return false;
}
}));
}
}
And here is my app.module.ts file:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { AuthGuard } from './auth/auth.guard';
import { AppComponent } from './app.component';
import { generalMachiningComponent } from './generalMachining/generalMachining.component';
import { setupSheetsComponent } from './setupSheets/setupSheets.component';
import { toolReOrderComponent } from './toolReOrder/toolReOrder.component';
import { RouterModule, Routes } from '@angular/router';
import { UserprofileComponent } from './userprofile/userprofile.component';
import { NavbarComponent } from './navbar/navbar.component';
import { SawComponent } from './saw/saw.component';
import { ButtonBarGenMachComponent } from './button-bar-gen-mach/button-bar-gen-mach.component';
import { DeburringComponent } from './deburring/deburring.component';
import { Cat50leadwellsComponent } from './cat50leadwells/cat50leadwells.component';
import { Cat40leadwellComponent } from './cat40leadwell/cat40leadwell.component';
import { CinciMillComponent } from './cinci-mill/cinci-mill.component';
import { DoosanComponent } from './doosan/doosan.component';
import { LeadwellLatheComponent } from './leadwell-lathe/leadwell-lathe.component';
import { MoriLatheComponent } from './mori-lathe/mori-lathe.component';
import { CylinderKingComponent } from './cylinder-king/cylinder-king.component';
import { RodHoneComponent } from './rod-hone/rod-hone.component';
import { PartsWashingComponent } from './parts-washing/parts-washing.component';
import { LoginHeaderComponent } from './login-header/login-header.component';
import { MainTemplateComponent } from './main-template/main-template.component';
import { LoginMainComponent } from './login-main/login-main.component';
import { AssemblyComponent } from './assembly/assembly.component';
import { EngineeringComponent } from './engineering/engineering.component';
import { HeaderComponent } from './header/header.component';
import { ENGCompressorFundamentalsComponent } from './eng-compressor-fundamentals/eng-compressor-fundamentals.component';
import { ENGCompressorFundamentalsIndexAdvancedRecipCompressorInfoComponent } from './eng-compressor-fundamentals-index-advanced-recip-compressor-info/eng-compressor-fundamentals-index-advanced-recip-compressor-info.component';
import { SignupComponent } from './signup/signup.component';
import { ForgotauthComponent } from './forgotauth/forgotauth.component';
import { LoadingSpinnerComponent } from './shared/loading-spinner/loading-spinner.component';
const appRoutes: Routes =[
{path: '', component: LoginMainComponent},
{path: 'header', component:HeaderComponent},
{path: 'signup', component:SignupComponent},
{path: 'forgotauth', component: ForgotauthComponent},
{path: 'userprofile', component: UserprofileComponent},
{path: 'setupsheets', component: setupSheetsComponent, canActivate: [AuthGuard]},
{path: 'toolreorder', component: toolReOrderComponent, canActivate: [AuthGuard]},
{path: 'generalmachining', component: generalMachiningComponent, canActivate: [AuthGuard]},
{path: 'generalmachining/gm', component: generalMachiningComponent, canActivate: [AuthGuard]},
{path: 'generalmachining/saw', component: SawComponent, canActivate: [AuthGuard]},
{path: 'generalmachining/deburring', component: DeburringComponent, canActivate: [AuthGuard]},
{path: 'generalmachining/partswashing', component: PartsWashingComponent, canActivate: [AuthGuard] },
{path: 'generalmachining/cat50leadwells', component: Cat50leadwellsComponent, canActivate: [AuthGuard]},
{path: 'generalmachining/cat40leadwell', component: Cat40leadwellComponent, canActivate: [AuthGuard]},
{path: 'generalmachining/cinci', component:CinciMillComponent, canActivate: [AuthGuard]},
{path: 'generalmachining/doosan', component: DoosanComponent, canActivate: [AuthGuard]},
{path: 'generalmachining/leadwell-lathe', component:LeadwellLatheComponent, canActivate: [AuthGuard]},
{path: 'generalmachining/moriseiki', component: MoriLatheComponent, canActivate: [AuthGuard]},
{path: 'generalmachining/cylinderking', component: CylinderKingComponent, canActivate: [AuthGuard]},
{path: 'generalmachining/rodhone', component: RodHoneComponent, canActivate: [AuthGuard]},
{path: 'assembly', component: AssemblyComponent, canActivate: [AuthGuard]},
{path: 'engineering', component: EngineeringComponent, canActivate: [AuthGuard]},
{path: 'engineering/compressor-fundamentals-index', component: ENGCompressorFundamentalsComponent, canActivate: [AuthGuard]},
{path: 'engineering/compressor-fundamentals-index/Advanced_Recip_Compressor_Info', component:ENGCompressorFundamentalsIndexAdvancedRecipCompressorInfoComponent, canActivate: [AuthGuard]}
];
@NgModule({
declarations: [
AppComponent,
generalMachiningComponent,
setupSheetsComponent,
toolReOrderComponent,
UserprofileComponent,
NavbarComponent,
SawComponent,
ButtonBarGenMachComponent,
DeburringComponent,
Cat50leadwellsComponent,
Cat40leadwellComponent,
CinciMillComponent,
DoosanComponent,
LeadwellLatheComponent,
MoriLatheComponent,
CylinderKingComponent,
RodHoneComponent,
PartsWashingComponent,
LoginHeaderComponent,
MainTemplateComponent,
LoginMainComponent,
AssemblyComponent,
EngineeringComponent,
HeaderComponent,
ENGCompressorFundamentalsComponent,
ENGCompressorFundamentalsIndexAdvancedRecipCompressorInfoComponent,
SignupComponent,
ForgotauthComponent,
LoadingSpinnerComponent
],
imports: [
BrowserModule,
RouterModule.forRoot(appRoutes),
FormsModule,
ReactiveFormsModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
I have read several posts stating that it was the providers in the app.module that need to have AuthGuard added to it, but other posts stated that my @Injection to Root should take care of that. I have tried both and it is not making a difference. Any help with this problem would be greatly appreciated.
EDIT: Add auth.service file
import { HttpClient, HttpErrorResponse } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Router } from '@angular/router';
import { catchError, Subject, throwError, tap } from "rxjs";
import { environment } from "../../environments/environment";
import { User } from "./user.model";
import { map, take } from 'rxjs/operators';
interface AuthResponseData {
kind: string;
idToken: string;
email: string;
refreshToken: string;
expiresIn: string;
localId: string;
registered?: boolean;
}
@Injectable({ providedIn: 'root' })
export class AuthService {
user = new Subject<User>();
private tokenExpirationTimer: any;
constructor(private http: HttpClient, private router: Router) { }
private handleAuthentication(email: string, userId: string, token: string, expiresIn: number) {
const expirationDate = new Date(new Date().getTime() + expiresIn * 1000);
const user = new User(
email,
userId,
token,
expirationDate
);
this.user.next(user);
this.autoLogout(expiresIn * 1000);
localStorage.setItem('userData', JSON.stringify(user));
}
checkUserAuth() {
const userData: {
email: string;
id: string;
_token: string;
_tokenExpirationDate: string;
} = JSON.parse(localStorage.getItem('userData'));
if (!userData) {
console.log("No stored user data: returning false.");
this.router.navigate(['']);
return false;
}
const loadedUser = new User(userData.email, userData.id, userData._token, new Date(userData._tokenExpirationDate));
if (loadedUser.token) {
this.user.next(loadedUser);
const expirationDuration = new Date(userData._tokenExpirationDate).getTime() - new Date().getTime()
this.autoLogout(expirationDuration);
return true;
}
}
autoLogin() {
const userData: {
email: string;
id: string;
_token: string;
_tokenExpirationDate: string;
} = JSON.parse(localStorage.getItem('userData'));
if (!userData) {
return;
}
const loadedUser = new User(userData.email, userData.id, userData._token, new Date(userData._tokenExpirationDate));
if (loadedUser.token) {
this.user.next(loadedUser);
this.router.navigate(['/userprofile']);
const expirationDuration = new Date(userData._tokenExpirationDate).getTime() - new Date().getTime()
this.autoLogout(expirationDuration);
}
}
autoLogout(expirationDuration: number) {
this.tokenExpirationTimer = setTimeout(() => {
this.logout();
}, expirationDuration);
}
signup(email: string, password: string) {
return this.http
.post<AuthResponseData>(
'https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=' + environment.API_KEY,
{
email: email,
password: password,
returnSecureToken: true
}
)
.pipe(catchError(this.handleError), tap(resData => {
this.handleAuthentication(
resData.email,
resData.localId,
resData.idToken,
+resData.expiresIn
);
}));
}
login(email: string, password: string) {
return this.http
.post<AuthResponseData>('https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=' + environment.API_KEY,
{
email: email,
password: password,
returnSecureToken: true
}
)
.pipe(catchError(this.handleError), tap(resData => {
this.handleAuthentication(
resData.email,
resData.localId,
resData.idToken,
+resData.expiresIn
);
}));
}
logout() {
this.user.next(null);
localStorage.removeItem('userData');
this.router.navigate(['']);
if (this.tokenExpirationTimer) {
clearTimeout(this.tokenExpirationTimer)
}
this.tokenExpirationTimer = null;
}
private handleError(errorRes: HttpErrorResponse) {
let errorMessage = 'An unknown error occured.';
if (!errorRes.error || !errorRes.error.error) {
return throwError(errorMessage);
}
switch (errorRes.error.error.message) {
case 'EMAIL_EXISTS':
errorMessage = 'This email exists already.';
break;
case "INVALID_PASSWORD" || "EMAIL_NOT_FOUND":
errorMessage = "Email or password are invalid";
break;
case 'MISSING_PASSWORD':
errorMessage = "A password is required."
break;
case 'USER_DISABLED':
errorMessage = "The user account has been disabled by an administrator."
}
return throwError(errorMessage);
}
}
Note: I have added the checkUserAuth() method and that has allowed me to return true or false in the canActivate() method in auth.guard, but I don't think this is the correct solution to the problem even though it is working now.
Solution 1:[1]
You don't need the first return, I bet that is the problem. You just do the .pipe and the .map and in there you evaluate and return a result.
Also, you are doing const isAuth = !!user but immediately after you negate that result in the if statement:
if(!isAuth){
return true;
}
else {
this.router.createUrlTree(['']);
return false;
}
Change that if(!isAuth) for if(isAuth) and try again. And the guards only need to be specified in the providers array of your routing module.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | JuanDeLasNieves |
