Authentication for Ionic Apps
In a previous post, I wrote about updating Vardyger's Admin UI in order to add support for internationalisation (i18n) and localisation (l10n). In this post, we'll update the Admin UI in order to add support for authentication.
The Cookie Monster
There are two common approaches to implementing server-side authentication for single page applications (SPAs): cookie-based authentication; and token-based authentication.
The cookie-based approach (which uses server-side cookies to authenticate the user on every request) is the most common, although it is rapidly being replaced by token-based authentication (which relies on a signed token that is sent to the server on each request).
Note: To learn more about the pros and cons of each approach, check out the links in the "References" section at the bottom of this post.
I'm going to start out by using the angular-http-auth module, the angular-local-storage module and the angular-mocks module (to simulate responses from Vardyger's RESTful API), before I take a detailed look at both token-based authentication and cookie-based authentication in subsequent posts.
Install angular-http-auth
angular-http-auth is an AngularJS module that makes it easy to add support for authentication to your Ionic apps.
To install angular-http-auth
, enter the following command:
bower install --save angular-http-auth
We also want to take advantage of the angular-local-storage and the angular-mocks modules, so let's install them too:
bower install --save angular-local-storage
bower install --save-dev angular-mocks
Note: If you use --save-dev
Bower will add the module to the devDependencies array in your project's bower.json
.
Update index.html
We need to update index.html
so that it includes the angular-http-auth
, the angular-local-storage
and the angular-mocks
scripts:
...
<!-- bower:js -->
...
<script src="bower_components/angular-http-auth/src/http-auth-interceptor.js"></script>
<script src="bower_components/angular-local-storage/dist/angular-local-storage.js"></script>
<!-- endbower -->
<script src="bower_components/angular-mocks/angular-mocks.js"></script>
...
Update app.js
First, we need to inject the angular-http-auth
(http-auth-interceptor), the angular-local-storage
(LocalStorageModule) and the angular-mocks
(ngMockE2E) modules into our app:
...
angular.module('vardyger', [
'ionic', // inject the Ionic framework
'http-auth-interceptor', // inject the angular-http-auth module
'LocalStorageModule', // inject the angular-local-storage module
'ngMockE2E', // inject the angular-mocks module
'pascalprecht.translate' // inject the angular-translate module
])
.config(function($ionicConfigProvider, $stateProvider,
$urlRouterProvider, $translateProvider) {
...
Next, we need to define a new nested state, app.welcome
:
.state('app.welcome', {
url: '/welcome',
cache: false,
views: {
'menuContent': {
templateUrl: 'templates/welcome-template.html',
}
}
})
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/app/welcome');
That will manage the Admin UI's new "Welcome" screen and serve as the apps fallback route. In English:
In German:
We also need to update the run
function:
.run(function($ionicPlatform, $httpBackend, localStorageService) {
...
$httpBackend.whenGET('https://posts')
.respond(function (method, url, data, headers) {
var authToken = localStorageService.get('authorizationToken');
return authToken ? [200, posts] : [401];
});
$httpBackend.whenPOST('https://login')
.respond(function(method, url, data) {
var authToken = 'NjMw ...';
return [200 , { authorizationToken: authToken } ];
});
$httpBackend.whenPOST('https://logout')
.respond(function(method, url, data) {
return [200];
});
$httpBackend.whenGET(/.*/).passThrough();
}
Which is where we define our mocks (by using $httpBackend
from the angular-mocks module) for our $http requests.
Update the MainController
User's can navigate to the "Content" screen from the side-menu:
We need to update the "Content" screen's MainController
(scripts/controllers/main-controller.js):
...
$http.get('https://posts')
.success(function (data, status, headers, config) {
$scope.listItems = data;
})
.error(function (data, status, headers, config) {
$log.error('An error occurred: ' + status);
});
....
Now, when a user chooses the "Content" item (from the side-menu) the MainController
will make a HTTP request and because the user has not been authenticated, the request will return a response with a HTTP status code of 401. The 401 will be intercepted by the angular-http-auth
module's “authService” and an event called event:auth-loginRequired
will be broadcast.
That means we also need to create a LoginController
(scripts/controllers/login-controller.js):
...
$scope.$on('event:auth-loginRequired', function(e, rejection) {
$scope.loginModal.show();
});
...
That provides an event handler for the auth-loginRequired
event, that prompts for the user's credentials:
When the user has logged in successfully, the previous HTTP request (for “posts”) will be resent by the authService
and the appropriate success/error block will be executed:
Update the side-menu template
We need to update the side-menu template (templates/side-menu-template.html
:
<ion-side-menus enable-menu-with-back-views="false">
...
<ion-side-menu side="left">
<ion-content class="has-header dark-bg">
<ion-list>
...
<ion-item menu-close class="item-icon-left item-dark"
ng-click="logout()" ng-show="isLoggedIn()">
{{ 'LOGOUT' | translate }}
</ion-item>
</ion-list>
</ion-content>
</ion-side-menu>
</ion-side-menus>
So, that it includes a "Logout" menu item:
Note: ng-show and the AppController
's isLoggedIn() function (see below) are used to show/hide the "Logout" menu item.
We also need to update the side-menu template's AppController
(scripts/controllers/app-controller.js):
Create the Authentication Service
Finally, we need to create the AuthenticationService
(scripts/services/authentication-service.js):
References:
- GitHub: angular-http-auth
- Keith D. Moore: Authentication with Ionic and AngularJS
- Stormpath: Token Based Authentication for Single Page Apps (SPAs)
- Auth0: Cookies vs Tokens - Getting auth right with AngularJS
- GitHub: The Vardyger publishing platform