# Angular Authentication

## Objectives

* Review the separation of concerns regarding Angular and Express
* Identify components needed to implement Angular authentication
* Create a service to provide authentication functions
* Utilize authentication functions

## Separation of Concerns

Creating APIs and Angular apps that consume them maintains **separation of concerns**. Meaning, our backend MVC is separated by our frontend MVC. This is beneficial because we can create multiple clients without altering the backend.

In order to connect the last pieces (authentication), we'll need to access the API endpoints that deal with JWT authentication.

## The Rundown of Angular Authentication

In order to interact with our API for authentication, we're going to do the following:

* Ensure the app has the correct endpoints protected
* Create a service to interact with the API endpoints
  * get JWT tokens (authenticate)
  * save JWT tokens (using localStorage)
  * delete JWT tokens (logout)
* Use the authentication service to store and delete tokens
* Create a service to send tokens with every request
  * inject an `Authorization` header into each request
  * configure the Angular app to use the header

Lastly, once authentication is working, we'll talk about adding a service for alerts.

### Ensuring Endpoints are protected

#### Starter Code

<https://github.com/WDI-SEA/angular-recipes>

Take a look at the starter code, which has an Express API and Angular app for creating and viewing **secret recipes** (such as the Krabby Patty secret formula, or Coca-cola formula). The API looks like this:

* Users
  * GET /api/users
  * POST /api/users
  * GET /api/users/:id
* Recipes
  * GET /api/recipes
  * POST /api/recipes
  * GET /api/recipes/:id
  * PUT /api/recipes/:id
  * DELETE /api/recipes/:id
* Auth
  * POST /api/auth

Our models are set up to include password hashing and JWT token generation at the `/api/auth` endpoint. Try running the app and verify that you can create, view, and delete secret recipes.

Our goal is to **lock down** these recipes so nobody can steal them. First, let's secure the endpoints by uncommenting these lines in `index.js`:

```javascript
app.use('/api/recipes', expressJWT({secret: secret}));
app.use('/api/users', expressJWT({secret: secret})
.unless({path: ['/api/users'], method: 'post'}));
```

Reviewing from the JWT authentication lesson, we can protect routes using middleware. `express-jwt` also provides an `.unless` function in order to exclude routes. This will allow us to `POST` to `/api/users`, or in other words, create a new user.

#### Another Review: JWT

Going back to JWT authentication, we authorize our user by sending the username and password to the server. If successful, we'll get a **token** that we can pass back and forth to the server. This token lets the server know we're authorized.

