angularjs interceptor $broadcast doesn't work - angularjs

I'm playing around with the code of
http://ngmodules.org/modules/http-auth-interceptor
and I'm wondering why
$rootScope.$broadcast('loginRequired');
doesn't trigger the alert in the controller
$scope.$on('loginRequired',function() {
alert('loginRequired');
});
The code:
<!doctype html>
<html ng-app="myModule">
<head>
<meta charset="utf-8">
</head>
<body>
<div id="content" class="ng-view"></div>
<script src="http://code.angularjs.org/1.1.5/angular.min.js"></script>
<script>
var buffer = angular.module('http-auth-interceptor-buffer', []);
buffer.factory('httpBuffer', function($injector) {
var buffer = [];
var $http;
function retryHttpRequest(config, deferred) {
function successCallback(response) {
deferred.resolve(response);
}
function errorCallback(response) {
deferred.reject(response);
}
$http = $http || $injector.get('$http');
$http(config).then(successCallback, errorCallback);
}
return {
append: function(config, deferred) {
buffer.push({
config: config,
deferred: deferred
});
},
retryAll: function(updater) {
for (var i = 0; i < buffer.length; ++i) {
retryHttpRequest(updater(buffer[i].config), buffer[i].deferred);
}
buffer = [];
}
};
});
var app = angular.module('myModule', ['http-auth-interceptor-buffer']);
app.config(function($httpProvider,$routeProvider, $locationProvider) {
$httpProvider.interceptors.push('securityInterceptor');
$routeProvider.
when('/one',{
controller: 'OneCtrl',
/*resolve: {
my: function(Data) {
return Data.getData();
}
},*/
templateUrl: './_one.html'
}).
when('/two', {
controller: 'TwoCtrl',
templateUrl:'./_two.html'
})
.otherwise({
redirectTo: '/one'
});
});
app.controller('OneCtrl',function($scope,Data) {
$scope.my = Data.getData();
$scope.$on('loginRequired',function() {
alert('loginRequired');
});
});
app.controller('TwoCtrl',function($scope) {
});
app.factory('Data', function($http,$q) {
return {
getData : function() {
var deferred = $q.defer();
var promise = $http.get('./security.php').success(function (response) {
deferred.resolve(response);
});
// Return the promise to the controller
return deferred.promise;
}
}
});
app.factory('securityInterceptor', function($q, $rootScope,httpBuffer) {
return {
request: function(config) {
return config || $q.when(config);
},
requestError: function(rejection) {
},
response: function(response) {
return response || $q.when(response);
},
responseError: function(rejection) {
if(rejection.status === 401) {
var deferred = $q.defer();
httpBuffer.append(rejection.config, deferred);
$rootScope.$broadcast('loginRequired');
return deferred.promise;
}
return $q.reject(rejection);
}
};
});
</script>
</body>
</html>
UPDATE
security.php
<?php
header('HTTP/1.1 401 Unauthorized');
$data = 'MyTets';
echo json_encode($data);
What's the trouble ?

I tested your code by putting
$rootScope.$broadcast('loginRequired');
in securityInterceptor
response: function(response) {
$rootScope.$broadcast('loginRequired');
return response || $q.when(response);
},
had problem to simulate the response of the php file. Your code and the events are working well. It should come from your php file which didn't return 401 right?
here is a plunker here

Related

AngularJS Unknown Provider ($injector:unpr) on Keycloak Authentification

