Right now I have a simple angular set up that has a login state and a cloud state. I want to make it so the cloud state can only be run if a user is authenticated. And if not, it will direct them to the login state.
So far I believe I have the "resolve" setup and I have the .run() function set up to redirect to the login state if there the resolve fails.
My only issue is, no matter what, my authenticated variable get's returned un-defined. I'm not sure how to incorporate the $http.get.
I'm new to angular, so If anyone has any suggestions, I'd gladly appreciate them.
var routerApp = angular.module('app', ['ui.router'])
.factory('Auth', function($http, $state, $q) {
var factory = { isLoggedIn: isLoggedIn };
return factory;
function isLoggedIn() {
$http.get('/auth/user').then(function(data) {
return true;
});
}
})
.config(function($stateProvider, $urlRouterProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$urlRouterProvider.otherwise('/cloud');
var authenticated = ['$q', 'Auth', function ($q, Auth) {
var deferred = $q.defer();
if(Auth.isLoggedIn()) deferred.resolve(); else deferred.reject();
return deferred.promise;
}];
var authGuest = function($state, Auth) {
if(Auth.isLoggedIn()) $state.transitionTo('cloud');
}
$stateProvider
.state('login', {
url: '/login',
templateUrl: "pages/templates/login.html",
onEnter: authGuest
})
.state('cloud', {
url: '/cloud',
templateUrl: "pages/templates/account.html",
resolve: { authenticated: authenticated }
})
})
.run(function ($rootScope, $state, $log) {
$rootScope.$on('$stateChangeError', function () {
$state.go('login');
});
});
First thing service method should return promise from isLoggedIn, other method should be using that promise to resolve it. Then you don't need to use use Auth.isLoggedIn() in if because anyhow it would be going to return an promise with catch, then & finally in it.
function isLoggedIn() {
retutn $http.get('/auth/user');
}
Then those method would be resolved using promise
var authenticated = ['$q', 'Auth', function ($q, Auth) {
var deferred = $q.defer();
Auth.isLoggedIn().then(function(){
deferred.resolve();
}, function(error){
deferred.reject();
});
return deferred.promise;
}];
var authGuest = function($state, Auth) {
Auth.isLoggedIn().then(function(){
$state.transitionTo('cloud');
})
}
Related
I have two http requests.
1. post method
2. get method.
I need to get data from two http request.
I added my code. But i think this is not correct approach. Please suggest me.
Question 1:
After two call to synchronous.
Controller
(function() {
'use strict';
angular.module('myApp').controller('loginController', loginController);
loginController.$inject = ['$auth', '$http', '$location', '$scope', '$window', '$rootScope', 'commonService', '$q', '$localStorage'];
// inject an auth service here!
function loginController($auth, $http, $location, $scope, $window, $rootScope, commonService, $q, $localStorage) {
commonService.getHostDetails().then(function(data) {
commonService.login().then(function(data) {
}).catch(function(data) {
alert('Sorry ! test function faild');
});
}).catch(function(data) {
alert('Sorry ! login function faild');
});
};
})();
Service Code:
angular.module('myApp').factory('commonService', ['$q', '$timeout', '$http',
function($q, $timeout, $http, commonService) {
return ( {
login : login,
test : test,
});
function login() {
// create a new instance of deferred
var deferred = $q.defer();
$http.get('host.json')
// handle success
.success(function(data, status) {
deferred.resolve(data);
})
// handle error
.error(function(data) {
deferred.reject(data);
});
// return promise object
return deferred.promise;
}
function test(formData) {
// create a new instance of deferred
var deferred = $q.defer();
console.log("in service");
console.log(formData);
$http.post('/api/test', formData, {
headers : {
'Content-Type' : 'application/json'
}
})
// handle success
.success(function(data) {
deferred.resolve(data);
})
// handle error
.error(function(data) {
deferred.reject(data);
});
// return promise object
return deferred.promise;
}
}]);
Question -2.
Also please suggestme two http request depends on two api.
First http request data need to parse into second api.
Promises are a great way of chaining you calls.
$http.post("your api").then(function(response) {
//do something
return response.data;
}).then(function(data) {
return $http.get("you other api")
}).then(response_from_second_api) {
});
If you have two calls then need to be resolved before you can do anything you could do something like that
var promise1 = $http.get("");
var promise2 = $http.get("");
$q.all([promise1,promise2]).then(values) {
var value_of_promise1 = values[0];
var value_of_promose2 = values[1];
});
I'm trying to check before every page load if his session is active, if not, redirect to the login page.
This is my App.js
var app = angular.module('Test', ['ngRoute']);
app.config(function($routeProvider){
$routeProvider
.otherwise({
resolve:
{
"checkUserSession": function($q,$http,$window)
{
console.log('start of the def')
var deffered = $q.defer();
$http.get('/general/php/getCurrentUser.php')
.then(function(result)
{
if(!result)
{
deffered.reject();
$window.location.href = "/";
}
else
{
deffered.resolve();
}
});
$window.location.href = "/";
return deffered.promise;
}
}
})
});
And it won't execute the console.log, the $http.get() or any of the redirects.
It doesn't matter if the promise is resolved or rejected, it looks likes the "checkUserSession" won't ever execute;
Every page includes the angular-route.js script.
Use an interceptor.
In my case I check for the authentication token in local storage but you could use your session logic here.
Start with angular.module(...).config -
.config(function ($stateProvider, $urlRouterProvider, $httpProvider) {
$httpProvider.interceptors.push('BearerAuthInterceptor');
...
Then use a service as following:
.factory('BearerAuthInterceptor', function ($q, $injector, $window) {
return {
request: function (config) {
config.headers = config.headers || {};
var tk = $window.localStorage.getItem('token');
if (tk) {
config.headers.Authorization = 'Bearer ' + tk;
}
return config || $q.when(config);
},
response: function (response) {
return response || $q.when(response);
},
responseError: function (rejection) {
if (rejection.status === 401) {
// Redirect to login ui-route
$injector.get('$state').go('login');
}
return $q.reject(rejection);
}
};
})
Some good examples here: http://www.webdeveasy.com/interceptors-in-angularjs-and-useful-examples/
I'm trying to get an authentication token from my REST API before calling any other endpoint (preferably once).
For this I created a token factory that calls the login and receives a token back. I then expect to inject that token factory into my other controllers. I was hoping that the dependencies where being respected but my controller calls the service before obtaining the token from the token factory. what did I do wrong ?
factory:
app.factory('tokenFactory', function($http, appConfig) {
console.log('calling endpoint: ' + appConfig.REST_ENDPOINT + 'authentication/login');
var apiToken;
$http.post(appConfig.REST_ENDPOINT + 'authentication/login', {
"username": "john",
"password": "open$esame"
}).
success(function(data) {
apiToken = data.token;
}).
error(function(data) {
//
});
return {
apiToken: apiToken
};
});
controller:
app.controller('clientListCtrl', function($scope, $http, appConfig, tokenFactory) {
console.log('calling endpoint: ' + appConfig.REST_ENDPOINT+'/client/list');
$http.get(appConfig.REST_ENDPOINT+'/client/list', {
header: { 'Authorization': tokenFactory.apiToken }
})
.success(function(data) {
$scope.clients = data;
}).
error(function(data, status) {
//
});
});
Yes, you have to take into account the asynchronous aspect of Ajax and leverage promise chaining (the $http.post actually returns a promise that you need to return). The factory will use the method getToken can define a success method to be notified when the result is received.
app.factory('tokenFactory', function($http, appConfig) {
console.log('calling endpoint: ' + appConfig.REST_ENDPOINT + 'authentication/login');
return {
getToken: function() {
return $http.post(appConfig.REST_ENDPOINT + 'authentication/login', {
"username": "john",
"password": "open$esame"
}).
success(function(data) {
return data.token;
}).
error(function(data) {
//
});
}
};
});
That said, I think that you should leverage the HTTP interceptor feature of Angular. This allows to transparently set the security token within your request. The first time the token is gotten using AJAX and then you can reuse this one.
app.factory('securityTokenInterceptor', function($q, tokenFactory) {
var currentToken = null;
return {
request: function(config) {
if (currentToken != null) {
config.headers['Authorization'] = currentToken;
return config;
}
var deferred = $q.defer();
tokenFactory.getToken().then(function(token) {
config.headers['Authorization'] = token.token;
currentToken = token.token;
deferred.resolve(config);
}, function(err) {
// Handle error (reject promise, ...)
});
return deferred.promise;
}
};
})
Here is the way to register your interceptor on $httpProvider:
app.config(function($httpProvider) {
$httpProvider.interceptors.push('securityTokenInterceptor');
})
Here is the fake factory I use to get token:
app.factory('tokenFactory', function($q, $timeout) {
return {
getToken: function() {
var deferred = $q.defer();
$timeout(function() {
deferred.resolve({token:'mytoken'});
}, 500);
return deferred.promise;
}
};
})
Hope it helps you,
Thierry
In your factory:
$http.post() is asynchronous, so the return after it will not contain the data coming from the post request. I would suggest returning the promise object you get from calling $http.post().
In your controller: you can use the returned promise and define the success method, in which you can do the get request.
tokenFactory.success(function (tokenData) {
token = tokenData.token;
$http.get(endpoint, { header: { 'auth': token } })
.success(...)
.error(...);
});
Not sure if it is the best way how to do it, but I think it could work this way.
I have a angular application with many $http request and i want redirect the users on the login page if the server session expires (get 401). Does anyone know a solution witch works for all $http without adding .error() on every $http?
It would be better if you could use an http interceptor to redirect all detected 401 errors.
// add an http interceptor via app.config
app.config(function($$httpProvider) {
$httpProvider.interceptors.push('my401Detector');
});
// interceptor logic.
app.factory('my401Detector', function($location, $q) {
return {
responseError: function(response) {
if(response.status === 401) {
$location.path('/login');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
};
});
You can use Interceptors to achieve this. From Mean.js source code
angular.module('users').config(['$httpProvider',
function($httpProvider) {
// Set the httpProvider "not authorized" interceptor
$httpProvider.interceptors.push(['$q', '$location', 'Authentication',
function($q, $location, Authentication) {
return {
responseError: function(rejection) {
switch (rejection.status) {
case 401:
// Deauthenticate the global user
Authentication.user = null;
// Redirect to signin page
$location.path('signin');
break;
case 403:
// Add unauthorized behaviour
break;
}
return $q.reject(rejection);
}
};
}
]);
}
]);
There is the following code:
angular.module('app.services', []).factory('authService', [
'SIGN_IN_ENDPOINT', 'SIGN_OUT_ENDPOINT', '$http', '$cookieStore', function(SIGN_IN_ENDPOINT, SIGN_OUT_ENDPOINT, $http, $cookieStore) {
var auth;
auth = {};
auth.signIn = function(credentials) {
return $http.post(SIGN_IN_ENDPOINT, {
user: credentials
}).then(function(response, status) {
return $cookieStore.put('user', response.data);
}, function(error) {
return console.log("Incorrect email/password");
});
};
return auth;
}
This is my module for authentication. Now I have the following function in controller:
angular.module('app.admin.controllers', []).controller('SignInController', [
'$scope', 'authService', '$state', function($scope, authService, $state) {
$scope.buttonText = "Login";
return $scope.login = function() {
$scope.buttonText = "Logging in. . .";
return authService.signIn($scope.credentials).then(function(response, status) {
return $state.go('allPosts');
}, function(err) {
$scope.invalidLogin = true;
return $scope.buttonText = "Login";
});
};
}
The problem: if I input wrong email/password, I'm waiting for 2 error callbacks - from the first 'then' from the second one, but I catch the first error callback (I can see console log) and after it THE SUCCESS callback executes! (return $state.go('allPosts')). Why? The Response from the server is 401 error.
The reason for this is, that you catch the error in the "app.services" and dont "bubble" the problem to higher tiers.
angular.module('app.services', []).factory('authService', [
'SIGN_IN_ENDPOINT', 'SIGN_OUT_ENDPOINT', '$http', '$cookieStore', function(SIGN_IN_ENDPOINT, SIGN_OUT_ENDPOINT, $http, $cookieStore) {
var auth;
auth = {};
auth.signIn = function(credentials) {
return $http.post(SIGN_IN_ENDPOINT, {
user: credentials
}).then(function(response, status) {
return $cookieStore.put('user', response.data);
}, function(error) {
console.log("Incorrect email/password");
return $q.reject(); //here is the important one.
});
};
return auth;
}
Or completely miss out the error handler.
auth.signIn = function(credentials) {
return $http.post(SIGN_IN_ENDPOINT, {
user: credentials
}).then(function(response, status) {
return $cookieStore.put('user', response.data);
});
};
If you catch the error and return a value within the error, following promises dont know about the occured error.
Since the auth service is returning the promise returned by then function, in the first error callback you need to return rejected promise.
You can do it in this way:
auth.signIn = function(credentials) {
return $http.post(SIGN_IN_ENDPOINT, {
user: credentials
}).then(function(response, status) {
return $cookieStore.put('user', response.data);
}, function(error) {
console.log("Incorrect email/password");
return $q.reject(error); // return a rejected promise;
});
};
Also remember to inject $q into your service, for this to work.
The promise returned by then is resolved with the return value of success and error callback functions.
If you do a standard return, you will always land up on the success path.
When you return $q.reject you are returning a promise that is eventually rejected. See documentation on $q.
You can also throw an exception from error callback in the service and get the same result.