There are many different approaches to authentication in general. One size certainly does not fit all. However, I will explain one approach to solve this problem using the ionic framework and Angular.js.
First, we need to discuss how the backend web service will handle authentication. One common approach is for the backend web service to require that an auth token be sent with each resource request. To obtain an auth token, the client will make a login request with a username and password. The web service will authenticate the username and password and if the authentication is successful, it will generate an auth token and place it in the response back to the client.
All resource requests from the client will require the auth token to be placed on the HTTP request header with the “Authentication” key. The auth token will be checked to make sure it is still valid and has not expired. If the auth token is invalid or has expired, an HTTP response with status of 401 will be returned back to the client.
On the client side, once we get an auth token from a successful login, we save it locally so that we can pass it to the backend on subsequent resource requests. We really don’t want to have to keep track of whether or not the auth token has expired. In fact, what would really be great is if our client application could detect when a user needs to login, force the user to login, and then after login is successful, just carry on where it left off like nothing ever happened. Sounds good, right? So how do we implement this kind of behavior in a somewhat unobstrusive way? Well, with a little work and some help from an external library and the way Angular.js works, we can achieve that.
Witold Szczerba created a wonderful Angular.js module called angular-http-auth that will help us create an elegant solution. Angular.js has a concept of “interceptors”. The angular-http-auth module provides an “authService” that uses an interceptor to intercept HTTP requests and responses. If an HTTP response returns a status of 401, the “authService” will broadcast an event indicating login is required. Our application can then listen for this event and open a login modal allowing the user to login. Once the user has logged in successfully, we can let the “authService” know that login has been confirmed. The “authService” will then re-issue the previous HTTP request and execute the success/error/finally handlers associated to the original request. How awesome is that? That is exactly what we want!
So let’s build an Ionic mobile web application using the angular-http-auth library. You can build an ionic starter app and then fill in the blanks with the files below or you can download the complete project at ionic-http-auth
Our app will have a left side menu with “Home”, “Customers” and “Logout” links. The default landing page will be the “Home” page. For simulation purposes, we are going to use $httpBackend from the ngMockE2E mocking framework. We define this in our app.js file below.
app.js
angular.module('ionic-http-auth', ['ionic', 'ngMockE2E', 'ionic-http-auth.services', 'ionic-http-auth.controllers'])
.run(function($rootScope, $ionicPlatform, $httpBackend, $http) {
// Mocking code used for simulation purposes (using ngMockE2E module)
var authorized = false;
var customers = [{name: 'John Smith'}, {name: 'Tim Johnson'}];
// returns the current list of customers or a 401 depending on authorization flag
$httpBackend.whenGET('https://customers').respond(function (method, url, data, headers) {
return authorized ? [200, customers] : [401];
});
$httpBackend.whenPOST('https://login').respond(function(method, url, data) {
authorized = true;
return [200 , { authorizationToken: "NjMwNjM4OTQtMjE0Mi00ZWYzLWEzMDQtYWYyMjkyMzNiOGIy" } ];
});
$httpBackend.whenPOST('https://logout').respond(function(method, url, data) {
authorized = false;
return [200];
});
// All other http requests will pass through
$httpBackend.whenGET(/.*/).passThrough();
})
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: "/app",
abstract: true,
templateUrl: "templates/menu.html",
controller: 'AppCtrl'
})
.state('app.home', {
url: "/home",
views: {
'menuContent' : {
controller: "HomeCtrl",
templateUrl: "templates/home.html"
}
}
})
.state('app.customers', {
url: "/customers",
views: {
'menuContent' : {
controller: "CustomerCtrl",
templateUrl: "templates/customers.html"
}
}
})
.state('app.logout', {
url: "/logout",
views: {
'menuContent' : {
controller: "LogoutCtrl"
}
}
});
$urlRouterProvider.otherwise("/app/home");
});
index.html
In the index.html, in addition to the normal includes, we will include the http-auth-interceptor.js from angular-http-auth.
The application will be placed in the ion-nav-view tag.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width"> <title>Home</title> <!-- ionic css --> <link rel="stylesheet" href="lib/ionic/css/ionic.css"> <!-- your app's css --> <link rel="stylesheet" href="css/style.css" > <!-- ionic/angularjs scripts --> <script src="lib/ionic/js/ionic.bundle.js"></script> <script src="lib/ionic/js/angular/angular-mocks.js"></script> <!-- http-auth-interceptor to automatically detect login required --> <script src="lib/angular-http-auth/http-auth-interceptor.js"></script> <!-- cordova script (this will be a 404 during development) --> <script src="cordova.js"></script> <!-- your app's script --> <script src="js/app.js"></script> <script src="js/services.js"></script> <script src="js/controllers.js"></script> </head> <!-- 'ionic-http-auth' is the name of this angular module (js/app.js) --> <body ng-app="ionic-http-auth" animation="slide-left-right-ios7"> <ion-nav-view></ion-nav-view> </body> </html>
In the “menu.html” file, we will define the side menu and where the content for the menu links will go.
menu.html
<ion-side-menus> <ion-pane ion-side-menu-content> <ion-nav-bar class="bar-stable nav-title-slide-ios7"> <ion-nav-back-button class="button-clear"><i class="icon ion-chevron-left"></i> Back</ion-nav-back-button> </ion-nav-bar> <ion-nav-view name="menuContent" animation="slide-left-right"></ion-nav-view> </ion-pane> <ion-side-menu side="left"> <header class="bar bar-header bar-stable"> <h1 class="title">Menu</h1> </header> <ion-content class="has-header"> <ion-list> <ion-item nav-clear menu-close ui-sref="app.home"> Home </ion-item> <ion-item nav-clear menu-close ui-sref="app.customers"> Customers </ion-item> <ion-item nav-clear menu-close ui-sref="app.logout"> Logout </ion-item> </ion-list> </ion-content> </ion-side-menu> </ion-side-menus>
The “AppCtrl” will be responsible for loading our login modal.
The “LoginCtrl” will be responsible for allowing the user to login. The “CustomerCtrl” will be responsible for fetching the customers from a backend web service. The “LogoutCtrl” will be responsible for allowing the user to logout.
controllers.js
angular.module('ionic-http-auth.controllers', [])
.controller('AppCtrl', function($scope, $state, $ionicModal) {
$ionicModal.fromTemplateUrl('templates/login.html', function(modal) {
$scope.loginModal = modal;
},
{
scope: $scope,
animation: 'slide-in-up',
focusFirstInput: true
}
);
//Be sure to cleanup the modal by removing it from the DOM
$scope.$on('$destroy', function() {
$scope.loginModal.remove();
});
})
.controller('LoginCtrl', function($scope, $http, $state, AuthenticationService) {
$scope.message = "";
$scope.user = {
username: null,
password: null
};
$scope.login = function() {
AuthenticationService.login($scope.user);
};
$scope.$on('event:auth-loginRequired', function(e, rejection) {
$scope.loginModal.show();
});
$scope.$on('event:auth-loginConfirmed', function() {
$scope.username = null;
$scope.password = null;
$scope.loginModal.hide();
});
$scope.$on('event:auth-login-failed', function(e, status) {
var error = "Login failed.";
if (status == 401) {
error = "Invalid Username or Password.";
}
$scope.message = error;
});
$scope.$on('event:auth-logout-complete', function() {
$state.go('app.home', {}, {reload: true, inherit: false});
});
})
.controller('HomeCtrl', function($ionicViewService) {
// This a temporary solution to solve an issue where the back button is displayed when it should not be.
// This is fixed in the nightly ionic build so the next release should fix the issue
$ionicViewService.clearHistory();
})
.controller('CustomerCtrl', function($scope, $state, $http) {
$scope.customers = [];
$http.get('https://customers')
.success(function (data, status, headers, config) {
$scope.customers = data;
})
.error(function (data, status, headers, config) {
console.log("Error occurred. Status:" + status);
});
})
.controller('LogoutCtrl', function($scope, AuthenticationService) {
AuthenticationService.logout();
})
When the user selects the “Customers” link from the left side navigation menu, an HTTP request will be issued to get the customers. Since the user has NOT been authenticated, the response will return a status of 401. This status will be intercepted by the “authService” and an event called “event:auth-loginRequired” will be broadcast. In our “LoginCtrl”, we will provide an event handler for that event and show the “loginModal” when that event occurs.
When the user submits the request to login, we provide a special option on the config called “ignoreAuthModule”. By setting this to true, we are telling the “authService” to ignore a 401 status returned by this login request.
Once the user logs in successfully, the following takes place:
1. The authToken received from the backend web service is placed on the header.
2. We inform the “authService” that login was confirmed and provide a function to be executed on the previous request config which
will set the authToken on the header for the previous request.
3. An event called “event:auth-loginConfirmed” will be broadcast by the “authService”. We respond to that event in the “LoginCtrl”
by hiding the “loginModal”. Our previous request for “customers” will be resent by the “authService” and the appropriate
success/error/finally block will be executed as if nothing ever happened.
If the user login attempt fails, we broadcast an event called “event:auth-login-failed”. We respond to this event in the “LoginCtrl” by just displaying an error message.
When the user selects the “Logut” link, a call is made to the backend web service to logout. Then the authToken is removed from the header and an event called “event:auth-logout-complete” is broadcast. The “LoginCtrl” will respond to this event by navigating the user to the “Home” page and show the “loginModal”.
services.js
angular.module('ionic-http-auth.services', ['http-auth-interceptor'])
.factory('AuthenticationService', function($rootScope, $http, authService, $httpBackend) {
var service = {
login: function(user) {
$http.post('https://login', { user: user }, { ignoreAuthModule: true })
.success(function (data, status, headers, config) {
$http.defaults.headers.common.Authorization = data.authorizationToken; // Step 1
// Store the token in SharedPreferences for Android, and Keychain for iOS
// localStorage is not very secure
// Need to inform the http-auth-interceptor that
// the user has logged in successfully. To do this, we pass in a function that
// will configure the request headers with the authorization token so
// previously failed requests(aka with status == 401) will be resent with the
// authorization token placed in the header
authService.loginConfirmed(data, function(config) { // Step 2 & 3
config.headers.Authorization = data.authorizationToken;
return config;
});
})
.error(function (data, status, headers, config) {
$rootScope.$broadcast('event:auth-login-failed', status);
});
},
logout: function(user) {
$http.post('https://logout', {}, { ignoreAuthModule: true })
.finally(function(data) {
delete $http.defaults.headers.common.Authorization;
$rootScope.$broadcast('event:auth-logout-complete');
});
},
loginCancelled: function() {
authService.loginCancelled();
}
};
return service;
})
login.html
<div class="modal" ng-controller="LoginCtrl"> <ion-header-bar class="bar-stable"> <h1 class="title">Login</h1> </ion-header-bar> <ion-content has-header="true" padding="true"> <form class="list"> <div class="item item-divider"> Just tap the login button to simulate a successful login </div> <div>{{message}}</div> <label class="item item-input"> <input type="text" placeholder="Username" ng-model="user.username" required> </label> <label class="item item-input"> <input type="password" placeholder="Password" ng-model="user.password" required> </label> <div class="padding"> <button class="button button-block button-stable" ng-click="login()">Login</button> </div> </form> </ion-content> </div>
customers.html
<ion-view title="Customers"> <ion-nav-buttons side="left"> <button menu-toggle="left"class="button button-icon icon ion-navicon"></button> </ion-nav-buttons> <ion-content padding="true"> <div class="list"> <a ng-repeat="customer in customers" class="item"> <h2>{{ customer.name }}</h2> </a> </div> </ion-content> </ion-view>
home.html
<ion-view title="Home"> <ion-nav-buttons side="left"> <button menu-toggle="left"class="button button-icon icon ion-navicon"></button> </ion-nav-buttons> <ion-content padding="true"> <p>Welcome to the ionic-http-auth home page!</p> <p>Select the menu icon at the top of the page and select the Customers link.</p> <p>You will then be prompted to login. You can just tap the login button to be logged in.</p> <p>You will then see the customers displayed.</p> </ion-content> </ion-view>
So, without too much effort, we are able to provide a fairly transparent authentication solution. Remember, one size does not fit all, and your mileage may vary.
Hi,
Great article!
How to create server side?
You could write your own backend authentication, however, I wouldnt recommend it. Depending on what your backend service is written in, you should be able to find something that works the way I have described or at least similar. You might look at CAS – Central Authentication Service. You might also consider an OAuth2 solution as well. My article was more focused on the client side.
Hi there, I enjoy reading through your article.
I like to wriite a little comment to support you.
Glad you enjoyed it
Very nice overview and handy! Thanks for the write up!
Your welcome. Thanks.
Pingback: Best of JavaScript, HTML & CSS – Week of March 17, 2014 | Flippin' Awesome
Very nice article
Thanks for that article!
I searched for the “AuthenticationService” but couldn’t find it. Where can I find the source code?
Look in the services.js file above. It is defined in there.
is it possible to get the source code for this? BYT a brillant article
I’m a noob Ionic / Angular, but this tutorial as-is didn’t work for me.
using latest ionic.
I had to reverse the load-order of services & controllers.
Also, you store your templates in /view folder. That messed me up, since a starter app uses /templates.
I ended up having success by not copy/pasting your code, but assembling pain-stakingly block-by-block.
Sorry you had so much trouble. I forgot about the folder change for the views/templates. I plan on putting a project together for this on Github. I hope you at least learned something about ionic and angular in the process.
Great article!
A nice addition would be to describe how the server generated token could be created in a way that allows full scalability (saving the token in a database would make it a bottleneck in large scaled applications). Can anybody help with this?
It is a pretty language specific task. I disagree with your statement regarding the saving of the token in the database. If performance is an issue, you can use a cache for the token. How you implement this is very dependent on the language and environment you are using.
I am also an Ionic/AngularJS noob, but I couldn’t get the code to run as is.
I had to change the first line of controllers.js to:
angular.module(‘ionic-http-auth.controllers’, [])
Also, on line 15 make sure that you have the correct path to signin.html. I had placed mine in /templates rather than /views
Thanks for the reply. I will correct those issues. I am working on getting a project setup on GitHub. I also plan to write some more articles related to translations and generating error messages. I am going to incorporate those into the GitHub project as well.
Pingback: AngularJS | Pearltrees
I have fixed the issues reported previously. Also, as promised, I have put the code for the project in Github, You can see it here. I hope this helps previous readers as well as future readers.
Curious, let’s say I ran this app on my mobile phone. What happens if the user closes their app and reopens it? Is it going to ask for their login again? That’d be extremely annoying to have to login every time they opened the app.
Agreed, that would be annoying. I was presenting a more conservative example. To solve this problem, upon a successful login, you could store the authorization token in localStorage for example. Then you could check to see if it exists maybe in the .run block in app.js. If it does, you could place it on the $http headers. If not, you could either take the user to the signin modal or just wait until they make a request that needs authorized access. I hope that helps. Let me know if that doesn’t make sense.
A more secure approach would be to store the token in SharedPreferences for Android, and Keychain for iOS
Great article, thanks! Couldn’t find anything else like it so glad you wrote it!
There is just one little issue I can’t seem to get resolved or figured out…
I use the left slide out menu (ionic start myApp sidemenu), which displays the “hamburger menu” in the top left corner which under certain circumstances (don’t quite understand how states work yet) shows a back button instead of the menu button.
When logging out (rediredt to dashboard page with $state.go), the menu button and back button both show in the top left. The back button shouldn’t show here and I have no idea why it does!?
Any ideas? Thanks!
Nevermind! Didn’t read properly! See you mention the issue in your home controller…
Will wait for next ionic build to hopefully resolve this.
Came here from ionic forum, and this article really blown my mind about how to setup cordova app with Auth function ..
Thanks Keith, you really made my day 😀
Hi Keith,
btw how to automatically init ionicModal login when user land on http://localhost/ionic-http-auth/www/#/app/customers directly, instead refering from home?
Thanks
Why are you wanting to init the modal when the customers view is displayed ?
Great tutorial. May you please help me out to switch between views by clicking on a button. That means when i click on a button the view in the index.html should change. I have a button on home.html page, when i click on this button i should be able to see the one.html page. Please help me.
If you just wanted a link or button to a page that is in the menu, you could do this:
If you wanted the page to be a nested state, you would need to configure that state appropriately in app.js. See angular-ui-router for the documentation for creating states and nested states, etc.
Hi after wracking brain for a day this tutorial nudged me in right direction thanks a lot . I have couple of question
1). why are you injecting $http in LoginCtrl ??
2). Wouldn’t it be better if events are listening on $rootScope instead of LoginCtrl’s $scope .
1). why are you injecting $http in LoginCtrl ??
>> I just forgot to remove it. It is not needed in this controller.
2). Wouldn’t it be better if events are listening on $rootScope instead of LoginCtrl’s $scope .
>> I really don’t think it would be any better. The approach I used seems to be a pretty common pattern. A better approach might be to (inside LoginCtrl) put a $scope.$watch on an attribute in the AuthenticationService. Look at the documentation for $scope.$emit and $scope.$broadcast.
i can’t get it working if i put listeners on LoginCtrl’s $scope but using $rooScope it just works i think its some nesting issue . Can u give some code example for better approach you mentioned . Which attribute of AuthenticationService should i put $scope.$watch on.
After thinking about this a little more, I think it would be much more complicated to use the $scope.$watch approach. And it would really be of little benefit. Also, to use that approach, you would have to alter the http-auth module to have it set a variable. Then there would be a circular reference issue that you could get around with $injector. Starting to sound uglier and uglier. The only thing that might make that approach better would be saving the expense of broadcasting the event across the rootScope and down. You could use $rootScope to minimize the broadcast if you were to cancel it by using event.stopPropagation(). That might be better.
So, the short answer is, you should probably just stick to using $rootScope and just add the event.stopPropagation() to the event listeners.
For example:
$rootScope.$on(‘event:auth-loginConfirmed’, function(event) {
event.stopPropagation();
$scope.username = null;
$scope.password = null;
$scope.loginModal.hide();
}
});
wow… just what I was looking for… great article! Thank you!
hi , I have the error ” no more request expected” when i called my api authentification.
login: function(user) {
$http.post(‘MyURLapi/login’, { email : user.username, password : user.password }, { ignoreAuthModule: true })
.success(function (response, message, headers, config) {
$http.defaults.headers.common.Authorization = data.authorizationToken; // Step 1
Have you any idea where it may come ? I work locally for now and my api server must return me { response : true, message : Connecté }
thank you in advance
In app.js, you will need to change the $httpBackend mocks to match your http calls and have then return what you are expecting. Of course, these mocks would need to be removed when you use the actual end points.
So in your case, you need to change the “login” mock to something like this:
thank you for your reply.
I see, i must be remove this ? : $httpBackend.whenGET(‘UrlApi/login’).respond(function (method, url, data, headers) {return authorized ? [200, customers] : [401]; });
I test thank you
You’re welcome. Enjoy!
it works half; It does not check the value of response and connect matter logins and password.
I tried this but it does not change :
$httpBackend.whenPOST(‘myapi/login’).respond(function(method, url, data) {
if (response=true){
authorized = true;
return [200 , { response: true, message:””, authorizationToken: “NjMwNjM4OTQtMjE0Mi00ZWYzLWEzMDQtYWYyMjkyMzNiOGIy” } ];
}
else{
authorized=false;
}
});
Not sure what you are doing with this “response” attribute. You are setting it to true in the if condition. I think you meant this “if (response == true)”.
Where is the “response” variable defined and what are you using it for ?
thank you for the correction.
is it possible to enelevr httpbackend locally? How can I do to test with HTTP? I retrieves no information on the server side.
Sorry I started in angularjs.
Thank you in advance for your help,
I misunderstood the usefulness of httpbackend. I want to recover data from my API without simulation now. it works great with
Hi,
I was stuck at the API Login. Could you please share your app.js code. I dont want to simulate this with mock. I want to login with a api. could you please help me. Thanks in advance.
You will just need to make your authentication call using $http or $resource. Have a look here: https://github.com/witoldsz/angular-http-auth.
Pay special note to the section on Ignoring the 401 interceptor. Also, most likely you will need to provide a config block to setup previous failed requests. Like putting a token on the header, etc.
How can i show the login modal by default when user open the app? i need user to login to view my application
The easiest thing to do in the sample app that I have provided would be to add this to the .run block:
$rootScope.$broadcast(‘event:auth-loginRequired’);
You could add an if block to that to check and see if they have already logged in, depending on your requirements.
Thanks for the article!
I tried to broadcast an event of login required inside .run, but then the whole screen white out. Could you help me on this please? Thanks again!
Hi,
Thanks for your article ! Your script also works with the cookie ? May be with $ http .defaults. headers.common.Authorization = cookies.token; and ngCookies after login ?
You can use a cookie or localStorage/sessionStorage depending on your requirements.
A more secure approach would be to store the token in SharedPreferences for Android, and Keychain for iOS
Nice article, what is the github address?
The github address is in the post. For your convenience, here it is
ionic-http-auth
Anybody try this with Azure Mobile Services and the authentication it provides?
Nice article really so helpful…..
Hey,
Great article! thanks alot for the afford.
I have a question, where/how do you maintain the token so that the application would remember the user is authorized even if we close the app and reopen?
Thanks,
Ben
You can either use localStorage or a cookie. I prefer to use localStorage. When a successful login occurs, get the token and put it in localStorage. You can always add it to your header by doing something like this depending on what your backend is expecting:
$http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
A more secure approach would be to store the token in SharedPreferences for Android, and Keychain for iOS
Hi,
Thanks for the article I have implemented the code and I am able to get response 200 from my API end point. But I am not able to toggle between login & my dashboard page. I have sessionStorage which stores user data after login. How to keep that check if user data exist or not. I am try to build app similar to google analytics on iOS.
Not sure what the problem might be. Can you post the code somewhere for me to look at?
Hi,
It is not working on latest version of ionic . Please help me out.
I updated the ionic-http-auth project to use the ionic library to v1.0.0-beta.14 manually. Issuing the ionic command >$ ionic lib update only updates the ionic specific libraries.
I downloaded your repository and run command ” ionic lib update ” to update lib and after that it is not working like the way it works with old ionic.
Like first time you click on customer , it is showing modal , but if you click on empty space around modal then modal gets hide.
Also it is not showing up after clicking customer link again.
I updated the ionic-http-auth project to use the ionic library to v1.0.0-beta.14 manually. Issuing the ionic command >$ ionic lib update only updates the ionic specific libraries.
Can you please tell me cause of the problem ?
Hi,
Thanks for fixing this.
One more question.
I added camera plugin in one page and it is working fine but after clicking photo when it comes back to the page , it reloads to homepage.
Do you any cause of this problem?
Thanks in advance
Hi, thank you for this article.
I tried to use this logic in a project but I have a problem: even if my server returns a 401 header, when I check
rejection.status
it always is 404
It seems that I’m not the only one fighting with this issue
401 Never Being Caught by Interceptor
The same happens with 500 header.
Thanks in advance
I am not sure what the problem here is. I know that sometimes a 404 can mean that it was unable to reach the endpoint. Some common reasons, network unavailable, endpoint not found on the server, in some cases, the server may in fact be down. Have you inspected the request and response headers to see if there is any extra information on them?
Thanks for your quick answer. I checked the response and I think the headers from the server are right. Firebug and fiddler confirm the server answered with a 401 header. It seems to me that the only difference is between success and error. At line 12231 of ionic.bundle.js I see:
callback = function(event) {
removeEventListenerFn(script, "load", callback);
removeEventListenerFn(script, "error", callback);
rawDocument.body.removeChild(script);
script = null;
var status = -1;
var text = "unknown";
if (event) {
if (event.type === "load" && !callbacks[callbackId].called) {
event = { type: "error" };
}
text = event.type;
status = event.type === "error" ? 404 : 200;
}
if (done) {
done(status, text);
}
};
From what I understand it seems that the status is set to 200 in case of success and to 404 in case of error, whatever the error code.
I tried two different servers, both php. The php code I use for my tests is the following:
header($_SERVER['SERVER_PROTOCOL'] . ' 401 Unauthorized', true, 401);
die();
I tried many variations of this code always with the same result.
Thanks for your time
Hi
I downloaded the whole project from this site after I tried hours to add these code to my own example project.
I don’t know what I doing wrong but for me it is not really working:
Start the ‘normal’ login workflow for the first time. Here all works as expected (except the problem that the login dialog is not really shown modal).
But then click logout and after this click customers again. You see the mocked data again without the need to login.
Only when you completely reload the page the login works as expected again.
What is the problem here?
I expect that when I clicked logout and then customers again that it asks me for login (because I clicked logout before)…
Also I see that the controllers.js is different at line 50. At the page here you wrote $state.go… and in the downloaded project there is only a console.log…
Regards,
SixDark
I corrected the problem when clicking on the Customers option after logging out. The reason this was a problem is because with beta14, the views are cached by default. As a quick fix, I disabled caching on the customers view. Also, the logout needed to be wrapped in an $ionicView.enter event so that the logout will occur. I could have disabled caching on this view as well but I decided this would demo this new feature. Good catch on the logout complete event handler. I corrected that as well. I also upgraded the side menu as well. Have a look at the Github repo to see the changes I made. I hope that helps you out.
Cheers,
Keith
Hi Keith,
thanks for your fast answer! Crazy…
The first I think was there must something with caching but I am new to AngularJS/ionic and I could not found something helpful in the web.
I played around and in a normal browser I found a workaround: add ‘$window.location.href = $location.protocol() + “://” + $location.host() + “:” + $location.port() + “/”;’ to the event ‘auth-logout-complete’ (this forces a redirect to Home and a complete refresh of the page). But this – for sure – was not working in an Android app.
Your approach is working well also in an app!
So I can try now to replace all the mocked stuff with our API login.
Thank you very much! Very good work!
Regards,
SixDark
Thanks for the great article!
I am exploring prompt to login at the start of app. I had this in the .run in app.js, and it seems that screen is blanked out.
$rootScope.$broadcast(‘event:auth-loginRequired’);
Could you take a look at this issue please and see if the same happens to you? Or maybe there is something else I need to add? Thanks!
Try replacing HomeCtrl with the following:
Thanks much! Mind if you briefly tell why this would work and putting the broadcast in .run doesn’t? Is that because of some scope or?
If you put it in the .run then you would need to do it as part of a state change or something along those lines.
If you just put it there directly, it will run before the rest of the app has been loaded. Check the console log and you will see what I mean. You could do something like the below:
This fires every time you visit the home tab. How would you just get it to fire once upon initial successful login?
Can you be more specific? I’m not sure what you are referring to.
In the above homectrl code the login modal shows when you navigate away and then navigate back. I am looking to do something similar. I want to make sure a login screen is presented on app start if user has not previously signed in.
I want to do the following.
1. Require login if token does not exist in local storage upon app start
2. If token exists then let user through
Have you any example on this? Great post!
Your best bet might be to add some code to the AppCtrl.
Hi,
This out-of-the-box solution works fine with the mock code but as soon as I change the GET url in Customers controller to my actual server url, I can see a 401 being returned in FireBug, but the 401 is not intercepted and the login does not display. Would you know why this would be happening? I am not very experienced in Angular/Ionic yet.
Thanks!
I apologize for not responding sooner. I have been juggling several projects lately. I would have to look at your code to really determine your issue. You need to make sure you are not adding the ignoreAuthModule option when making your normal http calls. That option is only used on the login and logout methods. That may very well be your problem. You could put some log statements in the angular-http-auth httpInterceptor functions to see why the 401 is not being captured. I hope that helps.
Hi! thanks for the post, i’ve changed the code to connect with my web api, but i get this error
Error: Unexpected request: GET templates/home.html
No more request expected
Error: Unexpected request: GET templates/login.html
No more request expected
Error: Unexpected request: GET templates/customers.html
No more request expected
ReferenceError: $state is not defined
at http://localhost:8082/js/app.js:40:17
Error: Unexpected request: GET templates/menu.html
No more request expected
at $httpBackend (http://localhost:8082/lib/ionic/js/angular/angular-mocks.js:1177:9)
at sendReq (http://localhost:8082/lib/ionic/js/ionic.bundle.js:17408:9)
at $get.serverRequest (http://localhost:8082/lib/ionic/js/ionic.bundle.js:17126:16)
at processQueue (http://localhost:8082/lib/ionic/js/ionic.bundle.js:20962:27)
at http://localhost:8082/lib/ionic/js/ionic.bundle.js:20978:27
at Scope.$get.Scope.$eval (http://localhost:8082/lib/ionic/js/ionic.bundle.js:22178:28)
at Scope.$get.Scope.$digest (http://localhost:8082/lib/ionic/js/ionic.bundle.js:21994:31)
at Scope.$get.Scope.$apply (http://localhost:8082/lib/ionic/js/ionic.bundle.js:22282:24)
at bootstrapApply (http://localhost:8082/lib/ionic/js/ionic.bundle.js:9266:15)
at Object.invoke (http://localhost:8082/lib/ionic/js/ionic.bundle.js:11994:17)
Looks like you need to remove the mocking code altogether since it is only needed for demo purposes.
Hello ,
And many thanks for the tutorial…
I do have a problem, if you can help me aout, it would be great …
When trying to login agains my own php server , I change only the url in the POST request in the authentiation service , changing with my own address.
var service = {
login: function(user) {
$http.post(‘js/login.php’, { user: user }, { ignoreAuthModule: })
The Post request does not get executed …What am I doeing wrong ? Please help :)Thank you ..
Looks like you need to do this:
Be sure to remove the mocking code as well. That url doesn’t look like a valid url. Not real familiar with php but it seems you need to call an actual url that is being hosted. Either locally or remote.
..thank you for your fast reply…
I changed the POST code to the following:
$http.post(‘http://localhost/test/login.php’, { user: user }, { ignoreAuthModule: true })
When I click login, the error block is exexuted and I do not see anything while inspecting the http traffic.
I am very new to angular…And when you say remove mock ing code, is the
var authorized = false; from app.js on line 4 not imprtant ? And do I have to remove also the dependencies ‘ngMockE2E’ and $httpBackend ?
I aplogize for my dumb questions, I hope I can get it working…
I have my own customers.php file , and the communication with it seems to work fine , with the get request $http.get(‘customers.php’).
I do not unserstand why I do not see any http traffic while clicking on Login button ….Please help
…And thank you :):)
Yes, you can remove all the code in the .run block. You no longer need ngMockE2E as well. You should see the http call after removing that code.
Many many thanks
…You helped me a lot ..it works …:)
I hope I understood everything …I still had a problem while trying to access the token , but then I noticed that the token was accessed via json , not via the http headers ..
Many many thanks , again .
No problem. Glad to hear you got it working. As far as where and how to obtain the token; that depends on where the backend places it.
I can not get the logout function to work, even downloaded the project as a whole, ran it and it does not log out?
NVM, it does log out on the demo, sorry long day. It does not log out now that I have added nested views as well? I cant figure out why it wont log out.
So I know the biggest problem I am having is my view is not refreshing when clicking logout, I am trying to use a tab button to do the logout. any suggestions?
Most likely it is because by default the views are cached. See the “Caching” section here: http://ionicframework.com/docs/api/directive/ionNavView/
Would be great if you could include the commonly needed localStorage of the auth token in your example for a fully useful auth template
Good suggestion. Some people have asked about that. I have updated the project on github with local storage as well as some bower updates.
A more secure approach would be to store the token in SharedPreferences for Android, and Keychain for iOS
Pingback: Best of JavaScript, HTML & CSS – Week of March 17, 2014 - Modern Web
Didn’t work for me. I’m new in Ionic and I don’t understand the problem. Here is the error:
Uncaught Error: [$injector:modulerr] Failed to instantiate module ionic-http-auth due to:
Error: [$injector:modulerr] Failed to instantiate module ngMockE2E due to:
Error: [$injector:nomod] Module ‘ngMockE2E’ is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
http://errors.angularjs.org/1.3.13/$injector/nomod?p0=ngMockE2E
at http://localhost:8100/lib/ionic/js/ionic.bundle.js:8755:12
at http://localhost:8100/lib/ionic/js/ionic.bundle.js:10459:17
at ensure (http://localhost:8100/lib/ionic/js/ionic.bundle.js:10383:38)
at module (http://localhost:8100/lib/ionic/js/ionic.bundle.js:10457:14)
at http://localhost:8100/lib/ionic/js/ionic.bundle.js:12789:22
at forEach (http://localhost:8100/lib/ionic/js/ionic.bundle.js:9015:20)
at loadModules (http://localhost:8100/lib/ionic/js/ionic.bundle.js:12773:5)
at http://localhost:8100/lib/ionic/js/ionic.bundle.js:12790:40
at forEach (http://localhost:8100/lib/ionic/js/ionic.bundle.js:9015:20)
at loadModules (http://localhost:8100/lib/ionic/js/ionic.bundle.js:12773:5)
Try cloning the repo at: https://github.com/keithdmoore/ionic-http-auth
Then execute npm install
Pingback: Mobile Web Weekly No.1 | ENUE Blog
Hi,
Great work.. Just need few clarification
The username and password which is sent the backend, won’t it be easily hackable by middle men.
does https call make it secure or are there other ways to encrypt them further.
There are several approaches and areas that can help. Here are a couple.
1. Certificates should be used to prevent “man in the middle” attacks.
2. Tokens should expire within a reasonable amount of time. For example, maybe 1 hour.
Pingback: Basic Auth in ionic framework / AngularJS | Hosik. C.
This is AWESOME bro ! nice tutorial, thank you so much !!!
Hello,
I am trying to run your app in my local system. I have downloaded your project from github repo. I am not able to run the app as you said. It is showing me a blank page. Also, I coulnt find any lib folder as you have mentioned in you index.html. The below mentioned files are not available in the project. Could you please share these files if you have ASAP. Thanks in Advance.
Did you run npm install? Did it have errors?
Hi, thank you this is a great article. I am very new to ionic and returning to coding after 10+ years of paper pushing.
Is it possible for you to put an example that doesn’t use ngMockE2E and httpBackend. For example,
Lets say I have a a JWT token based login api
http://localhost/v1/authenticate?email=emailaddress&password=psss
When a post request is used – it gives the following response
{“token”:”some long token string”}
and if you use v1/authenticate/user?token=the token above
It returns
{“user”:{“id”:”12″,”name”:”myname”,”email”:”emailaddress”,”created_at”:”2015-10-14 06:45:36″,”updated_at”:”2015-10-14 06:45:36″}}
Thanks,
Sukh
Thanks. To get an idea of how you might go about that, take a look at: https://github.com/trendicity/trendicity
To make it more secure you could store the token in SharedPreferences(Android) or in the Keychain(iOS). Let me know if that helps or if you need more help.
Thank you keithdmoore for the nice description.
Can I do digest authentication here.
I want to connect to http://180.87.230.91:8089/ODKAggregate/formList
In Google chrome browser or any browser it is taking digest authentication.
I want to do it in my ionic app.
I can I do it by the help of this example. I know this example will help me but bit confused between $httpBackend and $http.
Can you help me.
Thank you.
`$http` is used to make a backend request to a server. `$httpBackend` is usually used for integrated testing. I am using it for demo purposes.
I would recommend that you do a Google search for `digest authentication angularjs` and maybe try incorporating one of those libraries into your Ionic project.