I am desperately trying to get my web app to work with keycloak.
I have already secured my HTTP REST Service and now I want the user to get redirected to keycloak once he calls the app and is not yet logged in, and after that he gets returned to the app and can use the API Services.
To achieve this, I used this example:
https://github.com/keycloak/keycloak/blob/master/examples/demo-template/angular-product-app/src/main/webapp/index.html
And because I use angularJS version 1.7.x, I changed the code based on this mail:
http://lists.jboss.org/pipermail/keycloak-user/2015-October/003213.html
Still, I keep getting the error:
jquery.min.js:2 Uncaught Error: [$injector:unpr] Unknown provider:
AuthProvider <- Auth <- authInterceptor <- $http <- $templateRequest <- $route
https://errors.angularjs.org/1.7.2/$injector/unpr?p0=AuthProvider%20%3C-
%20Auth%20%3C-%20authInterceptor%20%3C-%20%24http%20%3C-
%20%24templateRequest%20%3C-%20%24route
at angular.js:138
at angular.js:4891
at Object.getService [as get] (angular.js:5051)
at angular.js:4896
at getService (angular.js:5051)
at injectionArgs (angular.js:5076)
at Object.invoke (angular.js:5100)
at Object.enforcedReturnValue [as $get] (angular.js:4943)
at Object.invoke (angular.js:5108)
at angular.js:4897
Here is my code:
scripts in index.html:
<script src="static/javascript/angular.js"></script>
<script src="static/javascript/angular-resource.js"></script>
<script src="static/javascript/angular-route.js"></script>
<script src="static/javascript/dirPagination.js"></script>
<script src="static/javascript/keycloak.js"></script>
<script src="static/javascript/admin.js"></script>
And my admin.js Angular file:
"use strict";
var module = angular.module('service', ['ngRoute', 'ngResource',
'angularUtils.directives.dirPagination']);
var auth = {};
var logout = function(){
console.log('*** LOGOUT');
auth.loggedIn = false;
auth.authz = null;
window.location = auth.logoutUrl;
};
angular.element(document).ready(["$http", function ($http) {
var keycloakAuth = new Keycloak({
url: 'https://keycloakserver/auth',
realm: 'realm',
clientId: 'app-admin'
});
auth.loggedIn = false;
keycloakAuth.init({ onLoad: 'login-required' }).success(function () {
auth.loggedIn = true;
auth.authz = keycloakAuth;
auth.logoutUrl = keycloakAuth.authServerUrl +
"/realms/realm/tokens/logout?redirect_uri=https://myapp/admin";
module.factory('Auth', function() {
return auth;
});
angular.bootstrap(document, ["service"]);
}).error(function () {
window.location.reload();
});
}]);
module.controller('transactions', function($scope, $http, $filter) {
$scope.transactions = [];
$scope.submit = function() {
var myParams = {
searchId: $scope.search_id
}
$http.get('https://myapp/rest/search',{params:myParams}).
then(function(response) {
$scope.transactions = response.data;
});
};
$scope.sort = function(keyname){
$scope.sortKey = keyname;
$scope.reverse = !$scope.reverse;
};
$scope.showDetails = function(transaction) {
$scope.selectedTransaction = transaction;
};
});
module.factory('authInterceptor', ["$q", "Auth", function($q, Auth) {
return {
'request': function (config) {
var deferred = $q.defer();
if (Auth.authz.token) {
Auth.authz.updateToken(5).success(function() {
config.headers = config.headers || {};
config.headers.Authorization = 'Bearer ' +
Auth.authz.token;
deferred.resolve(config);
}).error(function() {
deferred.reject('Failed to refresh token');
});
}
return deferred.promise;
},
'requestError': function(rejection) {
return $q.reject(rejection);
},
'response': function(response) {
return response;
},
'responseError': function(response) {
if (response.status == 401) {
console.log('session timeout?');
logout();
} else if (response.status == 403) {
alert("Forbidden");
} else if (response.status == 404) {
alert("Not found");
} else if (response.status) {
console.log(response.status);
if (response.data && response.data.errorMessage) {
alert(response.data.errorMessage);
} else {
alert("An unexpected server error has occurred");
}
} else if (response === 'Failed to refresh token') {
logout();
}
return $q.reject(response);
}
};
}]);
module.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('authInterceptor');
}]);
I would be very thankful for every hint! THANK YOU! :-)

How to propagate error in httpProvider responseError interceptor

