How to check if user is authenticated in AngularJS router? - javascript

I have an application set up with the Mean.js yeoman generator. It uses PassportJS to setup local-authentication. This all works great and I understand how to check if a user is logged in from the ExpressJS routes file.
The problem is that most of my page routing is done in the angular routes. I know how to check authentication in the controller with the the following code.
// Projects controller
angular.module('projects').controller('ProjectsController', ['$scope', '$stateParams', '$location', 'Authentication', 'Projects',
function($scope, $stateParams, $location, Authentication, Projects) {
$scope.authentication = Authentication;
But how do I check authentication in the routes. For example in this routes files how would I only allow authenticated users to access the tools html file and redirect users that arent logged in back to the home page:
'use strict';
//Setting up route
angular.module('analysis').config(['$stateProvider',
function($stateProvider) {
// Projects state routing
$stateProvider.
state('imageAnalysis', {
url: '/analysis',
templateUrl: 'modules/analysis/views/tools.client.view.html'
});
}
]);
I know there are similar posts out there but I had trouble understanding many of them. Thanks, I really appreciate any help. I am new to the stackoverflow community and still learning community standards.

At a high level, there are two approaches:
Use your view routing layer (e.g. UI Router) to catch “unauthenticated” state
Use HTTP interceptors to look for requests that have a 401 Unauthorized status, indicating that the user must login (or that their current session has expired)
In practice you’ll probably use a combination of both.
Speaking to the UI Router, there a two of doing this: resolves or events.
Resolves: The UI Router provides a nice feature called resolves, which allows you to provide a promise that must be resolved before the view is rendered. You could create a promise which resolves your user state.
Events: The UI Router provides a $stateChangeStart event, you can observe this event and prevent it if the user is not logged in. You would then send the user to login page. This is a bit more stateful (you have to remember where the user wanted to go in the first place, so that you can redirect after login).
I chose the event approach for my work on the Stormpath Angular SDK because it gives me the flexibility to define authorization on top of authentication.

You may be looking for HTTP Interceptors. Check auth on the requests.
From OneHungryMind:
HTTP interceptors are a great way to define behavior in a single place
for how a request or response is handled for ALL calls using the $http
service. This is a game changer when you want to set an auth token on
all outgoing calls or respond to a particular HTTP status error at the
system level. You can pair interceptors with the incredibly useful
Angular Storage module to cache an authenticated user and retrieve it
the next run time.
There are some good tutorials(1) out there(2)!

Related

How to hide angular admin views

I want to make an angular app with routes and jwt auth, but I don't want normal users to see the HTML partials of admin views. What's the best way to do this with laravel and angular? People can just put "/partials/adminPage1.html" on the url and see the partial when they are not logged in. My API is secure but I don't want the html to be public.
I want it so this is public:
index.php, publicPartial1.html, publicPartial2.html, etc
and only logged in users can use these files:
admin.php, adminPartial1.html, adminPartial2.html
You can/need to approach this in a few ways:
when "someone" puts "/partials/adminPage1.html" you need to check in the sever side (by the service you are checking it's permissions/role) then display/redirect to the appropriate route with ReturnUrl in the query for after login redirect.
You can be more secured by downloading the routes from the server by requesting them first (per user/role/permission) from a dedicated service, but then you'll need to bootstrap your AngularJS, since routing needs to be loaded with AngularJS life cycle, so in that case you are getting the routes, building them in a provider while bootstrapping AngularJS after getting the routes from the designated service as I mentioned.
* I would suggest to simply implement option (1) which is straight forward and most commonly used. *

AngularJs and Angular-UI-Router routes permissions

I'm facing an issue on how to implement route restrictions based on remote data gotten from the server.
Suppose I have the following config file:
angular.module('myApp')
.config(['$stateProvider', function($stateProvider) {
$stateProvider
.state('post', {
url: '/post/:post_id',
abstract: true,
[...]
})
.state('post.view', {
url: '/view'
[...]
})
.state('post.edit', {
url: '/edit'
[...]
})
}]);
The requirements for my application are:
A post has an owner (creator of the post) and its domain may be public or private.
If the domain is public, every user will be able to see the post (entering state post.view), and, if not (domain is private), only the owner will be able to see it.
The post.edit state is only accessible to the owner.
To do this, what is the best approach?
I was thinking of having a resolve promise that fetches the data from the server (post's domain and correspondent user role), performs the required checks and returns accordingly (promise resolved or rejected).
But then, how would I redirect the user to a correct state if he is not authorized? For example, a common user trying to access the post.edit state should be redirected to the post.view state, if the post's domain is public... But if the post's domain is private, an unauthorized access page should be presented. Is it a good approach to do this directly on the resolve? What are the alternatives?
Have a look at this great tutorial on routing authorization based on roles:
role based authorization
Also have a look at this stackoverflow answer, here there is conditional routing based on login, you can use the same logic of roles for your purpose,
Ui-router, routes based on permission

Adal.js not triggering the authentication

I am trying to integrate adal.js in my application. Below is my Code. Could someon please let me know why the authentication is not triggered.
var app = angular.module('TestWebApp', [
'ngRoute',
'testControllers',
'testServices',
'datatables',
'AdalAngular'
]);
/**
* Configure the Routes
*/
app.config(['$routeProvider', '$httpProvider', 'adalAuthenticationServiceProvider', function ($routeProvider, $httpProvider, adalAuthenticationServiceProvider) {
$routeProvider
// Home
.when("/dashboard", {templateUrl: "partials/package.html", controller: "searchCtrl",requireADLogin: true})
// else 404
.otherwise("/404", {templateUrl: "partials/404.html", controller: "searchCtrl"});
adalAuthenticationServiceProvider.init(
{
tenant: 'test.onmicrosoft.com',
clientId: '23234sf-sdf-------'
},
$httpProvider
);
}]);
And my page url is something link this.
http://localhost:8081/test-ui/#/dashboard
This should go to the Azure login page but its not going.
I think that your tenantID is probably right - it needs to be whatever your AD portal had for the app id URI(minus the name of the application) inside your azure portal. I would also look at the manifest - This is a pretty big deal to have changed. The steps are on the page Omar linked. The SinglePageApp example is a great resource in starting out, and the example is clean, if maybe a bit trivial for the adal portion. You should also check outvittorio's excellent deep dive for a good explanation and great review of adal.js
For me the problem was that I had a login button with a form that was redirecting because of action="#" and so the Azure redirecting was not working.
I just removed the whole action-attribute and it worked
Your issue: I think you have an issue with the link. Your link should be like this:http://localhost:8081/test-ui#/dashboard
Home
ToDo List
Contact List
Adal.Js uses requireADLogin keyword to interrupt the route change event. If you don't see any redirection, it could be related to the routechange event not firing. I suggest to add different routes to your app to verify angular routes first.
General guideline: You can try the sample app first to see if your configurations is working.
Sample app:https://github.com/AzureADSamples/SinglePageApp-DotNet
If you have an issue with config, you can follow the steps in the ReadMe file to setup your app config.
If sample app works for your config and your app is having issues, you can see the calls with Fiddler and further debug into adal.js as well. Login redirect event also broadcasts adal:loginRedirect.

Online/Offline App in Angular

I am developing an application in Angular that makes many calls to a couple of webservices. I want to develop an offline component to the app such that it will cache certain webservice results to LocalStorage and use them when the connection is offline.
That part is fairly simple, and the part I am having the most trouble with is how to branch logically when the app is offline.
Here is my current flow:
User loads the page
Webservice calls happen as usual
$http interceptor looks for a 404 error and marks a $rootScope.isOnline boolean flag to indicate we are offline, otherwise if no 404 then we mark as online
I want my code to branch depending on this flag, in a way that is maintainable. As such, I was looking at using dependency injection to inject either an 'online' service which makes calls to the webservice, or an 'offline' service, which interfaces with the LocalStorage results if they exist.
Can I base my dependency injection based off the online/offline flag to inject the correct service like so?
.factory('AuthService', ['$rootScope', '$injector', function($rootScope, $injector) {
if($rootScope.isOnline) {
return $injector.get('OnlineAuthService');
}
else {
return $injector.get('OfflineAuthService');
}
}])
.service('OnlineAuthService', ['$rootScope', '$http', '$location', 'serviceEndpoint', 'securityEndpoint', 'organisationId', function ($rootScope, $http, $location, serviceEndpoint, securityEndpoint, organisationId) {
this.ensureSession = function (data) {
// Do some connection to the webservice
};
}])
.service('OfflineAuthService', ['$rootScope', function ($rootScope) {
this.ensureSession = function (data) {
// Do some LocalStorage stuff
};
}])
AuthService.ensureSession(data);
The issue I am having is that $rootScope.isOnline is not marked as offline before the first call to my webservice, so even when the connection is offline, the dependency injection looks at $rootScope.isOnline and injects the OnlineAuthService.
Is this the correct way of developing an Online/Offline app in Angular or is there a better way?
There are many ways to do this. Take a look at this simplified plunker:
http://plnkr.co/edit/CpiJaF480Mai050b63RC?p=preview
The idea there is that you have the singleton service object returned by the service, but then you copy over different function methods as online/offline changes.
There are many other ways to do this, such as adding levels of indirection (creating a service from which you can request the current active service for example).
You can not simply do it with angular injection, because both factory and service calls will get called once and only once and will simply register a single instance for the service name (they will not get called every time you inject).

AngularJS and Handling 404 Errors

What is the best way to serve up proper 404's with an AngularJS app?
A little background: I'm building an Angular app and have opted to use
$locationProvider.html5Mode(true);
because I want the URLs to appear natural (and indistinguishable from a multi-page "traditional" web app).
On the server side (a simple Python Flask app), I have a catch-all handler that redirects everything to the angular app:
#app.route('/', defaults={'path': ''})
#app.route('/<path>')
def index(path):
return make_response(open('Ang/templates/index.html').read())
Now, I'm trying to figure out what to do with 404 errors. Most of the Angular apps I've seen do the following:
.otherwise({ redirectTo: '/' })
which means that there is no way they can serve up a proper 404.
However, I would much rather return a proper 404, with a 404 status code (mainly for SEO purposes).
What is the best way to handle 404s with Angular? Should I not worry about it and stick with a catch-all? Or should I remove the catch-all and serve up proper 404's on the server side?
edited for clarity
I think you are confusing Flask routes with Angular routes.
The 404 error code is part of the HTTP protocol. A web server uses it as a response to a client when the requested URL is not known by the server. Because you put a catch-all in your Flask server you will never get a 404, Flask will invoke your view function for any URLs that you type in the address bar. In my opinion you should not have a catch-all and just let Flask respond with 404 when the user types an invalid URL in the address bar, there is nothing wrong with that. Flask even allows you to send a custom page when a 404 code is returned, so you can make the error page look like the rest of your site.
On the Angular side, there is really no HTTP transaction because all the routing internal to the application happens in the client without the server even knowing. This may be part of your confusion, Angular links are handled entirely in the client without any requests made to the server even in html5mode, so there is no concept of a 404 error in this context, simply because there is no server involvement. An Angular link that sends you to an unknown route will just fall into the otherwise clause. The proper thing to do here is to either show an error message (if the user needs to know about this condition and can do something about it) or just ignore the unknown route, as the redirectTo: '/' does.
This does not seem to be your case, but if in addition to serving the Angular application your server implemented an API that Angular can use while it runs, then Angular could get a 404 from Flask if it made an asynchronous request to this API using an invalid URL. But you would not want to show a 404 error page to the user if that happened, since the request was internal to the application and not triggered by the user directly.
I hope this helps clarify the situation!
After playing around for a bit, as well as some discussions with Miguel, I compiled a few different solutions:
Just use a catch-all and don't worry about proper 404's. This can be set up with server-side code (like in my original solution), or better, with URL re-writing on your web server.
Reserve a certain section of your site for your angular app (like /app). For this section, set up a catch-all and don't worry about proper 404's. Your other pages will be served up as regular pages and visiting any invalid URL that doesn't start with /app will result in a proper 404.
Continuously make sure that all of your routes in app.js are mirrored in your server-side code (yes, pretty annoying), where you'll have those routes serve up your angular app. All other routes will 404.
P.S. The second option is my personal favorite. I've tried this out and it works quite well.
This is an old thread, but I cam across it while searching for the answer.
Add this to the end of your appRoutes.js and make a 404.html view.
.when('/404', {
templateUrl: 'views/404.html',
controller: 'MainController'
})
.otherwise({ redirectTo: '/404' })
I think that a real http 404 is going to be pretty useless "for SEO purposes" if you are not serving usable non-javascript content for real pages of your site. A search indexer is unlikely to be able to render your angular site for indexing.
If you are worried about SEO, you will need some sort of server side way to render the content that your angular pages are rendering. If you have that, adding 404s for invalid URLs is the easiest part of the problem.
Here is the best way to handle the error and works nicely
function ($routeProvider, $locationProvider, $httpProvider) {
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
};
window.location = "/";
}
if (status == 404) {
var deferred = $q.defer();
var req = {
config: response.config,
deferred: deferred
};
window.location = "#/404";
}
// otherwise
//return $q.reject(response);
window.location = "#/500";
}
return function (promise) {
return promise.then(success, error);
};
}
];
$httpProvider.responseInterceptors.push(interceptor);
});
// routes
app.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/404', {
templateUrl: '/app/html/inserts/error404.html',
controller: 'RouteCtrl'
})
.when('/500', {
templateUrl: '/app/html/inserts/error404.html',
controller: 'RouteCtrl'
})
......
};

Categories