R
R
Roman Khegay2020-06-04 08:43:38
Angular
Roman Khegay, 2020-06-04 08:43:38

How to properly use BehaviorSubject/Observable in guard?

Application on Ionic 5 / Angular 9. Ionic Data Storage

is used for data storage . auth.guard looks like this:

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

import { AuthService } from '@app/_services'

@Injectable({
    providedIn: 'root'
})

export class AuthGuard implements CanActivate {

    constructor(
        private router: Router,
        private authService: AuthService
    ) { }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        const accessToken = this.authService.currentTokenValue;

        if(accessToken) {
            return true;
        }

        this.router.navigate(['/login'])
        return false;
    }
}


auth.service:
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';

import { Storage } from '@ionic/storage'

import { BehaviorSubject, Observable, from } from 'rxjs'
import { map } from 'rxjs/operators';

import { Token } from '@app/_models'
import { environment } from '@env/environment';

@Injectable({
    providedIn: 'root'
})

export class AuthService {

    currentTokenSubject = new BehaviorSubject(null)

    constructor(
        private http: HttpClient,
        private storage: Storage,
    ) {
        this.getToken()
    }

    getToken() {
        this.storage.get('accessToken').then(res => {
            if (res) {
                this.currentTokenSubject.next(res)
            }
        })
    }

    public get currentTokenValue(): string {
        return this.currentTokenSubject.value;
    }

    login(username: string, password: string) {
        const headers = new HttpHeaders({
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'Authorization': 'Basic ' + btoa(username + ':' + unescape(encodeURIComponent(password)))
        })

        return this.http.post<Token>(`${environment.apiUrl}/auth/signin`, { }, { headers })
            .pipe(map((res: Token) => {
                let token = res.token
                // store user details and jwt token in local storage to keep user logged in between page refreshes
                this.storage.set('accessToken', token);
                return token;
            }));
    }

    logout() {
        // remove user from local storage to log user out
        this.storage.remove('accessToken');
        this.currentTokenSubject.next(null);
    }
}


well, jwt.interceptor:
import { Injectable } from '@angular/core'
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http'
import { Observable } from 'rxjs'

import { AuthService } from '@app/_services'

@Injectable()
export class JwtInterceptor implements HttpInterceptor {
    constructor(
        private authService: AuthService
    ) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // add authorization header with jwt token if available
        const currentToken = this.authService.currentTokenValue;

        if (currentToken) {
            request = request.clone({
                setHeaders: {
                    Authorization: `Bearer ${currentToken}`
                }
            });
        }

        return next.handle(request);
    }
}


The essence of the problem:
The get () method of Ionic Data Storage returns a promise and I need to wait for the resolve. But the rest of the structure works on Observable.

I get that:
The application is initialized, auth.guard is checked and currentTokenValue() returns null. The check does not pass and a redirect to the login page occurs, but the promise is implemented and the token is received, and the user can continue to use the application, since the auth.guard check returns true.

How to make friends with all this?

Answer the question

In order to leave comments, you need to log in

1 answer(s)
A
Anton Shvets, 2020-06-04
@Xuxicheta


Application is initialized, auth.guard is checked and currentTokenValue() returns null

Get a token before starting the application, using APP_INITIALIZER, it just uses the promise.
For example, like here https://blog.zverit.com/frontend/2017/06/17/app-in...
In general, a promise and an observable can be easily converted back and forth.
And the guard also works with a promise, I would immediately use Data Storage in it, and the service only to access the back. Angular can work with a promise in many places, it's just that it's usually not used, so it doesn't appear in the guides.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question