Sunday 8 April 2018

Angular2 Firebase OAuth for Google, Facebook, Twitter and GitHub

Authentication is one of the common part of any web and mobile application, in this tutorial will cover how to use Firebase API in Angular2 project for open authentication. It includes Facebook, google, twitter and GitHub. We will use angular CLI for completing this project.
Code on Github                                                              Live Demo


Prerequisite
Application Flow
There will be 2 pages one is for login and another would be home page which has to be login protected, once user authenticate successfully with any of the login provider he will redirect to home page. Where we have to show user's displayName, photo and email address.
application flow chart


Get started

Install Angular CLI using below command
 :~$ npm install @angular/cli -g  
Create a new angular project
 :~$ ng new AngularAuth  
 :~$ cd AngularAuth  
 :~$ ng serve  
Application will be up and running on port 4200 http://localhost:4200/

Create Firebase App

You can create Google Firebase App from console Firebase Console




Install required dependencies firebase and angularfire2

For consume firebase API in angular we need to install firebase and angularfire2 lib using npm
 :~$ npm install firebase angularfire2 --save  

First we need to configure AngularFireModule and AnglarFireAuth as below

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppComponent } from './app.component';

import { AngularFireModule } from 'angularfire2';
import { AngularFireAuth } from 'angularfire2/auth';


export const firebaseConfig = {
 apiKey: "api-key",
 authDomain: "auth-domain",
 databaseURL: "database-url",
 projectId: "project-id",
 storageBucket: "storage-bucket",
 messagingSenderId: "sender-id"
};


@NgModule({
 declarations: [
   AppComponent,
 ],
 imports: [
   BrowserModule,
   FormsModule,
   HttpModule,
   AngularFireModule.initializeApp(firebaseConfig),
 ],
 providers: [AngularFireAuth],
 bootstrap: [AppComponent]
})
export class AppModule

Firebase json configuration you can get it from Firebase console click on “Add Firebase to your web app
Project Overview Page

Create an Authorization service

In order to use firebase authentication in our application it is good to encapsulate authentication functions in one service, Lets create a service using angular CLI.
 :~$ ng generate service providers/auth.service.ts  

auth.service.ts

import { Injectable } from '@angular/core';
import { AngularFireAuth } from 'angularfire2/auth';
import * as firebase from 'firebase/app';

@Injectable()
export class AuthService {

 constructor(public afa: AngularFireAuth) { }

  loginWithGoogle() {
   return this.afa.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
 }

 loginWithFacebook() {
   return this.afa.auth.signInWithPopup(new firebase.auth.FacebookAuthProvider());
 }

 loginWithTwitter() {
   return this.afa.auth.signInWithPopup(new firebase.auth.TwitterAuthProvider());
 }

 loginWithGitHub() {
   return this.afa.auth.signInWithPopup(new firebase.auth.GithubAuthProvider());
 }

 logout() {
   localStorage.clear();
   return this.afa.auth.signOut();
 }
}
We need to import AngularFireAuth from angularfire2 lib and firebase from firebase lib. Next, AngularFireAuth service is injected into the constructor as public in AuthService. logout() method we are clearing out the localstorage data, will use localStorage to stroe displayname, email, photourl from app.component.ts

The AuthService consist of 5 following methods

loginWithFacebook() loginWithGoogle() loginWithTwitter() loginWithGitHub() logout()
AngularFireAuth provide two methods for OAuth, one is signInWithPopUp(provider) and signInWithRedirect(provider), one opens popup window within the page and another redirect you to auth providers website respectively. Both method requires authentication object as parameter, Authentication object means which auth provider you want to use.

Create LoginPage Component

 :~$ ng generate component loginPage  
Let’s implement LoginPage Component, This component will wrap the AuthService methods to make available for login-page html template, Inject the AuthService and Router as below

login-page.component.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../providers/auth.service';


@Component({
 selector: 'app-login-page',
 templateUrl: './login-page.component.html',
 styleUrls: ['./login-page.component.css'],

})
export class LoginPageComponent implements OnInit {

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

 ngOnInit() {
 }

 loginGoogle() {
   this.authService.loginWithGoogle().then((data)=> {
     this.router.navigate(['']);
   });
 }

 loginFacebook() {
   this.authService.loginWithFacebook().then((data)=> {
     this.router.navigate(['']);
   });
 }

 loginTwitter() {
   this.authService.loginWithTwitter().then((data)=> {
     this.router.navigate(['']);
   });
 }

 loginGitHub() {
   this.authService.loginWithGitHub().then((data)=> {
     this.router.navigate(['']);
   });
 }
}
In above code snippet we have implemented 4 methods which returns the promise and once promise is resolved we are redirecting user to home page.

Login Page HTML

Let's create login html page, i'm using bootstrap 4 for designing this page and also custom css for social login buttons

login-page.component.html