We have the following code in place for form validation:
$scope.masterModel.$save().then(function (data) {
$scope.masters.data.push(data);
$location.path('/master/edit/' + data.id);
}).error(function (data) {
$scope.errors = data.data;
});
Now we added code to generally catch code 500 server errors on a global level to the app.js
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(function ($injector) {
return {
'responseError': function (rejection) {
// do something on error
if (rejection.status === 500) {
var angularModalService= $injector.get("ModalService");
angularModalService.showModal({
templateUrl: "templates/common/session.html",
controller: "ModalController"
}).then(function (modal) {
modal.element.modal();
modal.close.then(function (result) {
if (result) {
}
});
});
}
}
};
});
}]);
As soon as we add this code, the error callback in the first code does not work anymore.
I think we have to somehow propagate the error in the responseError callback, but how does that work?
AngularJS Version is 1.5.11
You need to "reject the rejection" in the interceptor and return it in order for the error to be propagated:P
var app= angular.module('MyApp', []);
app.controller('Controller', function($scope, $http, $q) {
$http.get("http://www.example.invalid/fake.json")
.then(function(response) {
console.log("success");
}, function(error) {
console.log("controller error handler");
});
});
app.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(function ($injector, $q) {
return {
'responseError': function (rejection) {
console.log("interceptor error handler");
// do something on error
if (rejection.status === 500) {
// do something...
}
return $q.reject(rejection);
}
};
});
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="MyApp">
<div ng-controller="Controller">
</div>
</div>
As you can see, I added the line
return $q.reject(rejection);
at the end of your interceptor. You can check the console and see that now both messages are logged.

$scope.value does not update with a view

I have been trying all and nothing. I wanted create simple circle slider from values with my service, but $scope.value didn't update my view. Take a look at my demo:
https://jsfiddle.net/gh42tqxs/11/
<div ng-app="mainApp">
<div ng-controller="FooController">
<ui-knob value="value" options="options" knob-format data-note="{{note}}"></ui-knob>
</div>
</div>
var app = angular.module("mainApp",['ui.knob']);
app.service('Service', function ($http, $q) {
this.Get = function (premiereId) {
var deferred = $q.defer();
var req = {
method: 'GET',
url: 'https://api.myjson.com/bins/63xhr',
headers: {
'Content-Type': 'application/json'
}
}
$http(req).then(function (response) {
if (response.status == 200) {
deferred.resolve(response.data);
}
else {
deferred.reject(response.data);
}
}, function (error) {
deferred.reject(error.data);
});
return deferred.promise;
}
})
app.controller("FooController", function (Service, $timeout, $scope) {
function test() {
Service.Get().then(function (response) {
$timeout(function () {
console.log(response);
$scope.value = response.Number;
$scope.options.min = response.NumberMin;
$scope.options.max = response.NumberMax;
}, 1000);
})
}
test();
});
Animation isn't starting, we start with the minimum value, not from $scope.value.
ng knob == jquery knob slider
How fix it? Please help me.

Preparing http request url in interceptors in AngularJS

I tried to prepare http request url in interceptors rather than adding it to the object passed in $http. Here is the sample of code I tried:
angular.module('myapp', [])
.service('myservice', function() {
this.myfunction = function() {
var req = {method: 'POST', reqName: 'getInfo'};
return $http(req);
}
})
.factory('myInterceptor', function() {
var interceptor = {
'request': function(config) {
config.url = "http://www.myapi.com/demo/"+config.reqName;
return config;
}
}
return interceptor;
})
.config(function($httpProvider) {
$httpProvider.interceptors.push('myInterceptor');
})
But I'm getting an error, which says:
Error: [$http:badreq] Http request configuration url must be a string.
Received: undefined
Any help?
Let me show some tests that proof AngularJS documentation is correct.
Some details about Interceptor - http://docs.angularjs.org/api/ng/service/$http#interceptors
angular.module('myApp', [])
.service('service', function($http) {
this.myfunction = function() {
var req = {
method: 'POST',
reqName: 'getInfo'
};
return $http(req);
}
})
.factory('myInterceptor', function() {
var interceptor = {
'request': function(config) {
config.url = "http://www.myapi.com/demo/" + config.reqName;
return config;
}
}
return interceptor;
})
.config(function($httpProvider) {
$httpProvider.interceptors.push('myInterceptor');
})
describe("myApp", function() {
beforeEach(module("myApp"));
it("executes intercpetor that changes request", inject(function(service, $httpBackend) {
$httpBackend.expectPOST("http://www.myapi.com/demo/getInfo").respond(201, []);
service.myfunction().then(function(r) {
expect(r.config.url).toBe('http://www.myapi.com/demo/getInfo')
})
$httpBackend.flush();
}));
});
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-resource.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>

