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.
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
}
}));
}
}