I am creating login system in my Angular application. Everything works fine, I have just one problem with my AuthGuard. When I refresh the page in which implement 'canActivate: [AuthGuard]' it redirects me to login and i get isAuth = false. But from there I can go back to my guarded page without login.
AuthGuard
@Injectable()
export class AuthGuard implements CanActivate {
constructor(
private userAuthService: UserAuthService,
private router: Router
) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
):
| boolean
| UrlTree
| Observable<boolean | UrlTree>
| Promise<boolean | UrlTree> {
const isAuth = this.userAuthService.getIsAuth();
console.log(isAuth + ' at auth.guard.ts');
if (!isAuth) {
this.router.navigate(['/login']);
}
return isAuth;
}
}
And my UserAuthService file:
private authStatusListener = new Subject<boolean>();
isAuthenticated: boolean = false;
private token: string | null = '';
getIsAuth() {
return this.isAuthenticated;
}
getAuthStatusListener() {
return this.authStatusListener.asObservable();
}
login(user: UserLogin) {
const url = environment.apiUrl + '/auth/login/';
this.http.post<{ token: string }>(url, user).subscribe(
(res: any) => {
const token = res.token;
this.token = token;
if (this.token) {
this.isAuthenticated = true;
this.authStatusListener.next(true);
this.storeJwtToken(this.token);
this.router.navigate(['/']);
} else {
this.isAuthenticated = false;
this.authStatusListener.next(false);
}
},
(error) => {
this.authStatusListener.next(false);
}
);
}
autoAuthUser() {
const token = this.getJwtToken();
if (token) {
this.token = token;
this.isAuthenticated = true;
this.authStatusListener.next(true);
} else {
return;
}
}
storeJwtToken(token: string) {
localStorage.setItem('token', token);
}
getJwtToken(): any {
const token: any = localStorage.getItem('token');
return token;
}
And in my app.component.ts file I am just doing this:
ngOnInit(): void {
this.userAuthService.autoAuthUser();
}
You can create a promise (async) function in your UserAuthService and provide that function in app.module.ts as dependency. So, it will wait until resolves the promise of your async function before starting the app. With this you will be already logged in when your app starts or refresh. UserAuthService:
async init(): Promise<any>{
this.autoAuthUser();
}
app.module.ts:
@NgModule({
declarations: [...],
imports: [...],
providers: [
UserAuthService,
{
//With this your app will wait to resolve the promise of init() of your UserAuthService.
provide: APP_INITIALIZER,
useFactory: (service: UserAuthService) => function() { return service.init();},
deps: [UserAuthService],
multi: true
}],
bootstrap: [AppComponent]
})
export class AppModule { }
Another way
You can simply redirect from your autoAuthUser() function.
AuthGuard:
@Injectable()
export class AuthGuard implements CanActivate {
constructor(
private userAuthService: UserAuthService,
private router: Router,
state: RouterStateSnapshot
) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
){
const isAuth = this.userAuthService.getIsAuth();
console.log(isAuth + ' at auth.guard.ts');
if (!isAuth) {
//Here you pass queryParams with last url from state.url.
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
}
return isAuth;
}
}
UserAuthService :
constructor(
private route: ActivatedRoute,
private router: Router){}
autoAuthUser() {
const token = this.getJwtToken();
if (token) {
this.token = token;
this.isAuthenticated = true;
this.authStatusListener.next(true);
// Here you redirect to the last URL or '/'.
this.router.navigateByUrl(this.route.snapshot.queryParams['returnUrl'] || '/');
} else {
return;
}
}