4
votes

I am creating users dynamically in firestore auth and have added multiple types of claims i.e admin, instructor, assistant. So far i am able to login using the new created users and getting claims property as true i.e admin: true, instructor: true according to the login credentials i am providing. But i am unable to set up [AuthGuard] properly in routes and without even logging in i am able to redirect to components using urls and this should not be happening. I am a little bit confused as to how can i properly add AuthGuard. Here is my code

auth-service.ts

import * as firebase from 'firebase/app';

import { AngularFireAuth } from '@angular/fire/auth';
import { AngularFirestore } from '@angular/fire/firestore';
import { Injectable } from '@angular/core';
import { JwtHelperService } from '@auth0/angular-jwt';
import { Observable } from 'rxjs/Observable';
import { Router } from "@angular/router";

@Injectable() 
export class AuthService {
public user: Observable<firebase.User>;
public userDetails: firebase.User = null;

constructor(private _firebaseAuth: AngularFireAuth, private router: Router, 
    private _firestore: AngularFirestore,
    public jwtHelper: JwtHelperService ) {
    this.user = _firebaseAuth.authState;
    this.user.subscribe(
        (user) => {
            if(user) {
                this.userDetails = user;   
                this._firebaseAuth.auth.currentUser.getIdTokenResult(true).then(res => {
                    user= res.claims;
                })
            }
            else {
                this.userDetails = null;
            }
        }
    );
}
}

auth-guard.service.ts

import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/take';

import { CanActivate, Router } from '@angular/router';

import { AuthService } from './auth-service.service';
import { Injectable } from "@angular/core";
import { Observable } from "rxjs/Rx";
import { tap } from 'rxjs/operators';

@Injectable()

export class AuthGuard implements CanActivate {

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

    canActivate() {
      return this.auth.user
              .take(1)
              .map(authState => !!authState)
              .do(authenticated => {
                if (!authenticated) {
                    this.router.navigate(['auth/login']);
                }
              });

    }

  }

app-routing.module.ts

import { ExtraOptions, RouterModule, Routes } from '@angular/router';

import { AuthGuard } from './auth/auth-guard.service';
import { LoginComponent } from './auth/login/login.component'
import {
  NbAuthComponent,
} from '@nebular/auth';
import { NgModule } from '@angular/core';
import { RegisterComponent } from './auth/register/register.component';
import { RequestPasswordComponent } from './auth/request-password/request-password.component';
import { ResetPasswordComponent } from './auth/reset-password/reset-password.component';

const routes: Routes = [
  {
    path: 'pages',
    canActivate: [AuthGuard],
    loadChildren: () => import('../app/pages/pages.module')
      .then(m => m.PagesModule),
  },
  {
    path: 'studentcourseregistration',
    loadChildren: () => import('../app/studentcourseregistration/studentcourseregistration.module')
      .then(m => m.StudentcourseregistrationModule),
  },
  {
    path: 'auth',
    component: NbAuthComponent,
    children: [
      {
        path: '',
        component: LoginComponent,
      },
      {
        path: 'login',
        component: LoginComponent,
      },
      {
        path: 'register',
        component: RegisterComponent,
      },
      // {
      //   path: 'logout',
      //   component: ,
      // },
      {
        path: 'request-password',
        component: RequestPasswordComponent,
      },
      {
        path: 'reset-password',
        component: ResetPasswordComponent,
      },
    ],
  },
  // {
  //   path: 'student',
  //   loadChildren: () => import('../student/student.module')
  //     .then(m => m.StudentModule),
  // },
  { path: '', redirectTo: 'auth/login', pathMatch: 'full' },
  { path: '**', redirectTo: 'pages' },
];

const config: ExtraOptions = {
  useHash: false,
};

@NgModule({
  imports: [RouterModule.forRoot(routes, config)],
  exports: [RouterModule],
})
export class AppRoutingModule {
}

adding new user in auth like this

  this._firebaseAuth.auth.createUserWithEmailAndPassword(this.user.email, this.user.password)
  .then(cred => {
    const adminRole = firebase.functions().httpsCallable('addAdminRole');
    adminRole({email: this.user.email}).then(res => {
      console.log(res);
    })
  })
1
So any user sign in become an admin ? XD - Mises
@Mises there is no option for signup. There will be only one admin and that admin will be allowed to add other admins. instructors or assistants and mails will be sent to the newly added users to change their default passwords. The code is not yet in best shape as of now. Please point out the solution to my problem - ahsan nissar
Check are answear fit. - Mises

1 Answers

10
votes

It will be some thing like this:

import { CanActivate, Router } from '@angular/router';
import { Injectable } from "@angular/core";
import { AngularFireAuth } from '@angular/fire/auth';
import { take, switchMap } from 'rxjs/operators';

@Injectable()
export class AdminGuard implements CanActivate {

constructor(private auth: AngularFireAuth, private router: Router) { }

canActivate() {
    return this.auth.authState.pipe(
        take(1),
        switchMap(async (authState) => {
            if (!authState) { // check are user is logged in
                this.router.navigate(['/auth/login'])
                return false
            }
            const token = await authState.getIdTokenResult()
            if (!token.claims.admin) { // check claims
                this.router.navigate(['/auth/login'])
                return false
            }
            return true
        }),
    )
}
}