Lesson Monday

Authentication is the process of identifying users by matching sign-in credentials with user account data. If a user’s login credentials are correct, that user will gain access to the application and any gated content (such as an email inbox or social media feed).

User authentication is a cornerstone of most web applications. Without it, it would be very challenging to provide access to personal content (such as emails) while also keeping that content secure.

There are a number of different ways to add authentication to an Angular application. One of the most popular tools is Passport.js, but there’s no easy way to implement Passport.js without adding further “backend” tools such as Express.js and Node.js.

Fortunately, Firebase provides an authentication solution that can be plugged directly into an Angular application! With Firebase doing the heavy lifting, we don’t need to worry about encrypting passwords and storing sensitive user data.

We’ll start by adding Firebase authentication to our application. In the next lesson, we’ll walk through the process of protecting some routes (so they are only available to authenticated users) while leaving other routes open to all users, including those that aren’t signed in.

Configuring the Root Module and Firebase

First, let’s create a new Angular application and add Firebase:

$ ng new authentication-example
$ npm install firebase angularfire2 --save

Next, we’ll need to configure our root module. Most of this should already look familiar:

src/app/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 { AngularFireDatabaseModule } from 'angularfire2/database';
import { AngularFireAuthModule } from 'angularfire2/auth';
import { masterFirebaseConfig } from './api-keys';

export const firebaseConfig = {
  apiKey: masterFirebaseConfig.apiKey,
  authDomain: masterFirebaseConfig.authDomain,
  databaseURL: masterFirebaseConfig.databaseURL,
  storageBucket: masterFirebaseConfig.storageBucket,
  messagingSenderId: masterFirebaseConfig.messagingSenderId
};

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

This is almost identical to the root module configuration from when we first started working with Firebase. There are only two differences:

We add import { AngularFireAuthModule } from 'angularfire2/auth’; to the root module’s imports.

We add AngularFireAuthModule to the imports section of the annotation.

Before we go on, we’ll need to create a new Firebase application. This should mostly be review. Go ahead and create the application, set up an api-keys.ts file that contains sensitive Firebase environmental variables, and make sure to add that file to .gitignore.

Once you’re finished with this configuration, there’s only one more thing to do. Inside your Firebase application, click on “Authentication” in the right-hand pane. You’ll see a number of different provider options, all of them with a status of “Disabled.” We’ll use Google to sign in, so click on “Google”, enable the provider, and click save. Later, you may want to experiment with other providers as well.

Creating an Authentication Service

We could add our authentication code directly to app.component.ts but that wouldn’t be good practice. Instead, we should separate the code into a service.

$ ng g service authentication

This will create our service in a folder called authentication, allowing us to better organize our code.

Our service is going to need login() and logout() methods. We’ll also need to keep track of the user that’s currently logged in. Let’s add some code to do that now:

src/app/authentication/authentication.service.ts
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { AngularFireAuth } from 'angularfire2/auth';
import * as firebase from 'firebase/app';

@Injectable()
export class AuthenticationService {
  user: Observable<firebase.User>;

  constructor(public afAuth: AngularFireAuth) {
    this.user = afAuth.authState;
  }

  login() {
    this.afAuth.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
  }

  logout() {
    this.afAuth.auth.signOut();
  }

}

Most of the new code you’ll see here comes directly from Angularfire2’s documentation on GitHub. Let’s go over the new material.

We import Observable so that we can instantiate a user in our service. This should look familiar, since we’ve worked with observables such as FirebaseListObservable before. We also import AngularFireAuth, which will allow us to add authorization. Finally, import * as firebase from 'firebase/app’; gives us access to Firebase itself.

Within our class declaration, we start by declaring a user variable, which will be an Observable.

We instantiate AngularFireAuth inside our service’s constructor and then set the value of our user by checking the instance’s authState.

Now we can add our login() and logout() methods. Firebase provides the code inside these methods.

And that’s it for our service. By exposing some basic methods, Firebase makes it relatively simple to add authentication to applications.

Adding Our Service to the Root Component

To keep our application as simple as possible, we’ll add a basic navbar with the option to login and logout. (Make sure to add Bootstrap to your application. Here’s a refresher if you need it).

Here’s our navbar:

src/app/app.component.html
<nav class="navbar navbar-default">
  <div class="container-fluid">
    <div class="navbar-header">
      <a class="navbar-brand" routerLink="">Authentication Example</a>
    </div>
    <ul class="nav navbar-nav navbar-right">
      <li *ngIf="isLoggedIn"> \\if the user is logged in, we’ll show that user’s name along with an event binding to logout.
        <a>{{userName}}</a>
        <a (click)="logout()">Log out</a>
      </li>
      <li *ngIf ="!isLoggedIn"> \\if the user isn’t logged in, we’ll have an event binding to login.
        <a (click)="login()">Log in</a>
      </li>
    </ul>
  </div>
</nav>

This won’t work yet, but it should be helpful for visualization purposes. We’ll have two *ngIf directives. If a user is logged in, the application will show a username and the option to logout. If a user is logged out, there will be an option to log in.

Let’s update app.component.ts and get this code working.

src/app/app.component.ts
import { Component } from '@angular/core';
import { AuthenticationService } from './authentication.service';

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

  constructor(public authService: AuthenticationService) {
    this.authService.user.subscribe(user =>  {
      console.log(user);
    });
  }

  login() {
    this.authService.login();
  }

  logout() {
    this.authService.logout();
  }
}

Most of this should be review. First, we import our AuthenticationService and add it to providers. We also have login() and logout() methods, which simply call the methods from our service.

In our class declaration, we have a variable for user, which doesn’t have a type because it’s declared in our service. (We could choose to add the Observable type here, but if we did, we’d also need to import Observable, which our service is handling for us.)

We also subscribe to the user from our authService. If you didn’t get a chance to familiarize yourself with subscribing in week 3 of JavaScript, now would be a good time to review that lesson. By subscribing, we’ll be able to watch for changes in the user’s status. In other words, any code within our subscribe function will run when the user’s status is updated to logged in or logged out. Subscribing will also allow us to look at the user’s properties, which we wouldn’t be able to access otherwise.

If we run $ ng serve and go to localhost:4200, we should see the navbar. Go ahead and open the console and then click on “Login.” If you entered valid Google credentials, you’ll see the user object, which has properties such as displayName and email. Our login functionality is working but we aren’t doing much with it yet.

Here’s what we want to do:

  1. When a user logs in, set isLoggedIn to true. Set the value of userName to the value of the displayName from the Firebase user object.
  2. When a user logs out, set isLoggedIn to false.

Here’s our updated class declaration:

src/app/app.component.ts
...
  user;
  private isLoggedIn: Boolean;
  private userName: String;

  constructor(public authService: AuthenticationService) {
    this.authService.user.subscribe(user => {
      if (user == null) {
        this.isLoggedIn = false;
      } else {
        this.isLoggedIn = true;
        this.userName = user.displayName;
      }
    });
  }


...

We’ve added variables for isLoggedIn and userName. We’ve also added a conditional inside our subscribe() method. If there’s no user, isLoggedIn should be false. If there is, isLoggedIn should be true and we’ll set the userName to the Firebase user’s displayName.

Now a user can log in and out of the application via the navbar. When they do so, they’ll see changes in the navbar that reflect their login status.

We’ve successfully implemented user authentication but it’s still not very useful yet. In the next lesson, we’ll add some basic authorization, making some routes available only to signed-in users.