<div class="container" style="margin-top: 20px">
 <div class="row">
  
   <div class="col-md-3">
     <button class="loginBtn loginBtn--facebook" type="button" 
              (click)="loginFacebook()">Login with Facebook</button>
   </div>
   <div class="col-md-3">
     <button class="loginBtn loginBtn--twitter" type="button" 
              (click)="loginTwitter()">Login with Twitter</button>
   </div>
   <div class="col-md-3">
     <button class="loginBtn loginBtn--google" type="button" 
              (click)="loginGoogle()">Login with Google</button>
   </div>
   <div class="col-md-3">
     <button class="loginBtn loginBtn--github" type="button" 
              (click)="loginGitHub()">Login with GitHub</button>
   </div>
 </div>
</div>
Each button attached an onclick event and its calling the exposed method by loginPage component i.e loginWith Facebook, Twitter, Google, GitHub respectively.

Create Home Page Component

 :~$ ng generate component homePage  
This component is to show user's displayName, email and photo, inject AuthService and Router and implement logout() method which will invalidate users logged-in session.

home-page.component.ts

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../providers/auth.service';

@Component({
 selector: 'app-home-page',
 templateUrl: './home-page.component.html',
 styleUrls: ['./home-page.component.css']
})
export class HomePageComponent implements OnInit {
 private displayName: string;
 private photoURL: string;
 private email: string;

 constructor(private authService: AuthService, private router: Router) {
   this.displayName = localStorage.getItem('displayName');
   this.photoURL = localStorage.getItem('photoURL');
   this.email = localStorage.getItem('email');
 }

 ngOnInit() {
 }

 logout() {
   this.authService.logout();
   this.router.navigate(['login']);
 }
}
We are getting the data from local storage which we will storing from app.component.ts

Home Page HTML

Let's create home page html, as we are using bootstrap 4 for desiging the pages. Here i'm using card to display photo, username and email

home-page.component.html

<div class="container">
 <div class="card" style="margin-top: 20px; width: 18rem;">
   <img class="card-img-top" src={{photoURL}} alt="Card image cap">
   <div class="card-body">
     <h5 class="card-title">Welcome {{displayName}}</h5>
     <p class="card-text">{{email}}</p>
     <a href="#" (click)="logout()" class="btn btn-primary">Logout</a>
   </div>
 </div>
</div>

Navigation

In this application we have two pages, so definitely will need two routes to navigate to these pages "/ " and "/login" for home page and login page respectively. Lets implement routing.

We are configuring routing in app.module.ts, add the below code snippet.

app.module.ts

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

const routes: Routes = [
 { path: '', component: HomePageComponent },
 { path: 'login', component: LoginPageComponent }
];

@NgModule({
 declarations: [
   AppComponent,
   HomePageComponent,
   LoginPageComponent
 ],
 imports: [
   BrowserModule,
   FormsModule,
   HttpModule,
   AngularFireModule.initializeApp(firebaseConfig),
   RouterModule.forRoot(routes)
 ],
 providers: [AngularFireAuth, AuthService],
 bootstrap: [AppComponent]
})

Auth Gaurd

Finally we have to protect our home page by unauthorized access, Let’s add the code in App component constructor by injecting AngularFireAuth and Router. AngularFireAuth has instance variable authState which returns firebase.User observable, so we can subscribe on and check the auth status.

app.component.ts

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';

import * as firebase from 'firebase/app';
import { AngularFireAuth } from 'angularfire2/auth';

@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.css']
})
export class AppComponent {

 private isLoggedIn: boolean;
 private displayName: string;

 user: Observable<firebase.User>;
 constructor(afAuth: AngularFireAuth, private router: Router) {
   this.user = afAuth.authState;
  
   this.user.subscribe((auth) => {
     if(auth == null) {
       this.isLoggedIn = false;
       console.log("auth is null");
       this.router.navigate(['login']);
     } else {
       this.isLoggedIn = true;
       this.displayName = auth.displayName;
       localStorage.setItem('displayName', auth.displayName);
       localStorage.setItem('email', auth.email);
       localStorage.setItem('photoURL', auth.photoURL);
     }
   });
 }
}
We have declared 2 instance variable which holds loggedin status and display name and then based on isLoggedIn variable we will show user displayName on common navbar.

Add common navbar in app component template

App component is a root component where we have our router outlet, so whatever we add in this page will display for all components.

app.component.html

<nav class="navbar navbar-dark bg-dark">
 <a href="#" class="navbar-brand">NinjaCoders.info</a>
<span class="pull-right" *ngIf="isLoggedIn" style="color: yellow">
                           {{displayName}}</span>
 <span class="pull-right" *ngIf="!isLoggedIn" style="color: yellow">
                           Logged Out</span>
</nav>
<div>
 <router-outlet></router-outlet>
</div>
If user is logged in we are showing displayName else showing status as Logged Out.

That's it.

Next tutorial will learn how to deploy Angular apps to Firebase and GitHub pages. 

No comments:

Post a Comment