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.