I'm trying to protect my application's views based on roles. In laravel the solution is simply adding a middleware as part of the route parameters. Also would like to accomplish the same functionality on the client side using an Angular SPA app. Sorry I think these are two questions in one, anyways.
https://laravel.com/docs/5.1/authentication#protecting-routes
Any simple solution to accomplish this?
In Angular you can implement the same logic in an Interceptor something like this..
app.factory('ApiInterceptor', function() {
return {
'request': function(config) {
//check config.url against users roles somehow
disallow = doesTheUserHaveTheCorrectRoles(config.url);
//disallow the request to the API
if(disallow){
alert('No!');
return false;
}else{
return config;
}
}
}
}
Check the documentation on HTTP Interceptors https://docs.angularjs.org/api/ng/service/$http
Or you could do the same thing with a run block and do something when the location changes;
app.run(function ($rootScope) {
$rootScope.$on("$locationChangeStart", function (event, next, current){
disallow = doesTheUserHaveTheCorrectRoles(next);
//disallow access to the next location
if(disallow){
alert('No!');
return false;
}else{
return config;
}
});
});
https://docs.angularjs.org/guide/module
Related
i have developed single page application in angularjs. i have implemented the refresh token mechanism. refresh token suppose to refresh every 30 minutes. I am trying to handle refresh token in responseError of interceptor. I m trying to hold request if it returns 401 unauthorised error. Is there any mechanism to hold all the request once it return 401 error then refresh token and resume all request with new token.
Is it right way to handle the refresh token, here is sample code
$provide.factory('httpTokenInterceptor', function ($q, $injector, $cookies) {
return {
// On request sending
request: function (config) {
config.headers = config.headers || {};
// get this data from $cookies
var globals = $cookies.getObject('globals') || {};
//console.log(globals);
if (globals.authData)
config.headers.Authorization = 'Bearer ' + globals.authData.access_token;
return config;
},
// On response failure
responseError: function (rejection) {
console.log('AuthTokenHttpInterceptor responseError');
console.log(rejection);
if (rejection.status === 401) {
//hold current and all pending request
var aService = $injector.get('authenticationService');
aService.getRefreshToken().then(function(response) {
//need to resume all the request here
deferred.resolve(response);
});
return deferred.promise;
}
return $q.reject(rejection);
}
};
});
In short, you don't want to hold up any of your HTTP calls like that.
Your solution will go and refresh your token after one of your HTTP calls already failed. Also, just to be clear, your code is adding Authorization header even on HTTP calls that are getting resources like HTML templates. If you don't want to do this, then you should restrict that as well.
For one solution, check out this link. It doesn't use any particular library for handling JWT tokens, but you will have to create a wrapper around this implementation to use it wherever you need to do a HTTP call.
My suggestion (and personal preference when handling JWT tokens) is using the angular-jwt library. It's really easy to set up and you can check it out here.
There more complex libraries like auth0, which can do a lot of other stuff, and can be used in conjuction with angular-jwt library. Check out this link to see how to handle token refreshing both prior to a HTTP call and on page refresh.
Hope this helps.
You can hold requests and resume them using AngularJS Interceptors.
authInterceptor.$inject = ['$q', '$rootScope'];
function authInterceptor($q, $rootScope) {
return {
request: function(config) {
var deferred = $q.defer();
$rootScope.$watch('continue', function(value) {
if(value === true)
deferred.resolve(config);
});
return deferred.promise;
}
};
}
In the above example all of the requests hold until $rootScope.continue becomes true. Otherwise they will wait forever.
A similar-ish question here (but not applicable).
In my SPA I'm using PassportJS to handle authentication. I have many routes which require the user to be logged in. I route like this:
// ... other routing stuff
when('/insights', {
templateUrl: 'partials/insights',
controller: 'insightsCtrl',
resolve: {
loggedin: checkLoggedin
}
})
var checkLoggedin = function($q, $timeout, $http, $location, $rootScope){
var deferred = $q.defer();
$http.get('/loggedin').success(function(user){
if (user !== '0') {
$rootScope.user = user;
deferred.resolve();
}
else {
deferred.reject();
$location.url('/login');
}
});
return deferred.promise;
};
My route to check if logged in:
app.get('/loggedin', function(req, res) {
res.send(req.isAuthenticated() ? req.user : '0');
});
If they're not logged in, they're redirected to /login. Once that is successfully done though, I'd like to go back to the original page they tried to request.
Is there a built-in way to store the route the user is trying to access so that I can redirect to it once they're logged in? I can do this with a cookie I suppose, but it feels a little clunky to do so...
There is no built in approach besides using the custom callback that PassportJS provides, but if you are writing this for a SPA, you could leverage $cacheFactory and create a service/factory that stores the last visit page of that user.
Limitations for $cacheFactory are the same as local storage which are that they are domain specific, so if you change domains, your local storage will not be the same cross-domain.
You can use a custom callback as explained in passportjs documentation.
With custom callback, you can send the current query string to your server, and get a redirect to same query string after successful login.
Another possibility is to use localStorage to save current state and take it after login.
You have many modules to use localStorage with angular like angular-local-storage.
Can I define global variables or functions that can be accessed by all of my Angular service modules?
For example, I have several services that make http requests to various endpoints of the same url root. I would like to be able to change this root.
My service modules look like this.
var host = 'http://my-api.com';
return {
get: function(id) {
return $http({
url: host + '/contacts/' + id,
method: 'GET'
});
},
...
switchHost: function(url){
host = url;
}
});
So I can call ContactService.switchHost('http://new-url') in my Controllers to update this particular Service.
I would like to have some sort of Root Service where I coul define host and switchHost globally.
Note: The use case here is that some of our clients will be accessing our company API, and others will be self-hosting their resources.
i suggest you to create an interceptor which will digest an angular value like this.
angular.module('...')
.value('HOST', 'http://www.fu.bar.com/')
.factory('InterceptorFactory', function (HOST) {
return {
request: function(config) {
config.url = HOST + config.url;
return config;
}
};
})
.config(['$httpProvider',function($httpProvider) {
$httpProvider.interceptors.push('InterceptorFactory');
}]);
Whenever you call the $http service, the factory will be called and will add your host.
If you want to update the host, you just need to inject in any block the value and update it.
Thanks for the responses. I just did something like this.
angular.module('app.services').factory('RootService', function($http) {
return {
switchHost: function(url){
this.host = url;
},
host: 'http://app.apiary-mock.com'
}
});
This way the client can just type in a url on the settings page and the root will change, which is what I want.
Some of my AngularJS routes are to pages which require the user to be authenticated with my API. In those cases, I'd like the user to be redirected to the login page so they can authenticate. For example, if a guest accesses /account/settings, they should be redirected to the login form.
From brainstorming I came up with listening for the $locationChangeStart event and if it's a location which requires authentication then redirect the user to the login form. I can do that simple enough in my applications run() event:
.run(['$rootScope', function($rootScope) {
$rootScope.$on('$locationChangeStart', function(event) {
// Decide if this location required an authenticated user and redirect appropriately
});
}]);
The next step is keeping a list of all my applications routes that require authentication, so I tried adding them as parameters to my $routeProvider:
$routeProvider.when('/account/settings', {templateUrl: '/partials/account/settings.html', controller: 'AccountSettingCtrl', requiresAuthentication: true});
But I don't see any way to get the requiresAuthentication key from within the $locationChangeStart event.
Am I overthinking this? I tried to find a way for Angular to do this natively but couldn't find anything.
What I did is implement an angularjs interceptor which handles http request errors. Basically, what I do is when I get the 401 (unauthorized) from my backend, I save the current url and redirect the user to a login page. When the user does login successfully, I retrieve the saved url to set the path with $location.
app.config(function ($routeProvider, $locationProvider, $httpProvider) {
/* Global interceptor for 401 - not authorized */
var interceptor = ['$location', '$q', 'authorizationService', function ($location, $q, authorizationService) {
function success(response) {
return response;
}
function error(response) {
if (response.status === 401) {
authorizationService.saveUrl($location.path());
$location.path('/logon');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
return function (promise) {
return promise.then(success, error);
};
} ];
});
In the logon controller (on successful logon) I set the location like this:
$location.path(authorizationService.getUrl());
I use angular-http-auth in my project. If you have a backend is easier to delegate on it to handle the authentication, showing a login form automatically in each 401 response, than duplicate the authentication mapping both on client and server.
I want to implement authentication and authorization in the Flatiron stack (using Flatiron, Resourceful and Restful). I want to require that a user has the necessary permissions, when trying to change a resource. In the Restful Readme file, there's a note about authorization:
There are several ways to provide security and authorization for
accessing resource methods exposed with restful. The recommended
pattern for authorization is to use resourceful's ability for before
and after hooks. In these hooks, you can add additional business logic
to restrict access to the resource's methods.
It is not recommended to place authorization logic in the routing
layer, as in an ideal world the router will be a reflected interface
of the resource. In theory, the security of the router itself should
be somewhat irrelevant since the resource could have multiple
reflected interfaces that all required the same business logic.
TL;DR; For security and authorization, you should use resourceful's
before and after hooks.
So authorization can be handled by Resourceful's hooking system.
My actual problem is the authentication process at the beginning of every HTTP request.
Let's say I have a resource Post, a User and a resource Session. The REST API is being defined by using Restful. My main concern for this question is to ensure that a user has a session when creating a post. Other methods like save, update or for other resources like creating a user should work analogous.
File app.js:
var flatiron = require('flatiron');
var app = flatiron.app;
app.resources = require('./resources.js');
app.use(flatiron.plugins.http);
app.use(restful);
app.start(8080, function(){
console.log('http server started on port 8080');
});
File resources.js:
var resourceful = require('resourceful');
var resources = exports;
resources.User = resourceful.define('user', function() {
this.restful = true;
this.string('name');
this.string('password');
});
resources.Session = resourceful.define('session', function() {
// note: this is not restful
this.use('memory');
this.string('session_id');
});
resources.Post = resourceful.define('post', function() {
this.restful = true;
this.use('memory');
this.string('title');
this.string('content');
});
resources.Post.before('create', function authorization(post, callback) {
// What should happen here?
// How do I ensure, a user has a session id?
callback();
});
There's also a runnable version of the code (thanks #generalhenry).
So assume a user trying to create a post, already has been given a session id, that is sent with every request he makes by a cookie header. How can I access that cookie in the before hook (i.e. the authorization callback)?
The example can be started with node app.js and HTTP requests can be made using curl.
Keep in mind that these guidelines are for authorization process. If you need to use sessionId you can access it either way: req.sessionID, req.cookies["connect.sid"].
By checking requests this way you will be sure every users have valid session id.
app.use(flatiron.plugins.http, {
before: [
connect.favicon(),
connect.cookieParser('catpsy speeds'),
function(req, res) {
if (req.originalUrl === undefined) {
req.originalUrl = req.url;
}
res.emit('next');
},
connect.session({secret : 'secter'}),
function(req, res) {
console.log('Authenticating...');
console.dir(req.session);
//any other validation logic
if (req.url !== '/login' && typeof req.session.user == 'undefined') {
res.redirect('/login');
} else {
res.emit('next');
}
}
]
});
Here is project example using this approach.