a11smiles
a11smiles

Reputation: 1258

Angular 4 and Tokens

I have an injectable authentication service written for Angular 4. The code looks similar to the following:

auth.service.ts

import { CookieService } from 'ngx-cookie';

import { Identity } from './auth.identity';

export function authInit(authService: AuthService): () => Promise<any> {
    return (): Promise<any> => authService.checkAuthenticated();
}

@Injectable()
export class AuthService {

    identity: Identity;
    isAuthenticated:boolean = false;

    apiUrl: string = 'https://myUrl/api';

    constructor(private _http: HttpClient, private _cookieService: CookieService) {
        this.identity = new Identity();

    }

    checkAuthenticated(): Promise<any> {
        return new Promise((res, rej) => {
            let identity = this._cookieService.getObject('myToken');
            if (!!identity) {
                this.setAuthenticated(identity);
            }
        });
    }

    login(username: string, password: string) {
        let creds = {
            username: username,
            password: password
        };

        this._http.post<any>(this.apiUrl + '/auth/login', creds).subscribe(data => {
            this.setAuthenticated(data);
        });

    }

    logout() {
    }

    private setAuthenticated(data: any) {
        this._cookieService.putObject('myToken', data);
        this.isAuthenticated = true;

        // hydrate identity object
    }
}

auth.module.ts

import { NgModule, APP_INITIALIZER } from '@angular/core';
import { CommonModule } from '@angular/common';

import { AuthService, authInit } from './auth.service';

@NgModule({
    imports: [CommonModule],
    providers: [
        AuthService,
        {
            provide: APP_INITIALIZER,
            useFactory: authInit,
            deps: [AuthService],
            multi: true
        }
    ]
})
export class AuthModule { }

The idea is that when the app loads, I want to be able to check the local storage (cookies, sessionStorage or localStorage) to see if the value exists. (This is demonstrated by the commented if statement in the constructor.) Based on the isAuthenticated property I want to be able to show specific content.

Currently, if I uncomment the lines in the constructor, I'll get an exception document.* is not defined. I know what that means. Unfortunately, I don't know how to accomplish what I'm going for.

Keep in mind, this is a service and not a view component, so there's no ngOnInit method available.

EDITED So I've added the factory provider as suggested. However, I'm still getting the exception: document is not defined

Thanks!

Upvotes: 2

Views: 262

Answers (1)

Roy Reiss
Roy Reiss

Reputation: 993

When you have a service that you need to have run before everything else might be initialized you can use the APP_INITIALIZER token (the documentation is sparse to say the least :)

The gist is that in your application providers array you add a factory provider:

{
  provide: APP_INITIALIZER,
  useFactory: authInit,
  deps: [AuthService],
  multi: true
}

Make sure to have provide set specifically to APP_INITIALIZER and the multi value to true. The authInit function is factory that returns a function that returns a promise. It has to return a promise and not an observable. It would be something like:

export function authInit(authServ: AuthService) {
  return () => authServ.check();
}

The authServ.check() function is where you can put the logic you currently have commented in your service (just make sure it returns a promise as the result). Setting it up this way will let that logic run while the application loads.

Edit: Now that I take a look at the app.module.ts add the initialization of the cookie service and add the BrowserModule:

import { NgModule, APP_INITIALIZER } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';

import { CookieModule } from 'ngx-cookie';

import { AuthService, authInit } from './auth.service';

@NgModule({
    imports: [BrowserModule, CommonModule, CookieModule.forRoot()],
    providers: [
        AuthService,
        {
            provide: APP_INITIALIZER,
            useFactory: authInit,
            deps: [AuthService],
            multi: true
        }
    ]
})
export class AuthModule { }

Also, make sure to add ngx-cookie to your systemjs.config.js (if that's what you're using as your loader).

Upvotes: 1

Related Questions