Защита маршрутов с использованием Firebase Authentication и Ngrx - PullRequest
0 голосов
/ 31 августа 2018

Корректная проверка статуса аутентификации с использованием хранилища Ngrx

Мне нужно проверить, аутентифицирован ли пользователь, когда я получаю доступ к какому-либо маршруту моего приложения, проверив хранилище на предмет состояния аутентификации пользователя.

Когда я получаю доступ к маршруту и ​​вызывается метод CanActivate(), я проверил, что состояние аутентификации является начальным набором состояний, мне нужно, чтобы статус аутентификации был изменен для аутентифицированного пользователя, если он есть.

В методе CanActivate() я пытался вызвать действие GetUser() перед выполнением выбора, проверяя, аутентифицирован ли пользователь или нет, но это не работает должным образом.

Ниже приведен код выполнения проверки.

Мое приложение использует службу Firebase Authentication , поэтому в моей службе аутентификации есть метод, который возвращает состояние аутентификации пользователя.

getUsuario(): Observable<firebase.User> {
    return this._angularFireAuth.authState;
}

auth.state.ts:

import { createFeatureSelector, createSelector } from '@ngrx/store';
import * as fromAuth from '../reducers/auth.reducer';

export interface AuthState {
    userData: any; // firebase user models
    isLoggedIn: boolean;
    error: any;
    loading?: boolean;
    loaded: boolean;
}

export const authInitialState: AuthState = {
    userData: null,
    isLoggedIn: false,
    error: null,
    loaded: false,
};

export const getAuthState = createFeatureSelector<AuthState>('auth');

export const getAuthUserData = createSelector(getAuthState, fromAuth.getUserData);
export const getAuthIsLoggedIn = createSelector(getAuthState, fromAuth.getIsUserLoggedIn);
export const getAuthError = createSelector(getAuthState, fromAuth.getError);

auth.reducer.ts:

import { AuthActions, ActionsTypes } from '../actions/auth.actions';
import { AuthState, authInitialState } from '../states/auth.state';

export function reducer(
    state = authInitialState, action: AuthActions): AuthState {

switch (action.type) {

    case ActionsTypes.GET_USER: {
        return {
            ...state,
            loaded: false,
            loading: true
        };
    }

    case ActionsTypes.AUTHENTICATED:
        return {
            ...state,
            userData: action.payload,
            loaded: true,
            loading: false,
            isLoggedIn: true
        };

    case ActionsTypes.NOT_AUTHENTICATED:
        return {
            ...state,
            ...authInitialState,
            loaded: true,
            loading: false,
            isLoggedIn: false
        };

    case ActionsTypes.LOGIN_EMAIL_PASSWORD:
        return {
            ...state,
            loading: true
        };

    case ActionsTypes.AUTH_ERROR:
        return {
            ...state,
            ...action.payload,
            loading: false
        };

    case ActionsTypes.LOGOUT:
        return {
            ...state,
            loading: true
        };

    default: {
        return state;
    }
}

}

export const getUserData = (state: AuthState) => state.userData;
export const getIsUserLoggedIn = (state: AuthState) => state.isLoggedIn;
export const getError = (state: AuthState) => state.error;

auth.effects.ts:

import { Injectable } from '@angular/core';

// Services
import { AuthService } from '../services/auth.service';

// RxJS
import { Observable } from 'rxjs/Observable';
import { map, switchMap, catchError } from 'rxjs/operators';
import { of } from 'rxjs/observable/of';

// NGRX
import { Action } from '@ngrx/store';
import { Actions, Effect } from '@ngrx/effects';
import * as authActions from '../actions/auth.actions';
import { Router } from '@angular/router';

@Injectable()
export class AuthEffects {
    constructor(
        private _router: Router,
        private _authService: AuthService,
        private _actions$: Actions,
    ) { }


    @Effect()
    getUser: Observable<Action> = this._actions$.ofType(authActions.ActionsTypes
        .GET_USER).pipe(
            map((action: authActions.GetUser) => action.payload),
            switchMap(() => this._authService.getUsuario()),
            map(user => {
                if (user) {
                    const usuario = {
                        uid: user.uid,
                        email: user.email,
                        displayName: user.displayName
                    };
                    return new authActions.Authenticated(usuario);
                } else {
                    this._router.navigate(['auth/login']);
                    return new authActions.NotAuthenticated();
                }
            })
        );

    @Effect()
    loginEmailPassword: Observable<Action> = this._actions$.ofType(authActions.ActionsTypes
        .LOGIN_EMAIL_PASSWORD).pipe(
            map((action: authActions.LoginEmailPassword) => action.payload),
            switchMap(usuario => this._authService
                .loginWithEmailAndPassword(usuario.email, usuario.password).pipe(
                    map(() => {
                        this._router.navigate(['']);
                        return new authActions.GetUser();
                    }),
                    catchError((err => of(new authActions.AuthError({ error: err }))))
                ))
        );

    @Effect()
    logout: Observable<Action> = this._actions$.ofType(authActions.ActionsTypes
        .LOGOUT).pipe(
            map((action: authActions.Logout) => action.payload),
            switchMap(() => this._authService.logout().pipe(
                map(() => {
                    this._router.navigate(['auth/login']);
                    return new authActions.NotAuthenticated();
                }),
                catchError((err => of(new authActions.AuthError({ error: err }))))
            ))
        );

    @Effect()
    reautenticaUsuario: Observable<Action> = this._actions$.ofType(authActions.ActionsTypes
        .REAUTHENTICATE).pipe(
            map((action: authActions.Reauthenticate) => action.payload.password),
            switchMap(password => this._authService
                .reauthenticateUsuario(password).pipe(
                    map(() => new authActions.ReauthenticateSuccess()),
                    catchError((err => of(new authActions.AuthError({ error: err }))))
                ))
        );

}

auth.guard.ts:

canActivate(): Observable<boolean> {
    this._store.dispatch(new fromAuth.GetUser());
    return this._store.select(getAuthIsLoggedIn);
}
...