ChrisCv
ChrisCv

Reputation: 17

email Verified of AngularFire guards

I have a problem with registering a user on my app. In practice, from the Me tab button the user can access his Profile (if he has logged in, otherwise he is redirected to the Login page). enter image description here

The problem is that, after registering, the user can access the Profile page without having verified the email. I can't figure out how to use the emailVerified pipe https://github.com/angular/angularfire/blob/master/docs/auth/router-guards.md . Can anyone tell me how to implement this operation?

Tabs.routing.module:

const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['tabs/login']);
const routes: Routes = [
  {
    path: 'tabs',
    component: TabsPage,
    children: [
      {
        path: 'home',
        loadChildren: () => import('../home/home.module').then(m => m.HomePageModule)
      },
      {
        path: 'search',
        loadChildren: () => import('../search/search.module').then(m => m.SearchPageModule)
      },
      {
        path: 'profile',
        loadChildren: () => import('../profile/profile.module').then(m => m.ProfilePageModule),...canActivate(redirectUnauthorizedToLogin),
      },
      {
        path: 'login',
        loadChildren: () => import('../login/login.module').then(m => m.LoginPageModule)
      },
      {
        path: 'registration',
        loadChildren: () => import('../registration/registration.module').then(m => m.RegistrationPageModule)
      },
      {
        path: '',
        redirectTo: '/tabs/home',
        pathMatch: 'full'
      }
    ]
  },
  {
    path: '',
    redirectTo: '/tabs/home',
    pathMatch: 'full'
  }
];

Authentication-service:

import { Injectable, NgZone } from '@angular/core';
import { auth } from 'firebase/app';
import { User } from "./user";
import { Router } from "@angular/router";
import { AngularFireAuth } from "@angular/fire/auth";
import { AngularFirestore, AngularFirestoreDocument } from '@angular/fire/firestore';

@Injectable({
  providedIn: 'root'
})

export class AuthenticationService {
  userData: any;

  constructor(
    public afStore: AngularFirestore,
    public ngFireAuth: AngularFireAuth,
    public router: Router,  
    public ngZone: NgZone 
  ) {
    this.ngFireAuth.authState.subscribe(user => {
      if (user) {
        this.userData = user;
        localStorage.setItem('user', JSON.stringify(this.userData));
        JSON.parse(localStorage.getItem('user'));
      } else {
        localStorage.setItem('user', null);
        JSON.parse(localStorage.getItem('user'));
      }
    })
  }

  // Login in with email/password
  SignIn(email, password) {
    return this.ngFireAuth.signInWithEmailAndPassword(email, password)
  }

  // Register user with email/password
  RegisterUser(email, password) {
    return this.ngFireAuth.createUserWithEmailAndPassword(email, password)
  }

  // Email verification when new user register
  SendVerificationMail() {
    return this.ngFireAuth.currentUser.then(u => u.sendEmailVerification())
    .then(() => {
      this.router.navigate(['tabs/verify-email']);
    })
  }

  // Recover password
  PasswordRecover(passwordResetEmail) {
    return this.ngFireAuth.sendPasswordResetEmail(passwordResetEmail)
    .then(() => {
      window.alert('Password reset email has been sent, please check your inbox.');
    }).catch((error) => {
      window.alert(error)
    })
  }

  // Returns true when user is looged in
  get isLoggedIn(): boolean {
    const user = JSON.parse(localStorage.getItem('user'));
    return (user !== null && user.emailVerified !== false) ? true : false;
  }

  // Returns true when user's email is verified
  get isEmailVerified(): boolean {
    const user = JSON.parse(localStorage.getItem('user'));
    return (user.emailVerified !== false) ? true : false;
  }

  // Sign in with Gmail
  GoogleAuth() {
    return this.AuthLogin(new auth.GoogleAuthProvider());
  }

  // Auth providers
  AuthLogin(provider) {
    return this.ngFireAuth.signInWithPopup(provider)
    .then((result) => {
       this.ngZone.run(() => {
          this.router.navigate(['tabs/home']);
        })
      this.SetUserData(result.user);
    }).catch((error) => {
      window.alert(error)
    })
  }

  // Store user in localStorage
  SetUserData(user) {
    const userRef: AngularFirestoreDocument<any> = this.afStore.doc(`users/${user.uid}`);
    const userData: User = {
      uid: user.uid,
      email: user.email,
      displayName: user.displayName,
      photoURL: user.photoURL,
      emailVerified: user.emailVerified
    }
    return userRef.set(userData, {
      merge: true
    })
  }

  // Sign-out 
  SignOut() {
    return this.ngFireAuth.signOut().then(() => {
      localStorage.removeItem('user');
      this.router.navigate(['tabs/login']);
    })
  }

}

Upvotes: 0

Views: 1280

Answers (2)

waternova
waternova

Reputation: 1568

If you need to redirect to different paths if the user is unauthorized or has email unverified, you can use an inner map.

/** Redirect users to login screen or email confirmation screen */
const redirectToEmailConfirm: AuthPipeGenerator = (
  next: ActivatedRouteSnapshot,
  state: RouterStateSnapshot
) =>
  switchMap((user) => {
    return of(user).pipe(
      redirectUnauthorizedTo(`/login?redirectTo=${state.url}`),
      map((result) => {
        if (result === true) {
          // User is authorized
          if (user.emailVerified) {
            return true;
          } else {
            return ['/verify-email'];
          }
        } else {
          return result;
        }
      })
    );
  });

Upvotes: 3

ajheath
ajheath

Reputation: 153

I just had the same issue and managed to solve it like this. In your Tabs.routing.module document you import redirectUnauthorizedTo from AngularFire which by default checks if the user is logged in, not if the suer is logged in AND verified.

Instead you can write your own redirectUnauthorizedTo function called redirectUnverifiedTo like this.

First, make sure to import emailVerified and not redirectUnauthorizedTo.

import { AngularFireAuthGuard, emailVerified } from '@angular/fire/auth-guard';

Then define redirectUnverifiedTo.

const redirectUnverifiedTo = (redirect: any[]) => pipe(emailVerified, map(emailVerified => emailVerified || redirect));
const redirectUnauthorizedToLogin = () => redirectUnverifiedTo(['tabs/login']);

Finally you can use redirectUnauthorizedToLogin like normal!

Upvotes: 1

Related Questions