Firebase authentication - EGuillemot/Angular-test GitHub Wiki

Activate authentication

In Firebase

Activate authentification on Firebase and choose mail/password connexion mode

Create service

In src/app, ng g s services/auth/auth

In src/app/services/auth/auth.service.ts, add :

import * as firebase from 'firebase';
...
export class AuthService {
  ...
  createNewUser(email: string, password: string) {
    return new Promise(
      (resolve, reject) => {
        firebase.auth().createUserWithEmailAndPassword(email, password).then(
          () => {
            resolve();
          },
          (error) => {
            reject(error);
          }
        );
      }
    );
  }
  signInUser(email: string, password: string) {
    return new Promise(
      (resolve, reject) => {
        firebase.auth().signInWithEmailAndPassword(email, password).then(
          () => {
            resolve();
          },
          (error) => {
            reject(error);
          }
        );
      }
    );
  }
  signOutUser() {
    firebase.auth().signOut();
  }
}

Create components

In src/app/app.module.ts, add :

import { ReactiveFormsModule } from '@angular/forms';
...
@NgModule({
  ...
  imports: [
    ...
    ReactiveFormsModule
  ],
  ...
})
export class AppModule { }

Signup

In src/app, ng g c auth/signup

In src/app/auth/signup/signup.component.ts, add :

import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService } from '../../services/auth/auth.service';
...
export class SignupComponent implements OnInit {
  signupForm: FormGroup;
  errorMessage: string;
  constructor(private formBuilder: FormBuilder,
    private authService: AuthService,
    private router: Router) { }
  ngOnInit() {
    this.initForm();
  }
  initForm() {
    this.signupForm = this.formBuilder.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', [Validators.required, Validators.pattern(/[0-9a-zA-Z]{6,}/)]]
    });
  }
  onSubmit() {
    const email = this.signupForm.get('email').value;
    const password = this.signupForm.get('password').value;
    this.authService.createNewUser(email, password).then(
      () => {
        this.router.navigate(['/']);
      },
      (error) => {
        this.errorMessage = error;
      }
    );
  }
}

In src/app/auth/signup/signup.component.html, replace all by :

<div class="row">
  <div class="col-sm-8 col-sm-offset-2">
    <h2>Créer un compte</h2>
    <form [formGroup]="signupForm" (ngSubmit)="onSubmit()">
      <div class="form-group">
        <label for="email">Adresse mail</label>
        <input type="text" id="email" class="form-control" formControlName="email">
      </div>
      <div class="form-group">
        <label for="password">Mot de passe</label>
        <input type="password" id="password" class="form-control" formControlName="password">
      </div>
      <button class="btn btn-primary" type="submit" [disabled]="signupForm.invalid">Créer mon compte</button>
    </form>
    <p class="text-danger">{{ errorMessage }}</p>
  </div>
</div>

Signin

In src/app, ng g c auth/signin

In src/app/auth/signin/signin.component.ts, add :

import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService } from '../../services/auth/auth.service';
...
export class SigninComponent implements OnInit {
  signinForm: FormGroup;
  errorMessage: string;
  constructor(private formBuilder: FormBuilder,
    private authService: AuthService,
    private router: Router) { }
  ngOnInit() {
    this.initForm();
  }
  initForm() {
    this.signinForm = this.formBuilder.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', [Validators.required, Validators.pattern(/[0-9a-zA-Z]{6,}/)]]
    });
  }
  onSubmit() {
    const email = this.signinForm.get('email').value;
    const password = this.signinForm.get('password').value;
    this.authService.signInUser(email, password).then(
      () => {
        this.router.navigate(['/']);
      },
      (error) => {
        this.errorMessage = error;
      }
    );
  }
}

In src/app/auth/signin/signin.component.html, replace all by :

<div class="row">
  <div class="col-sm-8 col-sm-offset-2">
    <h2>Connexion</h2>
    <form [formGroup]="signinForm" (ngSubmit)="onSubmit()">
      <div class="form-group">
        <label for="email">Adresse mail</label>
        <input type="text" id="email" class="form-control" formControlName="email">
      </div>
      <div class="form-group">
        <label for="password">Mot de passe</label>
        <input type="password" id="password" class="form-control" formControlName="password">
      </div>
      <button class="btn btn-primary" type="submit" [disabled]="signinForm.invalid">Se connecter</button>
    </form>
    <p class="text-danger">{{ errorMessage }}</p>
  </div>
</div>

Header

In src/app/navigation/header/header.component.ts, add :

import * as firebase from 'firebase';
import { AuthService } from '../../services/auth/auth.service';
...
export class HeaderComponent implements OnInit {
  isAuth: boolean;
  ...
  constructor(private authService: AuthService) { }
  ngOnInit() {
    firebase.auth().onAuthStateChanged(
      (user) => {
        if(user) {
          this.isAuth = true;
        } else {
          this.isAuth = false;
        }
      }
    );
  }
  onSignOut() {
    this.authService.signOutUser();
  }
  ...
}

In src/app/navigation/header/header.component.html, add :

<mat-toolbar color="primary">
...
  <div fxFlex fxLayout fxLayoutAlign="end" fxHide.xs>
    <ul fxLayout fxLayoutGap="15px" class="navigation-items">
      ...
      <li *ngIf="isAuth">
        <button routerLink="/account" mat-flat-button color="accent">
          <mat-icon>account_circle</mat-icon> Profile
        </button>
        <button (click)="onSignOut()" mat-button>
          <mat-icon>exit_to_app</mat-icon> Sign out
        </button>
      </li>
      <li *ngIf="!isAuth">
        <button routerLink="/signin" mat-button>
          <mat-icon>person</mat-icon> Sign in
        </button>
        <button routerLink="/signup" mat-flat-button color="accent">
          <mat-icon>person_add</mat-icon> Sign up
        </button>
      </li>
    </ul>
  </div>
</mat-toolbar>

Authentication Guard

npm i rxjs
In src/app, ng g s services/authGuard/authGuard

In src/app/services/authGuard/authGuard.service.ts, add :

import { CanActivate, Router } from '@angular/router';
import { Observable } from 'rxjs';
import * as firebase from 'firebase';
...
export class AuthGuardService implements CanActivate {
  constructor(private router: Router) { }
  canActivate(): Observable<boolean> | Promise<boolean> | boolean {
    return new Promise(
      (resolve, reject) => {
        firebase.auth().onAuthStateChanged(
          (user) => {
            if(user) {
              resolve(true);
            } else {
              this.router.navigate(['signin']);
              resolve(false);
            }
          }
        );
      }
    );
  }
}

In src/app/app-routing.module.ts, add :

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'signup', component: SignupComponent },
  { path: 'signin', component: SigninComponent },
  { path: 'article', component: BookListComponent },
  { path: 'article/new', canActivate: [AuthGuardService], component: BookFormComponent },
  { path: 'article/view/:id', canActivate: [AuthGuardService], component: SingleBookComponent },
  { path: 'article/edit/:id', canActivate: [AuthGuardService], component: SingleBookComponent },
  { path: 'article/delete/:id', canActivate: [AuthGuardService], component: SingleBookComponent },
  { path: '**', component: PageNotFoundComponent }
];
⚠️ **GitHub.com Fallback** ⚠️