I have created a page inspired by the Angular SPA ADAL sample which can be found here
Upon returning from the Microsoft login page and accessing my API secured with AAD, the angular .config() function is called multiple times. This causes issues with updating the scope initially set in the app. However, after this initial conflict, everything functions as expected. Reloading the page does not replicate this issue, it only occurs the first time after logging in.
Is this normal behavior with ADAL? Is there a way to prevent this from happening? Am I incorrectly updating $scope from AJAX callbacks?
Here are some key code snippets:
app.js:
'use strict';
angular.module('app', ['ngRoute', 'AdalAngular'])
.config(['$routeProvider', '$httpProvider', 'adalAuthenticationServiceProvider', '$locationProvider', function ($routeProvider, $httpProvider, adalProvider, $locationProvider) {
$routeProvider.when("/visit", {
controller: "visitCtrl",
templateUrl: "/ngViews/Visit.html",
requireADLogin: false,
}).when("/visit/:visitNumber", {
controller: "visitCtrl",
templateUrl: "/ngViews/Visit.html",
requireADLogin: false,
}).when("/", {
controller: "homeCtrl",
templateUrl: "/ngViews/Home.html",
requireADLogin: false,
}).when("/teamwork", {
controller: "teamworkCtrl",
templateUrl: "/ngViews/Teamwork.html",
requireADLogin: false,
}).when("/mywork", {
controller: "myWorkCtrl",
templateUrl: "/ngViews/MyWork.html",
requireADLogin: false,
}).when("/dashboard", {
controller: "dashboardCtrl",
templateUrl: "/ngViews/Dashboard.html",
requireADLogin: false,
}).when("/error", {
templateUrl: "/ngViews/Error.html",
controller: "errorCtrl",
requireADLogin: false,
});
$routeProvider.otherwise({ redirectTo: '/' }); // needed to avoid a bug in ADAL see: https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/42
var endpoints = cmSettings.adalEndpoints;
//$locationProvider.html5Mode(true); // breaks ADAL
adalProvider.init(
{
instance: cmSettings.aadInstance,
tenant: cmSettings.tenant,
clientId: cmSettings.clientId,
extraQueryParameter: 'nux=1',
endpoints: endpoints,
cacheLocation: 'localStorage', // enable this for IE, as sessionStorage does not work for localhost.
// Also, token acquisition for the To Go API will fail in IE when running on localhost, due to IE security restrictions.
},
$httpProvider
);
}]);
controller:
'use strict'
angular.module('app')
.controller('navBarCtrl', ['$scope', '$location', 'visitsSvc', '$timeout', function ($scope, $location, visitsSvc, $timeout) {
$scope.myWorkCount = 0;
$scope.teamworkCount = 0;
$scope.loading = false;
var updateVisitCount = function () {
$scope.loading = true;
$scope.$on("cm:myVisitsReceived", function (event, args) {
$timeout(function () {
if (args.data) {
$scope.myWorkCount = args.data.length;
}
$scope.loading = false;
});
});
$scope.$on("cm:teamVisitsReceived", function (event, args) {
$timeout(function () {
if (args.data) {
$scope.teamworkCount = args.data.length;
}
$scope.loading = false;
});
});
visitsSvc.getMyVisits();
visitsSvc.getTeamVisits();
}
if ($scope.userInfo.isAuthenticated && !$scope.loading) {
updateVisitCount();
} else {
$scope.$on("adal:loginSuccess", function (scope) {
updateVisitCount();
});
}
}]);
data service:
angular.module('app')
.factory('visitsSvc', ['$http', '$rootScope', function ($http, $rootScope) {
$http.defaults.useXDomain = true;
delete $http.defaults.headers.common['X-Requested-With'];
return {
getMyVisits: function () {
$http.get(cmSettings.apiUrl + '/api/v1/visits/my').success(function (data, status, headers, config) {
$rootScope.$broadcast("cm:myVisitsReceived", { data: data, status: status, success: true });
})
.error(function (data, status, headers, config) {
$rootScope.$broadcast("cm:myVisitsReceived", { data: data, status: status, success: false });
});
},
getTeamVisits: function () {
$http.get(cmSettings.apiUrl + '/api/v1/visits/team').success(function (data, status, headers, config) {
$rootScope.$broadcast("cm:teamVisitsReceived", { data: data, status: status, success: true });
})
.error(function (data, status, headers, config) {
$rootScope.$broadcast("cm:teamVisitsReceived", { data: data, status: status, success: false });
});
},
getVisit: function (visitNumber) {
$http.get(cmSettings.apiUrl + '/api/v1/visits/' + visitNumber).success(function (data, status, headers, config) {
$rootScope.$broadcast("cm:visitsReceived", { data: data, status: status, success: true });
})
.error(function (data, status, headers, config) {
$rootScope.$broadcast("cm:visitsReceived", { data: data, status: status, success: false });
});;
},
search: function (searchTerms) {
return $http.get(cmSettings.apiUrl + '/api/v1/visits/search/' + searchTerms).success(function (data, status, headers, config) {
$rootScope.$broadcast("cm:searchVisitsReceived", { data: data, status: status, success: true });
})
.error(function (data, status, headers, config) {
$rootScope.$broadcast("cm:searchVisitsReceived", { data: data, status: status, success: false });
});
},
};
}])
console output:
DOM7011: The code on this page disabled back and forward caching. For more information, see: http://go.microsoft.com/fwlink/?LinkID=291337
File: authorize
HTML1300: Navigation occurred.
File: login
HTML1200: microsoftonline.com is on the Internet Explorer Compatibility View List ('C:\Users\Micah\AppData\Local\Microsoft\Internet Explorer\IECompatData\iecompatdata.xml').
File: login
DOM7011: The code on this page disabled back and forward caching. For more information, see: http://go.microsoft.com/fwlink/?LinkID=291337
File: authorize
HTML1506: Unexpected token.
File: localhost:44300, Line: 115, Column: 1
The returned id_token is not parseable.
The returned id_token is not parseable.
State: 0edacc1d-a253-42ac-8a1d-cf1206ad3beb
State status:true
State is right
renewToken is called for resource:https://cloudmedIdentity.onmicrosoft.com/dataApi
Add adal frame to document:adalRenewFramehttps://cloudmedIdentity.onmicrosoft.com/dataApi
...
(replace with your own unique content)
...
Fragment has access token