UI Router and Satellizer force login - angularjs

I know this question isn't very specific, but I have no idea how to solve this issue or where I should begin..
I'm using Satellizer for authentication with UI-Router. the problem is I have 2 routes /sign_in and /profile the /sign_in issue a request to the server returning the user info (when successful login)... this currentUser info is used all over the application...
the problem is when a user try to go to /profile before login, it renders the empty view... how do I enforce user to login first before accessing these "restricted" views ?!
again I know this isn't a specific question but I really don't know where to start

Have a look into handling the routechangestart event on UI router, I did something similar.
I think this will give you some clues http://arthur.gonigberg.com/2013/06/29/angularjs-role-based-auth/
The key is that you need to create some form of authentication service where you can store the logged in state of the user, and since a service is invoked once (singleton) it is shared across your app (controllers, other services, etc). Then in the routechangestart event handling you can prevent routing to the target page and redirect to login if user is not authenticated.

I found the answer in this Github Repo
the idea is to have role based authentication as discussed in the Repo readme.
so the code for forbidding the unauthorised access to /profile would be something like the following
$stateProvider.state("profile", {
url: "/profile",
templateUrl: "app/profile/profile.html",
controller: "ProfileCtrl",
controllerAs: "user",
data: {
permissions: {
except: ['guest'],
redirectTo: 'sign_in'
}
}
});
given that you defined the guest role
definePermissions = function(Permission, Identity) {
Permission.defineRole('guest', function(stateParams) {
return !Identity.currentUser;
});
};

Related

Angular ADAL requires authentication for non authenticated routes

Scenario is AngularJS 1.6.5 SPA, c# WebAPI and Azure AD (AAD) for authentication. I'm using Angular-ADAL library to handle the authentication and angular-route to handle routes. Strange thing is that routes that CAN be anonymous (i.e. DO NOT require the requireADLogin: true in the route definition) but need to go to the backend (for example to get an image or to get data from the API), get intercepted by ADAL and never get to the backend/API.
My routes are defined like so, when I want a route protected:
.when('/clasesDeDocumento', {
templateUrl: 'app/views/mantenedores/clasesDeDocumento/clasesDeDocumento.html',
controller: 'clasesDeDocumentoController',
controllerAs: 'vm',
requireADLogin: true,
title: "clases de documentos"
})
And similar to the above, but without the requiredADLogin: true when not protected.
According to the documentation:
Routes that do not specify the requireADLogin=true property are added to the anonymousEndpoints array automatically.
Clicking on an unprotected link does not take you to the Azaure Authentication page, however the backend/API request gets intercepted and an error is thrown.
I have solved this (manually) adding an anonymousEndpoints array, but for larger applications, this would not be feasible.
Any ideas?
This is expected behavior. The requireADLogin and anonymousEndpoints parameters are used for the different purpose.
The requireADLogin is used for the whether the routes are needle to protect. If it is true, the app will requires users to authenticate first before they can access the route.
The anonymousEndpoints parameter is used help the adal library to determine whether the $http service required to inject the token. And by default, the route URL will be added into the anonymousEndpoints if we doesn't specify the requireADLogin parameter to true(refer the routeChangeHandler).

Adal Infinite redirection + Edge + Trusted sites

Background:
I have a single page app (built using Angular) which uses adal and adal-angular to authenticate against Azure Active Directory.
Have been using version 1.0.7 of adal and adal-angular (tried using 1.0.14 as well but still no luck) and ui-router for routing.
Issue:
Few of our users are getting continuous authentication loop while trying to access the web application on Edge browser specifically.
Note that it works fine with IE, Chrome and Firefox. Surprisingly it also works fine when Edge is opened in InPrivate window.
This issue is device specific, user specific and only occurs in Edge.
Workaround:
When my site is added to the trusted sites (via Control Panel -> Internet Options), the authentication loop issue is resolved and everything works seamlessly.
Any idea why this is happening?
From what I’m assuming as of now is that it’s a cookie issue when adal writes to the auth cookie to the site and Edge can’t seem to read it?
Also any suggestions for a better fix/workaround for this? As I can’t tell all my users to go and add my website to their trusted sites collection.
Code snippet of app.js:
function authenticationInit(adalAuthenticationServiceProvider, $httpProvider, $locationProvider) {
$locationProvider.html5Mode(false);
var endpoints = {
// Map the location of a request to an API to a the identifier of the associated resource
"EndPointKey": window.config.aadEndPointUrl,
"EndPointValue": window.config.aadResouceIdUrl
};
adalAuthenticationServiceProvider.init(
{
instance: window.config.AADAuthenticationInstance,
tenant: window.config.tenant,
clientId: window.config.clientId,
extraQueryParameter: 'nux=1',
endpoints: endpoints
}, $httpProvider);
}
function registerRoutes($stateProvider) {
$stateProvider
.state('home', {
templateUrl: getViewUrl('widgets'),
controller: 'WidgetsController',
controllerAs: 'widget',
url: '/dashboard'
})
.state('terms',
{
templateUrl: getViewUrl('terms'),
controller: 'TermsController',
controllerAs: 'terms',
url: '/terms'
})
}
$rootScope.$on('$locationChangeStart', function (e) {
if (adalAuthenticationService.userInfo.isAuthenticated == false) { // Will be executed during first time login and token expiration
adalAuthenticationService.login();
}
});
$rootScope.$on("adal:loginSuccess", function (e) { // Will be executed after AAD authentication is successful
NavigationFactory.navigateTo('home');
});
Have raised the same query here- https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/537
adal uses localStorage to save the tokens and reads data from it later on (you also have the option to change it to session storage). The point is that if adal is not able to write into local storage, you will not get the tokens. There is a setting in Microsoft Edge that lets the websites store data. To enable this, go to: Settings>Advanced Settings and enable: 'Let sites save protected media license on my device'. I hope this fixes your issue.

