Thinkster
Firebase
Build a Real-Time Slack Clone using AngularFire

Creating the Auth Controller

Learn how to create an AngularJS controller to manage authentication with Firebase

Create a controller named AuthCtrl and inject Auth and $state into it.
angular.module('angularfireSlackApp')
  .controller('AuthCtrl', function(Auth, $state){
    var authCtrl = this;

  });

The $state service is provided by ui-router for us to control the state of our application. We can use the go() function on $state to redirect our application to a specific state. We also create a reference to the this keyword within our controller because we're using the controller as syntax. For more information about this syntax, see this lesson.

Next, create a user object on our controller with empty strings for email and password
angular.module('angularfireSlackApp')
  .controller('AuthCtrl', function(Auth, $state){
    var authCtrl = this;

    authCtrl.user = {
      email: '',
      password: ''
    };
  });

This user object will be used with the ng-model directive in our form. Next, we'll need two functions on our controller, one for registering users and one for logging in users. $firebaseAuth provides us with two functions: $authWithPassword for logging in users and $createUser for registering users. Both of these functions take a user object like the one we initialized on our controller, and return a promise. If you're not familiar with how promises work, read this to learn more about promises.

Create the following login function in AuthCtrl:
authCtrl.login = function (){
  Auth.$signInWithEmailAndPassword(authCtrl.user.email, authCtrl.user.password).then(function (auth){
    $state.go('home');
  }, function (error){
    authCtrl.error = error;
  });
};

When authentication is successful, we want to send the user to the home state. When it fails, we want to set the error on our controller so we can display the error message to our user.

Create the following register function in AuthCtrl
authCtrl.register = function (){
  Auth.$createUserWithEmailAndPassword(authCtrl.user.email, authCtrl.user.password).then(function (user){
    $state.go('home');
  }, function (error){
    authCtrl.error = error;
  });
};

Our register function is nearly identical to our login function, it calls $createUserWithEmailAndPassword instead of $signInWithEmailAndPassword. Now that we have our authentication service and controller created, let's update our templates and put them to use.

In app/index.html, include the auth service and controller after where app.js is included.
<script src="app.js"></script>
<script src="auth/auth.controller.js"></script>
<script src="auth/auth.service.js"></script>
In app/app.js, specify AuthCtrl as our controller for both the login and register states.
.state('login', {
  url: '/login',
  controller: 'AuthCtrl as authCtrl',
  templateUrl: 'auth/login.html'
})
.state('register', {
  url: '/register',
  controller: 'AuthCtrl as authCtrl',
  templateUrl: 'auth/register.html'
})
In app/auth/register.html, add ng-submit to the form and specify authCtrl.register() as the submit function.
Add ng-model="authCtrl.user.email" to the email input.
Add ng-model="authCtrl.user.password" to the password input.

The resulting form should look like this:

<form ng-submit="authCtrl.register()">
  <div class="input-group">
    <input type="email" class="form-control" placeholder="Email" ng-model="authCtrl.user.email">
  </div>
  <div class="input-group">
    <input type="password" class="form-control" placeholder="Password" ng-model="authCtrl.user.password">
  </div>
  <input type="submit" class="btn btn-default" value="Register">
</form>
Finally, add the following div just above the form:
<div ng-show="authCtrl.error">
  <span>{{ authCtrl.error.message }}</span>
</div>

This div will remain hidden until our authentication controller reaches an error, in which case the error message it will get displayed to our user. Next, let's update our login template in a similar fashion.

In app/auth/login.html, add ng-submit="authCtrl.login()" to the form.
Add ng-model="authCtrl.user.email" to the email input.
Add ng-model="authCtrl.user.password" to the password input.
<form ng-submit="authCtrl.login()">
  <div class="input-group">
    <input type="email" class="form-control" placeholder="Email" ng-model="authCtrl.user.email">
  </div>
  <div class="input-group">
    <input type="password" class="form-control" placeholder="Password" ng-model="authCtrl.user.password">
  </div>
  <input type="submit" class="btn btn-default" value="Log In">
</form>
Finally, add the same div that we did for the register view just above the form for error handling:
<div ng-show="authCtrl.error">
  <span>{{ authCtrl.error.message }}</span>
</div>

Now we should have a working register and login system, but we have no way of telling if the user is logged in or not. The login and registration pages are still accessible if we are authenticated. We can resolve this by using the resolve property on our states. resolve allows us to create dependencies that can be injected into controllers or child states. These dependencies can depend on services in our app that return promises, and the promises will get resolved before our controller gets instantiated. Read the ui-router Github Wiki if you're not familiar with how resolve works with ui-router.

Add the following resolve property to both the login and register state:
resolve: {
  requireNoAuth: function($state, Auth){
    return Auth.$requireSignIn().then(function(auth){
      $state.go('home');
    }, function(error){
      return;
    });
  }
}

The $firebaseAuth service provides us with a $requireSignIn function which returns a promise. This promise will get resolved with an auth object if the user is logged in. The Firebase Documentation provides a table of what information is available to us within auth. If the user is not authenticated, the promise gets rejected. In our requireNoAuth dependency, if the User is logged in we want to send them back to the home state, otherwise, we need to catch the error that gets thrown and handle it gracefully by returning nothing, allowing the promise to be resolved instead of rejected. Now, we should no longer be able to access the login or register states if we're authenticated.

Check your work

You can view the completed & working code for this tutorial here: