Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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;
                }
            });
    }
}
like image 291
Franck Charlier Avatar asked Jun 05 '17 04:06

Franck Charlier


3 Answers

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

like image 176
Hampus Avatar answered Nov 08 '22 10:11

Hampus


I had a similar problem, which in my case was being caused by the click event being wired to a button element, but in Chrome the entire form was being submitted every time the button was clicked, because CHrome's default action is to treat a button click as a submit if there is no type attribute on the button.

The fix was to add the tag type="button" to the login button in the html

Description here

like image 24
jazza1000 Avatar answered Nov 08 '22 08:11

jazza1000


Dependencies are singletons within the scope of an injector.

However, Angular DI is a hierarchical injection system, which means that nested injectors can create their own service instances. For more information, see Hierarchical Injectors.
Source

Just double check where you provide your service.

List your modules and inspect providers section in each.
If there are more than one instance - each module will provide its own instance of service.

I had a same issue and found I add AuthService to AppModule and forget to remove it from AuthModule, so login page(in the auth module) had other instance of it.

like image 1
vp_arth Avatar answered Nov 08 '22 08:11

vp_arth