Reputation: 1800
I am currently developing an Angular 4 application.
The application uses Auth0 for authentication whose syntax is quite similar to those of other authentication services.
The code for authentication looks as follows:
// auth.services.ts
@Injectable()
export class Auth {
public lock = new Auth0Lock(myConfig.clientID, myConfig.domain, myConfig.lock);
public userProfile: any;
public idToken: string;
public signUpIncomplete: boolean;
// Configure Auth0
private auth0 = new Auth0.WebAuth({
domain: myConfig.domain,
clientID: myConfig.clientID,
redirectUri: myConfig.redirectUri,
responseType: myConfig.responseType
});
// Create a stream of logged in status to communicate throughout app
private loggedIn: boolean;
private loggedIn$ = new BehaviorSubject<boolean>(this.loggedIn);
constructor(private router: Router, private http: Http) {
// Set userProfile attribute of already saved profile
this.userProfile = JSON.parse(localStorage.getItem('profile'));
}
public isAuthenticated(): boolean {
// Check whether the id_token is expired or not
console.log("isAuthenticated");
return tokenNotExpired('id_token');
}
public login(username?: string, password?: string): Promise<any> {
if (!username && !password) {
return;
}
return this.processLogin(username, password);
}
public logout() {
// Remove tokens and profile and update login status subject
localStorage.removeItem('token');
localStorage.removeItem('id_token');
localStorage.removeItem('profile');
this.idToken = '';
this.userProfile = null;
this.setLoggedIn(false);
// Go back to the home rout
this.router.navigate(['/']);
}
public loginWithWidget(): void {
this.lock.show();
}
// Call this method in app.component
// if using path-based routing <== WE ARE USING PATH BASED ROUTING
public handleAuth(): void {
// When Auth0 hash parsed, get profile
this.auth0.parseHash({}, (err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
// window.location.hash = '';
this._getProfile(authResult);
this.router.navigate(['/']);
} else if (err) {
this.router.navigate(['/']);
console.error(`Error: ${err.error}`);
}
});
}
private setLoggedIn(value: boolean) {
// Update login status subject
this.loggedIn$.next(value);
this.loggedIn = value;
}
private _getProfile(authResult) {
// Use access token to retrieve user's profile and set session
// const lock2 = new Auth0Lock(myConfig.clientID, myConfig.domain, myConfig.lock)
const idToken = authResult.id_token || authResult.idToken;
this.lock.getProfile(idToken, (error, profile) => {
if (error) {
// Handle error
console.error(error.error);
return;
}
// Save session data and update login status subject
this._setSession(authResult, profile);
if (!this.checkUserHasRole(profile)) {
this.router.navigate(['/signup/complete']);
}
});
}
private _setSession(authResult, profile) {
// Save session data and update login status subject
localStorage.setItem('token', authResult.access_token || authResult.accessToken);
localStorage.setItem('id_token', authResult.id_token || authResult.idToken);
localStorage.setItem('profile', JSON.stringify(profile));
this.idToken = authResult.id_token || authResult.idToken;
this.setLoggedIn(true);
this.userProfile = profile;
this.checkUserHasRole(profile);
}
private processLogin(username?: string, password?: string): Promise<any> {
const options = {
client_id: myConfig.clientID,
connection: postConfig.body.connection,
grant_type: 'password',
username,
password,
scope: myConfig.scope
};
const headers = new Headers();
headers.append('content-type', 'application/json');
const reqOpts = new RequestOptions({
method: RequestMethod.Post,
url: postConfig.urlLogin,
headers,
body: options
});
return this.http.post(postConfig.urlLogin, options, reqOpts)
.toPromise()
.then(this.extractData)
.then((data) => { this._getProfile(data); })
.catch(this.handleLoginError);
}
...
}
The problem I have is that the isAuthenticated
method is called more than 1000 times on page load. Additionally, it is called each time I move the mouse in the window object.
Although I followed the tutorial of Auth0 step by step, I suppose that this cannot be the expected behaviour as it will and already does affect the performance of the application.
What could be a reason for the fact that isAuthenticated is called that often? Do I have to implement a timer which does the check periodically after a specified time or do I have to implement an observer? Are there any obvious mistakes in my code?
Upvotes: 4
Views: 16397
Reputation: 1800
Finally, I have found out the reason.
My navigation component implements a transition effect using a host listener @HostListener('mouseover', ['$event'])
. I accidentally added the host listener to the window object. For that reason, each time I moved the mouse the host listener was fired. Since my navigation template contains *ngIf="auth.isAuthenticated()"
to display some navigation items just in case the user is authenticated, isAuthenticated
was fired so many times.
Upvotes: 3
Reputation: 7427
The reason why isAuthenticated
is called so many times depends on the component that is calling it, which you don't have here. isAuthenticated
is never called once in this service.
Set up a router guard instead, called CanActivate
by the Angular API. This will be called on route activation and redirects can happen on failures before the routed component can even be loaded, and will only be called once. Use that to call service.isAuthenticated
instead.
login.guard.ts
import { Injectable } from '@angular/core';
import { Router, CanActivate } from '@angular/router';
import { Auth } from './auth.service';
@Injectable()
export class LoginGuard implements CanActivate {
constructor(public auth: Auth, protected router: Router) { }
canActivate() {
if (!this.auth.isAuthenticated()) {
this.router.navigate(['/']);
return false;
}
return true;
}
In your routes definition
export const routes: Routes = [
{ path: '', component: SomeComponent },
{ path: 'main', component: ProtectedComponent, canActivate: [LoginGuard] }
]
It shouldn't be called 1000s of times in any case. I'm guessing there's some looping going on in your component or injection tree.
Upvotes: 5