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! :-)
Related
I have capturing the application response error ,while capturing the error, i am getting the error.
In Interceptor ,according to the response code , have assign the rootscope broadcast and show the alert message in controller.
Here $rootScope.$broadcast('loginRequired'); is assigning in interceptor and while capturing in service response inside controller.
$rootScope.$on("loginRequired", function(e) {
alert("hello");
alertsManager.addAlert('Yay!', 'alert-success');
});
interceptor.
var interceptor = function($q, alerts, $rootScope, $timeout, $location) {
return {
request: function(config) {
console.log(config);
return config;
},
response: function(response) {
var deferred = $q.defer();
$rootScope.$broadcast('loginRequired');
return response || $q.when(response);
},
responseError: function(rejection) {
if (rejection.status == 500) {
$location.url('/ho');
var deferred = $q.defer();
$rootScope.$broadcast('loginRequired');
return $q.reject(rejection);
}
console.log(rejection.status);
return $q.reject(rejection);
}
}
};
$httpProvider.interceptors.push(interceptor);
alertManagerfactory
var alertsManager = function() {
return {
alerts: {},
addAlert: function(message, type) {
this.alerts[type] = this.alerts[type] || [];
this.alerts[type].push(message);
},
clearAlerts: function() {
for (var x in this.alerts) {
delete this.alerts[x];
}
}
};
};
alertsManager.$inject = [];
In controller :
var LoginController = function($scope, $rootScope, alerts, alertsManager) {
$scope.alerts = alertsManager.alerts;
// getting error in this line
//getting typeError: Cannot read property 'alerts' of undefined
LoginService.AfterLogin(username, password)
.then(function(response) {}, function(status) {
console.log("Error" + status);
if (status === 500) {
$rootScope.$on("loginRequired", function(e) {
alert("hello");
alertsManager.addAlert('Yay!', 'alert-success');
});
}
});
};
LoginController.$inject = ['$scope', '$rootScope', 'alerts', 'alertsManager'];
In controller view.
<div ng-repeat="alert in alerts" ng-class="'alert-' + (alert.type || 'warning')" close="closeAlert($index)">{{alert.msg}}</div>
"this" keyword in your addAlert "method" is actually referencing anonymous function you have assigned to addAlert prop.
There are couple of ways to deal with this. For example creating variable that holds your object.
var alertsManager = function() {
var $this = {
alerts: {},
addAlert: function(message, type) {
$this.alerts[type] = $this.alerts[type] || [];
$this.alerts[type].push(message);
},
clearAlerts: function() {
for (var x in $this.alerts) {
delete $this.alerts[x];
}
}
};
return $this;
};
alertsManager.$inject = [];
I'm building my (first) angular app that will have tokens inserted into headers (the content shown is for the most part taken from here)
angular.module('myApp')
.factory('sessionInjector', ['SessionService', function(SessionService) {
var sessionInjector = {
request: function(config) {
config.headers['x-session-token'] = SessionService.getToken();
return config;
}
};
return sessionInjector;
}])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
The trouble I'm having is with SessionService - how can I initialize this with call to the server?
For example, this didn't work:
.factory('SessionService', ['$injector', function($injector){
var token = "";
return {
getToken: function () {
var http = $injector.get('$http');
if (token === "") {
http.get('http://localhost/api/auth/getToken').success(function (ret) {
token = ret;
});
}
return token;
//I can see a $q/deferred/promise should be used somehow here...
//but I'm not sure it solves the problem I'm having...
}
}
}]);
because it just overloads my cpu to 100%...
Since it's my first angular app, I'm sure I'm missing something, but... what?
EDIT:
Another take on the matter... still doesn't work though... (again, uses up cpu, probably infinite loop)
.factory('sessionData', function () {
var currentToken = '[uninitialized-token]';
return {
getToken: function () {
return currentToken;
},
setToken: function (token) {
currentToken = token;
}
}
})
.factory('sessionInjector', ['sessionData', '$injector', '$q', function (sessionData, $injector, $q) {
var sessionInjector = {
request: function (config) {
var deferred = $q.defer();
var http = $injector.get('$http');
http.get('http://localhost/api/auth/getToken').success(function (ret) {
sessionData.setToken(ret);
console.log("successfully authenticated with token " + sessionData.getToken());
config.headers['x-header-sessionID'] = sessionData.getToken();
deferred.resolve(config);
})
.error(function(){
console.log("failed to authenticate");
deferred.resolve(config);
});
return deferred.promise;
}
};
return sessionInjector;
}])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
.run(['$http', 'sessionData', function ($http, configs, sessionData) {
$http.get('http://localhost/api/auth/testMethod').then(function (ret) {
//do something...
});
}])
Check whether this modified code fragment will solve your issues.
.factory('SessionService', ['$http', '$q', function($http, $q) {
var token = null;
var sessionService = {};
var differred = $q.defer();
sessionService.readToken = function() {
return $http.get('http://localhost/api/auth/getToken')
.success(function (res) {
console.log('Auth Success and token received: ' + JSON.stringify(res.data));
// Extract the token details from the received JSON object
token = res.data;
differred.resolve(res);
}, function (res) {
console.log('Error occurred : ' + JSON.stringify(res));
differred.reject(res);
}
)
};
sessionService.getToken = function() {
return token;
};
sessionService.isAnonymous = function() {
if (token)
return true;
else
return false;
};
return sessionService;
}])
.factory('sessionInjector', ['SessionService', function(SessionService) {
var sessionInjector = {
request: function(config) {
if (!sessionService.isAnonymous) {
config.headers['x-session-token'] = SessionService.getToken();
return config;
}
}
};
return sessionInjector;
}])
.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
Answer was actually pretty straight forward - if the targeted URL is for login, then don't inject anything (look for the comment the fix):
.factory('sessionData', function () {
var currentToken = '[uninitialized-token]';
return {
getToken: function () {
return currentToken;
},
setToken: function (token) {
currentToken = token;
}
}
})
.factory('sessionInjector', ['sessionData', '$injector', '$q', function (sessionData, $injector, $q) {
var sessionInjector = {
request: function (config) {
//The fix:
if(config.url === 'http://localhost/api/auth/getToken')
return config;
var deferred = $q.defer();
var http = $injector.get('$http');
http.get('http://localhost/api/auth/getToken').success(function (ret) {
sessionData.setToken(ret);
console.log("successfully authenticated with token " + sessionData.getToken());
config.headers['x-header-sessionID'] = sessionData.getToken();
deferred.resolve(config);
})
.error(function(){
console.log("failed to authenticate");
deferred.resolve(config);
});
return deferred.promise;
}
};
return sessionInjector;
}])
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push('sessionInjector');
}])
.run(['$http', 'sessionData', function ($http, configs, sessionData) {
$http.get('http://localhost/api/auth/testMethod').then(function (ret) {
//do something...
});
}])
I am trying to follow this tutorial
http://www.codeproject.com/Articles/784106/AngularJS-Token-Authentication-using-ASP-NET-Web-A
I don't know which angularjs package to download so that I could use localStorageService and ngAuthSettings in my angularjs code.
I am getting the following err when I run the mvc 5 asp.net vs2013 web api app.
Unknown provider: localStorageServiceProvider <- localStorageService <- authInterceptorService <- $http <- $templateRequest <- $compile
Here is my code.
var appointmentReminderApp = angular.module('appointmentReminderApp', ["ngRoute", "ui.bootstrap"]);
appointmentReminderApp.config(function ($routeProvider, $locationProvider,$httpProvider) {
$httpProvider.interceptors.push('authInterceptorService');
$locationProvider.html5Mode(true);
$routeProvider
.when("/home", {
templateUrl: "App/Home.html",
controller: "HomeController"
})
.when("/Register", {
templateUrl: "App/AuthForm/templates/register.html",
controller: "authRegisterController"
})
.when("/Login", {
templateUrl: "App/AuthForm/templates/login.html",
controller: "authLoginController"
})
.otherwise({ redirectTo: "/home" });
});
appointmentReminderApp.factory('authInterceptorService', ['$q', '$injector', '$location', 'localStorageService', function ($q, $injector, $location, localStorageService) {
var authInterceptorServiceFactory = {};
var _request = function (config) {
config.headers = config.headers || {};
var authData = localStorageService.get('authorizationData');
if (authData) {
config.headers.Authorization = 'Bearer ' + authData.token;
}
return config;
}
var _responseError = function (rejection) {
if (rejection.status === 401) {
var authService = $injector.get('authService');
var authData = localStorageService.get('authorizationData');
if (authData) {
if (authData.useRefreshTokens) {
$location.path('/refresh');
return $q.reject(rejection);
}
}
authService.logOut();
$location.path('/login');
}
return $q.reject(rejection);
}
authInterceptorServiceFactory.request = _request;
authInterceptorServiceFactory.responseError = _responseError;
return authInterceptorServiceFactory;
}]);
appointmentReminderApp.factory('authService', ['$http', '$q', 'localStorageService', 'ngAuthSettings', function ($http, $q, localStorageService, ngAuthSettings) {
var registerUser = function (auth) {
return $http.post("/api/Account/Register", auth);
};
var loginUser = function (loginData) {
var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.Password;
if (loginData.useRefreshTokens) {
data = data + "&client_id=" + ngAuthSettings.clientId;
}
var deferred = $q.defer();
$http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {
if (loginData.useRefreshTokens) {
localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName, refreshToken: response.refresh_token, useRefreshTokens: true });
}
else {
localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName, refreshToken: "", useRefreshTokens: false });
}
_authentication.isAuth = true;
_authentication.userName = loginData.userName;
_authentication.useRefreshTokens = loginData.useRefreshTokens;
deferred.resolve(response);
}).error(function (err, status) {
_logOut();
deferred.reject(err);
});
return deferred.promise;
};
return {
registerUser: registerUser,
loginUser: loginUser
};
}
]);
Have you downloaded the angular local storage service module? do you have this line
<script src="scripts/angular-local-storage.min.js"></script>
in your index.html?
The required JS file doen't come bundled with Angular.
You can get it from here.
I was unable to find the CDN, will update if I find one.
I am trying to call AuthenticationService.logout() on a 401 http error. However, I can't make it work. I suppose I can't inject a service to the config, but how can I achieve this then?
myApp.config(['$httpProvider', 'AuthenticationService', function ($httpProvider, AuthenticationService) {
var interceptor = ['$rootScope','$q', function(scope, $q) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
var deferred = $q.defer();
var req = {
config: response.config,
deferred: deferred
}
/* LOGOUT HERE */
AuthenticationService.logout();
return deferred.promise;
}
return $q.reject(response);
}
return function(promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
}]);
What am I doing wrong, and how can I fix it?
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