![JWT Auth](http://i.stack.imgur.com/hv4J2.png)

If we want to incorporate this concept into Angular, we'll need to create a **service** in order to store, retrieve, and delete these tokens.

### Creating an Authentication Service

We're going to create an authentication service for our app, and it'll have the following functions available to us:

* saveToken
  * saves the token to our app (using localStorage)
* getToken
  * retrieves the token from localStorage
* removeToken
  * removes the token from localStorage
* isLoggedIn
  * determines if a token exists in localStorage (basically a wrapper for getToken)
  * **Optional:** determine if the token has expired
* currentUser
  * gets the current user from the token

In `public/app/services.js`, let's create our service.

```javascript
//...

.factory('Auth', ['$window', function($window) {
  return {
    saveToken: function(token) {
      $window.localStorage['secretrecipes-token'] = token;
    },
    getToken: function() {
      return $window.localStorage['secretrecipes-token'];
    },
    removeToken: function() {
      $window.localStorage.removeItem('secretrecipes-token');
    },
    isLoggedIn: function() {
      var token = this.getToken();
      return token ? true : false;
    },
    currentUser: function() {
      if (this.isLoggedIn()) {
        var token = this.getToken();
        try {
          var payload = JSON.parse($window.atob(token.split('.')[1]));
          return payload;
        } catch(err) {
          return false;
        }
      }
    }
  }
}]);
```

**Some things to note:**

* In order to use `localStorage` and make our application more durable and testable, we inject `$window` into the service
* `saveToken`, `getToken`, and `removeToken` manipulate `localStorage`
* `isLoggedIn` checks to see if a token exists
* `currentUser` returns the entire payload of the JWT token, which should include the user
  * `var payload = JSON.parse($window.atob(token.split('.')[1]));`
    * `JSON.parse` will parse the `localStorage` string as JSON
    * `.atob` will decode base-64 encoded content (which is what the JWT token is stored as)
    * Items in the token are period-delimited. `token.split('.')[1]` will return the payload (second item)

### Testing the Authentication Service

Let's test the authentication service by adding functionality to **signup**, **login**, and **logout** routes.

#### Signup

If you go to `http://localhost:3000/signup`, we have a signup form, but there's no action defined for the `userSignup` function in the `SignupCtrl`. What do we have to do to POST to `/api/users`?

* Get data from form
* Use `$http` to POST to `/api/users`
* Get a response, then redirect to the home page

In `public/app/controllers.js`, let's add the following functionality to the `SignupCtrl`

```javascript
//...

.controller('SignupCtrl', ['$scope', '$http', '$location', function($scope, $http, $location) {
  $scope.user = {
    email: '',
    password: ''
  };
  $scope.userSignup = function() {
    $http.post('/api/users', $scope.user).then(function success(res) {
      $location.path('/');
    }, function error(res) {
      console.log(data);
    });
  }
}])

//...
```

We'll see that to keep things simple, we'll POST the user data to `/api/users` using `$http`, then redirect the user to the home page. We'll need `$http` and `$location` to be injected for both of these actions.

You can verify that a user was added to the database by checking the contents via MongoHub or another MongoDB client. We'll also verify in a second by implementing the `LoginCtrl`:

```javascript
//...

.controller('LoginCtrl', ['$scope', '$http', '$location', 'Auth', function($scope, $http, $location, Auth) {
  $scope.user = {
    email: '',
    password: ''
  };
  $scope.userLogin = function() {
    $http.post('/api/auth', $scope.user).then(function success(res) {
      Auth.saveToken(res.data.token);
      console.log('Token:', res.data.token)
      $location.path('/');
    }, function error(res) {
      console.log(data);
    });
  }
}])

//...
```

The `LoginCtrl` is similar to `SignupCtrl`, but with the following differences:

* We're POSTing to `/api/auth` instead of `/api/users`
* We're injecting our `Auth` service and calling the `.saveToken` function. Now, our token will be saved to `localStorage`!

You can verify that the token is being saved by creating a user, then logging in. Now go to the Chrome console and type the following:

```javascript
localStorage
```

There should be a key-value pair that looks like this:

```javascript
{secretrecipes-token: "tokenhere"}
```

Now to delete the token, we'll add a function to the `NavCtrl` in order to delete the token.

In `public/app/controllers.js`, alter the `NavCtrl` to contain the following:

```javascript
//...

.controller('NavCtrl', ['$scope', 'Auth', function($scope, Auth) {
  $scope.Auth = Auth;
  $scope.logout = function() {
    Auth.removeToken();
    console.log('My token:', Auth.getToken());
  }
}])

//...
```

Again, we're injecting the `Auth` service in order to remove the token. Also, we're assigning the `Auth` service to the controller's scope, which will allow us to call all of the service's functions. Let's modify the `navbar.html` template to reflect this.

In **public/app/views/navbar.html**

```markup
<div class="collapse navbar-collapse navbar-ex1-collapse">
  <ul class="nav navbar-nav navbar-right">
    <li><a href="/signup" ng-hide="Auth.isLoggedIn()">Signup</a></li>
    <li><a href="/login" ng-hide="Auth.isLoggedIn()">Login</a></li>
    <li><a ng-click="logout()" ng-show="Auth.isLoggedIn()">Logout</a></li>
  </ul>
</div>
```

### Sending Tokens on Each Request

So we kinda have logging in and logging out finished, but our Recipe endpoints are still coming back with 401 errors (unauthorized). This is because **we're not sending the token for each request.** But don't worry, we can send the tokens by creating, yes, another **service** and configuring our app to send the token with every request.

This service will specifically be an **interceptor**, because we'll configure our app to intercept requests and inject an Authorization header into each request.

In `public/app/services.js`:

```javascript
//..

.factory('AuthInterceptor', ['Auth', function(Auth) {
  return {
    request: function(config) {
      var token = Auth.getToken();
      if (token) {
        config.headers.Authorization = 'Bearer ' + token;
      }
      return config;
    }
  }
}])
```

Again, we're injecting the `Auth` service and returning a new service. This service must have a function called `request` that will take in the configuration for requests and spit it back out again. Specifically, we will:

* Get the JWT token
* See if the token exists
  * If so, add the token to the Authorization header
* Return the request configuration

In order for our app to use this configuration, we need to call `.config` and pass the service as an **interceptor** to `$httpProvider`. In `public/app/app.js`:

```javascript
//..

.config(['$httpProvider', function($httpProvider) {
  $httpProvider.interceptors.push('AuthInterceptor');
}])
```

To test and see if this configuration works, try logging in and see if you can get a list of recipes to appear on the home page.