How to check if user is authenticated in AngularJS controller?

I am using Auth0 in my AngularJS 1.x.x app.
The way I have the app set up is by using div ng-if="isAuthenticated" to display (and hide) div's in my templates. However, I want to be able to prevent API calls in my controllers if the user is not authenticated. What is the correct syntax to block those calls?
I think I'm looking for something like:
if (isAuthenticated) {
ServerRequest.getAllPatients({
}).then(function(resp){
$scope.myTestData = resp
}).catch(function(err){
console.log(err);
});
} else { alert("you are not logged in") }
It depends on the exact requirements but in general you would have an Angular service that keeps track if the user is authenticated or not and the you inject this service wherever you need to apply conditional logic based of the authentication status.
Another possibility would be to completely reject the navigation to certain routes if those routes require an authenticated user and there isn't one. You could check this article for some guidance (Deal with users authentication in an AngularJS web app).

Firebase $onAuth has wrong authData after $authWithOAuthRedirect from Facebook

I am trying to authenticate users of my Firebase (Angularfire) app with Facebook Login.
Everything works as expected when I authenticate with a pop-up window, but to support as many browsers as possible (Chrome on iOS doesn't support pop-ups, for e.g.) I want to fallback to authenticating with a redirect ($authWithOAuthRedirect).
I have confirmed my setting in Facebook are correct (my app ID and secret, for e.g.) but when I am redirected back to my app after Facebook authenticating with a redirect, $onAuth fires but I don't have my Facebook authData.
Instead, I have anonymous authData. For a bit of background; all users are authenticated anonymously if they are not otherwise authenticated (with Facebook, in this e.g.).
I can't see to find why this would be - the user should now be authenticated with Facebook, and have the Facebook authData.
Excepts of my code are below for some context:
Triggered when a user clicks the login button
function logIn () {
firebaseAuth
.$authWithOAuthRedirect('facebook', function (error) {
if (error) {
console.log(error);
}
});
}
$onAuth (inside my Angular app's run)
function run ($rootScope, firebaseAuth, sessionStore) {
$rootScope
.$on('$routeChangeError', function (event, next, prev, error) {
if (error === 'AUTH_REQUIRED') {
console.log(error);
}
});
$rootScope
.$on('$routeChangeSuccess', function (event, current, prev) {
$rootScope.title = current.$$route.title;
});
firebaseAuth
.$onAuth(onAuth);
function onAuth (authData) {
console.log(authData);
}
}
Route resolver to otherwise anonymously authenticates users
function sessionState ($q, firebaseAuth) {
var deferred = $q.defer();
firebaseAuth
.$requireAuth()
.then(deferred.resolve, guest);
return deferred.promise;
function guest () {
firebaseAuth
.$authAnonymously()
.then(deferred.resolve, rejected);
}
function rejected () {
deferred.reject('AUTH_REQUIRED');
}
}
The route resolver (sessionState) checks to see if the user is authenticated already, and if not, tries to anonymously authenticate them.
After the Facebook authentication redirect, the user will already be authenticated, and therefore does not need to be anonymously authenticated.
But, it appears that they are? As $onAuth logs the authData to the console, and it is anonymous.
Any help with this would be much appreciated! I am sure it has something to do with my route resolver, as pop-up authentication works fine (the route is already resolved).
EDIT: I tried completely removing my route resolver in case it was that causing an issue, but it made no difference. The user was just 'unauthenticated' instead of being either authenticated with Facebook (after $authWithOAuthRedirect) or anonymously.
UPDATE: I tried authenticating with Twitter and the redirect transport and I have encountered the exact same problem. I have also tried using port 80, instead of port 3000 that my app was being served on locally, but no joy.
UPDATE: When I turn off html5Mode mode in my app - and routes now begin with #s - $authWithOAuthRedirect works perfectly. From this I can only assume that $authWithOAuthRedirect does not support AngularJS's html5Mode. Can anyone confirm this is an issue, or do I need to change my code to support html5Mode and authWithOAuthRedirect?
EXAMPLE REPO Here is an example repo demonstrating the problem: https://github.com/jonathonoates/myapp
Look in the dist directory - you should be able to download this and run the app to reproduce the problem. In scripts/main.js is the app's JS; I've added a couple of comments but it's pretty self explanatory.
To reproduce the problem: click on the 'Facebook Login' button, and you'll be redirected to Facebook to authenticate. FB will redirect you back to the app, but here lies the problem - you won't be authenticated, and the returned authData will be null - you'll see this in the console
UPDATE: When I add a hashPrefix in html5Mode e.g.
$locationProvider
.html5Mode(true)
.hashPrefix('!');
The app works as I would expect - authenticating with Facebook and the redirect transport works.
Couple of niggles though:
The URL has #%3F appended to it, and is available/visible in the browser's history.
This would rewrite URLs with #! in browsers that do not support History.pushState (html5Mode), and some less advanced search engines might look for a HTML fragment because of the 'hashbang'.
I'll look into highjacking the URL upon being redirected back from Facebook instead of using hashPrefix. In the URL there is a __firebase_request_key which may be significant e.g.
http://localhost:3000/#%3F&__firebase_request_key=
It looks like this is indeed an incompatibility between Firebase and AngularJS's html5mode as you suspected. At the end of the redirect flow, Firebase was leaving the URL as "http://.../#?", and Angular apparently doesn't like that so it did a redirect to "http://.../" This redirect interrupts Firebase (the page reloads while we're trying to auth against the backend) and so it is unable to complete the authentication process.
I've made an experimental fix that ensures we revert the URL to http://.../#" at the end of the redirect flow, which Angular is happy with, thus preventing the problematic redirect. You can grab it here if you like: https://mike-shared.firebaseapp.com/firebase.js
I'll make sure this fix gets into the next version of the JS client. You can keep an eye on our changelog to see when it is released.

Multiple Login Screens and Namespaces

I have a problem I can't figure out, in my application I have two login screens, one for the admin the other for the regular user. These login screens however point to the same controller in the same Auth namespace aside from that I have separated the rest of my controllers and routes into two namespaces; FrontEnd Namespace and BackEnd namespace.
Now my issue right now is when a regular user uses their credentials to login from the backend they are allowed access, I installed this package kodein/acl to handle roles and permissions and it seems to be working because when a user logs in to the backend now they can't do anything. The problem now is that even though they can't do anything they are still able to redirect to admin dashboard.
What I want here is this; when a regular user tries to login to admin backend they are denied access. I am a bit confused, i dunno how to o about it.
Do I have to create separate auth controllers and methods in both namespaces? Is that even possible? How would i go about it?
I use AngularJS for my frontend so in my route file i have this:
Route::group(['domain' => 'admin.website.loc'], function() {
Route::any('{url?}', function($url) {
return view('backend.index');
})->where(['url' => '[-a-zA-Z0-9/]+']);
});
Route::any('{url?}', function($url) {
return view('frontend.index');
})->where(['url' => '[-a-zA-Z0-9/]+']);
Which catch all urls and return to a single Index page,on the front end since i use JWT for authentication it validates the token on the frontend and if invalid or not available takes the user to the login page.
I thought of something else, maybe a temporary measure maybe permanent, i added the following code to my login function():
$url_parts = parse_url($request->url());
$host_parts = explode('.', $url_parts['host']);
if ($host_parts[0] == "admin")
{
$user = User::find(Auth::user()->id);
if (!$user->is('administrator'))
{
Auth::logout();
return response()->json(['error' => 'You Are Not Authorized!']);
}
}
first i get the request url,
then i get the "host" part of the request url and split it using '.' as the delimiter, this way i can check the subdomain.
Since my admin side uses a sub domain i check to see if the login request was from a url with the 'admin' subdomain, if it was then i check the authenticated user's role to see if they're administrator, if they are not, i log them out immediately and return an error message.
This way i don't have to create a separate function and route for the two login screens.

Resources