1

I have developed a website in Angular 4 and ASP.Net Core. However, there's one thing that has been bugging me. It has some basic login implementation with JWT and i'm using local storage to save the user object and the token.

Everything works great once the user logs in, but whenever I refresh the browser Angular always asks me log in again. I have a method on AppComponent to refresh the token and return a new user object every time the app starts (the logic happens in asp net core if it can revalidate the token saved on localStorage or not).

Anyway, I've been searching around and I believe I need to return Observables. Here's another thread with a very similar issue:

AuthGuard doesn't wait for authentication to finish before checking user

The major difference is that the answers use fire base, and I believe their object is already an Observable while mine is a boolean.

Here are my codes:

StorageService (localStorage)

export class StorageService {

    constructor(private storage: LocalStorageService) { }
    public user: User = null;

    public get isLoggedIn() : boolean {
        return this.user != null;
    }

    public isInRole(roleName: string): boolean {
        if (!this.isLoggedIn)
            return false;

        for (let r of this.user.roles) {
            if (r.name == roleName)
                return true;
        }

        return false;
    }

    public get token(): string {
        return <string>this.storage.get(STORAGE_TOKEN) || null;
    }

    public set token(value: string) {
            if (value == null) {
                this.storage.remove(STORAGE_TOKEN);
            } 
            else {
                this.storage.set(STORAGE_TOKEN, value);
            }
        }    
    }

AuthGuard implementation

export class IsLoggedIn implements CanActivate {

    constructor(private storage: StorageService, private router: Router) { }

    canActivate(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot){

        if (this.storage.isLoggedIn) {
            return true;
        }
        this.router.navigate(['account/login'], {
            queryParams: {
                return: state.url
            }
        });
        return false;
    }
}

Token Refresh on AppComponent (ngOnInit)

     this.net.get<LoginModel>(`Authentication/RefreshToken`).subscribe(t => {
        this.storage.token = t.token;
        this.storage.user = t.user;
     });

Lastly, this is how im using IsInRole:

export class AdminOnly implements CanActivate {

    constructor(
        private storage: StorageService,
        private router: Router
    ) { }

    canActivate(
        childRoute: ActivatedRouteSnapshot,
        state: RouterStateSnapshot
   ) {
        var message: string = null;

        if (!this.storage.isLoggedIn) {
            message = 'Você precisa se logar!';
        }

        if (!this.storage.isInRole('Admin')) {
            message = 'Você precisa ser um administrador!';
        }

        if (message == null) {
            return true;
        }

        this.router.navigate(['account/login'], {
            queryParams: {
                return: state.url,
                message: message
            }
        });
        return false;
    }
}

How should I convert my boolean variables to Observables? Any other opinion on how to solve this issue is welcome. Thanks in advance.

Lucas Arruda
  • 533
  • 1
  • 8
  • 15

1 Answers1

2

You can use .of that is on the observable object as a static function. Whenever you subscribe it will reference the current object state.

Rx.Observable.of(state)

Here is a codepen example: https://codepen.io/ndcunningham/pen/OvVxjx?editors=1111

LocalStorage service

    public get isLoggedIn() : Observable<boolean> {
    return Rx.Observable.of(this.user !== null);
}

Guard

export class IsLoggedIn implements CanActivate {

constructor(private storage: StorageService, private router: Router) { }

canActivate(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    return this.storage.isLoggedIn.do(e => !e && this.router.navigate(['account/login'], {
        queryParams: {
            return: state.url
        }
    }));
  }
}
Nico
  • 1,961
  • 17
  • 20
  • I have no observables. Should I change my storageService IsLoggedIn to an Observable? Sorry i really dont understand Observables – Lucas Arruda Mar 11 '18 at 21:28
  • now i have a bunch of new console errors. Basically because i also have a Role check. I’ll update the question with it, maybe im doing it wrong. – Lucas Arruda Mar 11 '18 at 21:53
  • See this gist:https://gist.github.com/ndcunningham/d1e52c74bde4c0eee34f028a65c4210a The brackets might be off tho – Nico Mar 11 '18 at 22:02
  • Ok its almost fully working. The only issue is when the user doesn't have any login information (new user). It floods the console with this error: `ERROR TypeError: Cannot read property 'roles' of null`. I have changed both IsInRole and LoggedIn to Observable and updated my guard – Lucas Arruda Mar 11 '18 at 22:10
  • I updated the gist to use !== and === additionally added !! to the observable to ensure it returns true/false – Nico Mar 11 '18 at 22:18
  • I've updated the question. Im still getting a bunch of console error logs (exactly the one i typed above in a comment). I believe it has to do with this AdminOnly class. I have updated my others just like your gist. I have also succesfully imported rxjs operators. Can you help me in this last task? – Lucas Arruda Mar 11 '18 at 22:29
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/166640/discussion-between-nico-and-lucas-arruda). – Nico Mar 11 '18 at 22:37