Franck Charlier Franck Charlier - 3 years ago 156
TypeScript Question

Angular Service is reset on each call of it

In my web Angular project I've created an AuthenticationGuard and an AuthenticationService to handle the security.

These files comes from another branch of my project who works perfectly.

Here is how my scripts should works:


  1. Navigate to 'auth/login'

  2. User inputs his credentials

  3. Authservice call the back-end wepApi to get a Bearer Token

  4. Back-end returns the token.

  5. AuthService set his var 'isLoggedIn' to true;

  6. AuthService use to router to navigate to '/home'

  7. AuthGuard check authentication by checking the 'isLoggedIn' of AuthService.



My problem is when AuthGuard access the AuthService : AuthService always return false.

auth.guard.ts



import { Injectable } from '@angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot,RouterStateSnapshot } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
let url: string = state.url;

return this.checkLogin(url);
}

checkLogin(url: string): boolean {
if (this.authService.getIsLoggedIn()) {
return true;
}

// Store the attempted URL for redirecting
this.authService.redirectUrl = url;

// Navigate to the login page with extras
this.router.navigate(['/auth/login']);
return false;
}
}





auth.service.ts


import { Injectable } from '@angular/core';
import { Router } from '@angular/router';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/delay';

import { config } from './../shared/smartadmin.config';

import { Http, Headers, RequestOptions, Response } from '@angular/http';
import 'rxjs/add/operator/map'

@Injectable()
export class AuthService {
private isLoggedIn: boolean = false;

public redirectUrl: string;

constructor(private router: Router, private http: Http) {
}

public getIsLoggedIn(): boolean {
console.log("getIsLoggedIn() = " + this.isLoggedIn); // Always returns false
return this.isLoggedIn;
}

public login(username: string, password: string) {
this.ProcessLogin(username, password)
.subscribe(result => {
if (result === true) {
console.log("before attribution");
console.log("this.isLoggedIn = " + this.isLoggedIn); // returns false
this.isLoggedIn = true;
console.log("after attribution");
console.log("this.isLoggedIn = " + this.isLoggedIn); // returns true
this.router.navigate(this.redirectUrl ? [this.redirectUrl] : ['/home']);
} else {
this.logout();
}
});
}


public logout(): void {
localStorage.removeItem('oAuthToken');
this.isLoggedIn = false;
}

private ProcessLogin(username: string, password: string): Observable<boolean> {

let headers = new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' });
let options = new RequestOptions({ headers: headers });
let body = 'grant_type=password&username=' + encodeURIComponent(username) + '&password=' + encodeURIComponent(password);

let endpoint = config.API_ENDPOINT + 'token';

return this.http.post(endpoint, body, options)
.map((response: Response) => {
// login successful if there's a jwt token in the response
let token = response.json() && response.json().access_token;
if (token) {
localStorage.setItem('oAuthToken', token);

// return true to indicate successful login
return true;
} else {
localStorage.removeItem('oAuthToken');
// return false to indicate failed login
return false;
}
});
}
}




Answer Source

Without having seen your module definitions I suspect that you have not made AuthService a core service (a Singleton) which mean that every module that makes use of it will have its own instance (keeping track of its own isLoggedIn flag).

To make a service a singleton in angular it has to be served by the root module injector. To accomplish this you will need to do this:

import { NgModulep } from '@angular/core';
import { CommonModule, ModuleWithProviders } from '@angular/common';
import { AuthService, AuthGuard } from './services/index';

@NgModule({
  imports: [
    CommonModule,
    ModuleWithProviders
  ]
})
export class SharedModule {

  static forRoot(): ModuleWithProviders {
    return {
      ngModule: SharedModule,
      providers: [
        AuthService,
        AuthGuard
      ]
    };
  }

}

And then call the forRoot method when importing the SharedModule into the root AppModule.

@NgModule({
  imports: [
    ...
    SharedModule.forRoot(),
    ...
  ],
  ...,
  bootstrap: [AppComponent]
})
export class AppModule { }

Have a look att "Configure Core Services" here https://angular.io/docs/ts/latest/guide/ngmodule.html#!#core-for-root

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download