Angular $q multiple promises

I am trying to do this:
Run fun1
Then after fun1 returns (promise resolves), run fun2 and fun3 in parallel (async)
Then after fun2, 3 return (promises resolve), run some fun4 with different input all in parallel
Then eventually run fun5 after all fun4 are resolved.
$q.all({
fun1: fun1()
}).then(function(){
return $q.all({
fun2: fun2(),
fun3: fun3(),
});
}).then(function(){
var promises = [];
for(var i = 0; i < 3; i += 1){
promises.push(fun4(i));
}
return $q.all(promises);
}).then(function(){
fun5();
});
fun 1~5 are all api calls similar to this pattern:
var funX = function(){
return $http({
method: 'GET',
url: '/someURL'
}).success(function(data, status, headers, config) {
return;
}).error(function(data, status, headers, config) {
return new Error();
});
};
I want to make sure they are running in the order I describe above. Looks like fun4 did not wait for fun2 and fun3 to return, and fun5 did not wait for all fun4 to return too.
So I was able to resolve this issue by using the new api and get rid of deprecated error and success.
As I said earlier, what's probably happening is that some of your Promises are failing. The best way to guard against the $http Promises failing would be to deal with the error in the .catch() (don't use .success() and .error() as they're deprecated) and return a truthy value.
Following is a test case trying to simulate your process failing (open up the console and see what gets logged):
angular.module('Test', [])
.controller('Ctrl', function($q, Factory) {
$q.all({
fun1: Factory.succeedFunc('1')
})
.then(function() {
return $q.all({
fun2: Factory.failingFunc('2'),
fun3: Factory.succeedFunc('3')
});
})
.then(function() {
var promises = [];
for (var i = 0; i < 3; i++) {
promises.push(Math.random() > 0.5 ? Factory.failingFunc('4: ' + i) : Factory.succeedFunc('4: ' + i));
}
return $q.all(promises);
})
.then(function() {
Factory.succeedFunc('5');
});
})
.factory('Factory', function($q, $timeout) {
return {
failingFunc: function(msg) {
var deferred = $q.defer();
console.log('executing', msg);
$timeout(function() {
console.log('failed', msg);
deferred.reject();
}, 500);
return deferred.promise;
},
succeedFunc: function(msg) {
var deferred = $q.defer();
console.log('executing', msg);
$timeout(function() {
console.log('succeeded', msg);
deferred.resolve();
}, 1000);
return deferred.promise;
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="Test" ng-controller="Ctrl">
</div>
And here is what happens when you deal with the rejection and return a value in the Promises' .catch() (though be careful not to throw in the .catch() as that will reject the newly created Promise too:
angular.module('Test', [])
.controller('Ctrl', function($q, IgnoreErrors) {
var Factory = IgnoreErrors;
$q.all({
fun1: Factory.succeedFunc('1')
})
.then(function() {
return $q.all({
fun2: Factory.failingFunc('2'),
fun3: Factory.succeedFunc('3')
});
})
.then(function() {
var promises = [];
for (var i = 0; i < 3; i++) {
promises.push(Math.random() > 0.5 ? Factory.failingFunc('4: ' + i) : Factory.succeedFunc('4: ' + i));
}
return $q.all(promises);
})
.then(function() {
Factory.succeedFunc('5');
});
})
.factory('Factory', function($q, $timeout) {
return {
failingFunc: function(msg) {
var deferred = $q.defer();
console.log('executing', msg);
$timeout(function() {
console.log('failed', msg);
deferred.reject();
}, 500);
return deferred.promise;
},
succeedFunc: function(msg) {
var deferred = $q.defer();
console.log('executing', msg);
$timeout(function() {
console.log('succeeded', msg);
deferred.resolve();
}, 1000);
return deferred.promise;
}
};
})
.factory('IgnoreErrors', function(Factory) {
return {
failingFunc: function(msg) {
return Factory.failingFunc(msg).catch(function() {
return true;
});
},
succeedFunc: function(msg) {
return Factory.succeedFunc(msg).catch(function() {
return true;
});
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="Test" ng-controller="Ctrl">
</div>

Resources