diff --git a/README.md b/README.md
index 2cb218cf..88f88df2 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ If you find a security issue with our libraries or services please report it to
## The Library
-This is a GA released version. The current version is **1.0.11**.
+This is a GA released version. The current version is **1.0.12**.
You have multiple ways of getting ADAL JS:
@@ -32,10 +32,10 @@ Via NPM:
Via CDN:
-
-
+
+
-CDN will be updated to latest version 1.0.11.
+CDN will be updated to latest version 1.0.12.
Via Bower:
diff --git a/bower.json b/bower.json
index f65046cd..3263b857 100644
--- a/bower.json
+++ b/bower.json
@@ -1,13 +1,14 @@
{
"name": "adal-angular",
- "version": "1.0.11",
+ "version": "1.0.12",
"homepage": "https://github.com/AzureAD/azure-activedirectory-library-for-js",
"authors": [
"MSOpentech"
],
"description": "Azure Active Directory Client Library for js",
- "main": ["./lib/adal.js",
- "./lib/adal-angular.js"
+ "main": [
+ "./lib/adal.js",
+ "./lib/adal-angular.js"
],
"moduleType": [
"node"
@@ -19,7 +20,7 @@
"directory",
"azure"
],
- "licenses": [
+ "licenses": [
{
"type": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0"
@@ -39,6 +40,7 @@
"angular-resource": "~1.2.26",
"angular-mocks": "~1.2.26",
"jasmine": "2.0.0",
- "angular-route": "~1.2.26"
+ "angular-route": "~1.2.26",
+ "angular-ui-router": "^0.3.1"
}
}
diff --git a/changelog.txt b/changelog.txt
index 24b4724a..454fc9f4 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,12 @@
+Version 1.0.12
+==========================
+* Adding support for Login using a pop-up instead of a full redirect. Please see this: https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/60
+* Updating anonymousEndpoints feature to handle nested states in ui-router. Please see this: https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/345
+* Fix bug in anonymousEndpoints allowing templateUrl property of the route/state to be declared as a function. Thanks @dhodgin for the Pull Request. Please see this: https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/368
+* Using window.crypto.getRandomValues API to generate version 4 UUID as per RFC 4122. Please see this: https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/88
+* Fix bug in handleWindowCallback to call the callback defined on config after Login operation is completed. Please see this: https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/324
+* Other bug fixes and updates.
+
Version 1.0.11
==========================
* Adding support for using a special html for iFrames. This prevents app reloading in the iframe. Please see this: https://github.com/AzureAD/azure-activedirectory-library-for-js/wiki/FAQs#q1-my-app-is-re-loading-every-time-adal-renews-a-token
diff --git a/dist/adal-angular.min.js b/dist/adal-angular.min.js
index f0de0170..0886f8a8 100644
--- a/dist/adal-angular.min.js
+++ b/dist/adal-angular.min.js
@@ -1,2 +1,2 @@
-/*! adal-angular v1.0.11 2016-07-19 */
-!function(){"use strict";if("undefined"!=typeof module&&module.exports&&(module.exports.inject=function(a){return new AuthenticationContext(a)}),angular){var a=angular.module("AdalAngular",[]);a.provider("adalAuthenticationService",function(){var a=null,b={isAuthenticated:!1,userName:"",loginError:"",profile:""},c=function(c){var d=a.getCachedToken(c);b.isAuthenticated=null!==d&&d.length>0;var e=a.getCachedUser()||{userName:""};b.userName=e.userName,b.profile=e.profile,b.loginError=a.getLoginError()};this.init=function(b,d){if(!b)throw new Error("You must set configOptions, when calling init");var e=window.location.hash,f=window.location.href;e&&(f=f.replace(e,"")),b.redirectUri=b.redirectUri||f,b.postLogoutRedirectUri=b.postLogoutRedirectUri||f,d&&d.interceptors&&d.interceptors.push("ProtectedResourceInterceptor"),a=new AuthenticationContext(b),c(a.config.loginResource)},this.$get=["$rootScope","$window","$q","$location","$timeout",function(d,e,f,g,h){function i(a,b){return b.requireADLogin?a.requireADLogin!==!1:!!a.requireADLogin}function j(b){if(a.config&&a.config.anonymousEndpoints)for(var c=0;c-1)return!0;return!1}var k=function(f,i,j){a.verbose("Location change event from "+j+" to "+i);var k=e.location.hash;if(a.isCallback(k)){a.verbose("Processing the hash: "+k);var l=a.getRequestInfo(k);if(a.saveTokenFromHash(l),l.requestType!==a.REQUEST_TYPE.LOGIN&&(a.callback=e.parent.AuthenticationContext().callback,l.requestType===a.REQUEST_TYPE.RENEW_TOKEN&&(a.callback=e.parent.callBackMappedToRenewStates[l.stateResponse])),l.stateMatch)if("function"==typeof a.callback){if(f.preventDefault(),l.requestType===a.REQUEST_TYPE.RENEW_TOKEN){if(l.parameters.access_token)return void a.callback(a._getItem(a.CONSTANTS.STORAGE.ERROR_DESCRIPTION),l.parameters.access_token);if(l.parameters.id_token)return void a.callback(a._getItem(a.CONSTANTS.STORAGE.ERROR_DESCRIPTION),l.parameters.id_token);if(l.parameters.error)return void a.callback(a._getItem(a.CONSTANTS.STORAGE.ERROR_DESCRIPTION),null)}}else{c(a.config.loginResource),b.userName?(h(function(){c(a.config.loginResource),d.userInfo=b},1),d.$broadcast("adal:loginSuccess")):d.$broadcast("adal:loginFailure",a._getItem(a.CONSTANTS.STORAGE.ERROR_DESCRIPTION));var m=a._getItem(a.CONSTANTS.STORAGE.LOGIN_REQUEST);m&&(a.verbose("Redirecting to start page: "+m),f.preventDefault(),!g.$$html5&&m.indexOf("#")>-1&&g.url(m.substring(m.indexOf("#")+1)),e.location=m)}else d.$broadcast("adal:stateMismatch",a._getItem(a.CONSTANTS.STORAGE.ERROR_DESCRIPTION))}else c(a.config.loginResource),b.isAuthenticated||!b.userName||a._renewActive||(a._renewActive=!0,a.acquireToken(a.config.loginResource,function(c,e){a._renewActive=!1,c?d.$broadcast("adal:loginFailure","auto renew failure"):e&&(b.isAuthenticated=!0)}));h(function(){c(a.config.loginResource),d.userInfo=b},1)},l=function(){a.info("Login event for:"+g.$$url),a.config&&a.config.localLoginUrl?g.path(a.config.localLoginUrl):(a.info("Start login at:"+window.location.href),d.$broadcast("adal:loginRedirect"),a.login())},m=function(c,d){d&&d.$$route&&(i(d.$$route,a.config)?b.isAuthenticated||a._renewActive||a.loginInProgress()||(a.info("Route change event for:"+g.$$url),l()):d.$$route.templateUrl&&!j(d.$$route.templateUrl)&&a.config.anonymousEndpoints.push(d.$$route.templateUrl))},n=function(c,d,e,f,h){d&&(i(d,a.config)?b.isAuthenticated||a._renewActive||a.loginInProgress()||(a.info("State change event for:"+g.$$url),l()):d.templateUrl&&!j(d.templateUrl)&&a.config.anonymousEndpoints.push(d.templateUrl))},o=function(b,c,d,e,f,g){a.verbose("State change error occured. Error: "+g),g&&g.data&&(a.info("Setting defaultPrevented to true if state change error occured because adal rejected a request. Error: "+g.data),b.preventDefault())};return d.$on("$routeChangeStart",m),d.$on("$stateChangeStart",n),d.$on("$locationChangeStart",k),d.$on("$stateChangeError",o),c(a.config.loginResource),d.userInfo=b,{config:a.config,login:function(){l()},loginInProgress:function(){return a.loginInProgress()},logOut:function(){a.logOut()},getCachedToken:function(b){return a.getCachedToken(b)},userInfo:b,acquireToken:function(b){var c=f.defer();return a._renewActive=!0,a.acquireToken(b,function(d,e){a._renewActive=!1,d?(a.error("Error when acquiring token for resource: "+b,d),c.reject(d)):c.resolve(e)}),c.promise},getUser:function(){var b=f.defer();return a.getUser(function(c,d){c?(a.error("Error when getting user",c),b.reject(c)):b.resolve(d)}),b.promise},getResourceForEndpoint:function(b){return a.getResourceForEndpoint(b)},clearCache:function(){a.clearCache()},clearCacheForResource:function(b){a.clearCacheForResource(b)},info:function(b){a.info(b)},verbose:function(b){a.verbose(b)}}}]}),a.factory("ProtectedResourceInterceptor",["adalAuthenticationService","$q","$rootScope",function(a,b,c){return{request:function(c){if(c){c.headers=c.headers||{};var d=a.getResourceForEndpoint(c.url);if(a.verbose("Url: "+c.url+" maps to resource: "+d),null===d)return c;var e=a.getCachedToken(d);if(e)return a.info("Token is available for this url "+c.url),c.headers.Authorization="Bearer "+e,c;if(a.loginInProgress())return a.info("login is in progress."),c.data="login in progress, cancelling the request for "+c.url,b.reject(c);var f=b.defer();return a.acquireToken(d).then(function(b){a.verbose("Token is available"),c.headers.Authorization="Bearer "+b,f.resolve(c)},function(a){c.data=a,f.reject(c)}),f.promise}},responseError:function(d){if(a.info("Getting error in the response."),d){if(401===d.status){var e=a.getResourceForEndpoint(d.config.url);a.clearCacheForResource(e),c.$broadcast("adal:notAuthorized",d,e)}else c.$broadcast("adal:errorResponse",d);return b.reject(d)}}}}])}else console.error("Angular.JS is not included")}();
\ No newline at end of file
+/*! adal-angular v1.0.12 2016-08-31 */
+!function(){"use strict";if("undefined"!=typeof module&&module.exports&&(module.exports.inject=function(a){return new AuthenticationContext(a)}),angular){var a=angular.module("AdalAngular",[]);a.provider("adalAuthenticationService",function(){var a=null,b={isAuthenticated:!1,userName:"",loginError:"",profile:""},c=function(c){var d=a.getCachedToken(c);b.isAuthenticated=null!==d&&d.length>0;var e=a.getCachedUser()||{userName:""};b.userName=e.userName,b.profile=e.profile,b.loginError=a.getLoginError()};this.init=function(b,d){if(!b)throw new Error("You must set configOptions, when calling init");var e=window.location.hash,f=window.location.href;e&&(f=f.replace(e,"")),b.redirectUri=b.redirectUri||f,b.postLogoutRedirectUri=b.postLogoutRedirectUri||f,b.isAngular=!0,d&&d.interceptors&&d.interceptors.push("ProtectedResourceInterceptor"),a=new AuthenticationContext(b),c(a.config.loginResource)},this.$get=["$rootScope","$window","$q","$location","$timeout","$injector",function(d,e,f,g,h,i){function j(a,b){return b.requireADLogin?a.requireADLogin!==!1:!!a.requireADLogin}function k(b){if(a.config&&a.config.anonymousEndpoints)for(var c=0;c-1)return!0;return!1}function l(a){var b=null,c=[];if(a.hasOwnProperty("parent"))for(b=a;b;)c.unshift(b),b=i.get("$state").get(b.parent);else for(var d=a.name.split("."),e=0,f=d[0];e-1&&g.url(n.substring(n.indexOf("#")+1)),e.location=n)}}else d.$broadcast("adal:stateMismatch",a._getItem(a.CONSTANTS.STORAGE.ERROR_DESCRIPTION))}else c(a.config.loginResource),b.isAuthenticated||!b.userName||a._renewActive||(a._renewActive=!0,a.acquireToken(a.config.loginResource,function(c,e){a._renewActive=!1,c?d.$broadcast("adal:loginFailure","auto renew failure"):e&&(b.isAuthenticated=!0)}));h(function(){c(a.config.loginResource),d.userInfo=b},1)},n=function(){a.info("Login event for:"+g.$$url),a.config&&a.config.localLoginUrl?g.path(a.config.localLoginUrl):(a.info("Start login at:"+window.location.href),d.$broadcast("adal:loginRedirect"),a.login())},o=function(c,d){if(d&&d.$$route)if(j(d.$$route,a.config))b.isAuthenticated||a._renewActive||a.loginInProgress()||(a.info("Route change event for:"+g.$$url),n());else{var e;e="function"==typeof d.$$route.templateUrl?d.$$route.templateUrl(d.params):d.$$route.templateUrl,e&&!k(e)&&a.config.anonymousEndpoints.push(e)}},p=function(c,d,e,f,h){if(d)for(var i=l(d),m=null,o=0;o-1},AuthenticationContext.prototype.getCachedToken=function(a){if(!this._hasResource(a))return null;var b=this._getItem(this.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY+a),c=this._getItem(this.CONSTANTS.STORAGE.EXPIRATION_KEY+a),d=this.config.expireOffsetSeconds||120;return c&&c>this._now()+d?b:(this._saveItem(this.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY+a,""),this._saveItem(this.CONSTANTS.STORAGE.EXPIRATION_KEY+a,0),null)},AuthenticationContext.prototype.getCachedUser=function(){if(this._user)return this._user;var a=this._getItem(this.CONSTANTS.STORAGE.IDTOKEN);return this._user=this._createUser(a),this._user},AuthenticationContext.prototype.registerCallback=function(a,b,c){this._activeRenewals[b]=a,window.callBacksMappedToRenewStates[a]||(window.callBacksMappedToRenewStates[a]=[]);var d=this;window.callBacksMappedToRenewStates[a].push(c),window.callBackMappedToRenewStates[a]||(window.callBackMappedToRenewStates[a]=function(c,e){for(var f=0;f-1)){var b=this._user.profile.upn.split("@");a+="&domain_hint="+encodeURIComponent(b[b.length-1])}return a},AuthenticationContext.prototype._createUser=function(a){var b=null,c=this._extractIdToken(a);return c&&c.hasOwnProperty("aud")&&(c.aud.toLowerCase()===this.config.clientId.toLowerCase()?(b={userName:"",profile:c},c.hasOwnProperty("upn")?b.userName=c.upn:c.hasOwnProperty("email")&&(b.userName=c.email)):this.warn("IdToken has invalid aud field")),b},AuthenticationContext.prototype._getHash=function(a){return a.indexOf("#/")>-1?a=a.substring(a.indexOf("#/")+2):a.indexOf("#")>-1&&(a=a.substring(1)),a},AuthenticationContext.prototype.isCallback=function(a){a=this._getHash(a);var b=this._deserialize(a);return b.hasOwnProperty(this.CONSTANTS.ERROR_DESCRIPTION)||b.hasOwnProperty(this.CONSTANTS.ACCESS_TOKEN)||b.hasOwnProperty(this.CONSTANTS.ID_TOKEN)},AuthenticationContext.prototype.getLoginError=function(){return this._getItem(this.CONSTANTS.STORAGE.LOGIN_ERROR)},AuthenticationContext.prototype.getRequestInfo=function(a){a=this._getHash(a);var b=this._deserialize(a),c={valid:!1,parameters:{},stateMatch:!1,stateResponse:"",requestType:this.REQUEST_TYPE.UNKNOWN};if(b&&(c.parameters=b,b.hasOwnProperty(this.CONSTANTS.ERROR_DESCRIPTION)||b.hasOwnProperty(this.CONSTANTS.ACCESS_TOKEN)||b.hasOwnProperty(this.CONSTANTS.ID_TOKEN))){c.valid=!0;var d="";if(!b.hasOwnProperty("state"))return this.warn("No state returned"),c;if(this.verbose("State: "+b.state),d=b.state,c.stateResponse=d,d===this._getItem(this.CONSTANTS.STORAGE.STATE_LOGIN))return c.requestType=this.REQUEST_TYPE.LOGIN,c.stateMatch=!0,c;if(!c.stateMatch&&window.parent&&window.parent.AuthenticationContext())for(var e=window.parent.AuthenticationContext()._renewStates,f=0;f-1&&b+1-1)return this.config.endpoints[b];if(!(a.indexOf("http://")>-1||a.indexOf("https://")>-1)){if(this.config&&this.config.anonymousEndpoints)for(var c=0;c-1)return null;return this.config.loginResource}return this._getHostFromUri(a)===this._getHostFromUri(this.config.redirectUri)?this.config.loginResource:null},AuthenticationContext.prototype._getHostFromUri=function(a){var b=String(a).replace(/^(https?:)\/\//,"");return b=b.split("/")[0]},AuthenticationContext.prototype.handleWindowCallback=function(){var a=window.location.hash;if(this.isCallback(a)){var b=this.getRequestInfo(a);this.info("Returned from redirect url"),this.saveTokenFromHash(b);var c=null;if(b.requestType===this.REQUEST_TYPE.RENEW_TOKEN&&window.parent&&window.parent!==window)return this.verbose("Window is in iframe"),c=window.parent.callBackMappedToRenewStates[b.stateResponse],void(c&&c(this._getItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION),b.parameters[this.CONSTANTS.ACCESS_TOKEN]||b.parameters[this.CONSTANTS.ID_TOKEN]));window&&window.oauth2Callback&&(this.verbose("Window is redirecting"),c=this.callback),window.location=this._getItem(this.CONSTANTS.STORAGE.LOGIN_REQUEST)}},AuthenticationContext.prototype._getNavigateUrl=function(a,b){var c="common";this.config.tenant&&(c=this.config.tenant);var d=this.instance+c+"/oauth2/authorize"+this._serialize(a,this.config,b)+this._addLibMetadata();return this.info("Navigate url:"+d),d},AuthenticationContext.prototype._extractIdToken=function(a){var b=this._decodeJwt(a);if(!b)return null;try{var c=b.JWSPayload,d=this._base64DecodeStringUrlSafe(c);return d?JSON.parse(d):(this.info("The returned id_token could not be base64 url safe decoded."),null)}catch(e){this.error("The returned id_token could not be decoded",e)}return null},AuthenticationContext.prototype._base64DecodeStringUrlSafe=function(a){return a=a.replace(/-/g,"+").replace(/_/g,"/"),window.atob?decodeURIComponent(escape(window.atob(a))):decodeURIComponent(escape(this._decode(a)))},AuthenticationContext.prototype._decode=function(a){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";a=String(a).replace(/=+$/,"");var c=a.length;if(c%4===1)throw new Error("The token to be decoded is not correctly encoded.");for(var d,e,f,g,h,i,j,k,l="",m=0;c>m;m+=4){if(d=b.indexOf(a.charAt(m)),e=b.indexOf(a.charAt(m+1)),f=b.indexOf(a.charAt(m+2)),g=b.indexOf(a.charAt(m+3)),m+2===c-1){h=d<<18|e<<12|f<<6,i=h>>16&255,j=h>>8&255,l+=String.fromCharCode(i,j);break}if(m+1===c-1){h=d<<18|e<<12,i=h>>16&255,l+=String.fromCharCode(i);break}h=d<<18|e<<12|f<<6|g,i=h>>16&255,j=h>>8&255,k=255&h,l+=String.fromCharCode(i,j,k)}return l},AuthenticationContext.prototype._decodeJwt=function(a){if(this._isEmpty(a))return null;var b=/^([^\.\s]*)\.([^\.\s]+)\.([^\.\s]*)$/,c=b.exec(a);if(!c||c.length<4)return this.warn("The returned id_token is not parseable."),null;var d={header:c[1],JWSPayload:c[2],JWSSig:c[3]};return d},AuthenticationContext.prototype._convertUrlSafeToRegularBase64EncodedString=function(a){return a.replace("-","+").replace("_","/")},AuthenticationContext.prototype._serialize=function(a,b,c){var d=[];if(null!==b){d.push("?response_type="+a),d.push("client_id="+encodeURIComponent(b.clientId)),c&&d.push("resource="+encodeURIComponent(c)),d.push("redirect_uri="+encodeURIComponent(b.redirectUri)),d.push("state="+encodeURIComponent(b.state)),b.hasOwnProperty("slice")&&d.push("slice="+encodeURIComponent(b.slice)),b.hasOwnProperty("extraQueryParameter")&&d.push(b.extraQueryParameter);var e=b.correlationId?b.correlationId:this._guid();d.push("client-request-id="+encodeURIComponent(e))}return d.join("&")},AuthenticationContext.prototype._deserialize=function(a){var b,c=/\+/g,d=/([^&=]+)=([^&]*)/g,e=function(a){return decodeURIComponent(a.replace(c," "))},f={};for(b=d.exec(a);b;)f[e(b[1])]=e(b[2]),b=d.exec(a);return f},AuthenticationContext.prototype._guid=function(){for(var a="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx",b="0123456789abcdef",c=0,d="",e=0;36>e;e++)"-"!==a[e]&&"4"!==a[e]&&(c=16*Math.random()|0),"x"===a[e]?d+=b[c]:"y"===a[e]?(c&=3,c|=8,d+=b[c]):d+=a[e];return d},AuthenticationContext.prototype._expiresIn=function(a){return this._now()+parseInt(a,10)},AuthenticationContext.prototype._now=function(){return Math.round((new Date).getTime()/1e3)},AuthenticationContext.prototype._addAdalFrame=function(a){if("undefined"!=typeof a){this.info("Add adal frame to document:"+a);var b=document.getElementById(a);if(!b){if(document.createElement&&document.documentElement&&(window.opera||-1===window.navigator.userAgent.indexOf("MSIE 5.0"))){var c=document.createElement("iframe");c.setAttribute("id",a),c.style.visibility="hidden",c.style.position="absolute",c.style.width=c.style.height=c.borderWidth="0px",b=document.getElementsByTagName("body")[0].appendChild(c)}else document.body&&document.body.insertAdjacentHTML&&document.body.insertAdjacentHTML("beforeEnd",'');window.frames&&window.frames[a]&&(b=window.frames[a])}return b}},AuthenticationContext.prototype._saveItem=function(a,b){return this.config&&this.config.cacheLocation&&"localStorage"===this.config.cacheLocation?this._supportsLocalStorage()?(localStorage.setItem(a,b),!0):(this.info("Local storage is not supported"),!1):this._supportsSessionStorage()?(sessionStorage.setItem(a,b),!0):(this.info("Session storage is not supported"),!1)},AuthenticationContext.prototype._getItem=function(a){return this.config&&this.config.cacheLocation&&"localStorage"===this.config.cacheLocation?this._supportsLocalStorage()?localStorage.getItem(a):(this.info("Local storage is not supported"),null):this._supportsSessionStorage()?sessionStorage.getItem(a):(this.info("Session storage is not supported"),null)},AuthenticationContext.prototype._supportsLocalStorage=function(){try{return"localStorage"in window&&window.localStorage}catch(a){return!1}},AuthenticationContext.prototype._supportsSessionStorage=function(){try{return"sessionStorage"in window&&window.sessionStorage}catch(a){return!1}},AuthenticationContext.prototype._cloneConfig=function(a){if(null===a||"object"!=typeof a)return a;var b={};for(var c in a)a.hasOwnProperty(c)&&(b[c]=a[c]);return b},AuthenticationContext.prototype._addLibMetadata=function(){return"&x-client-SKU=Js&x-client-Ver="+this._libVersion()},AuthenticationContext.prototype.log=function(a,b,c){if(a<=Logging.level){var d=(new Date).toUTCString(),e="";e=this.config.correlationId?d+":"+this.config.correlationId+"-"+this._libVersion()+"-"+this.CONSTANTS.LEVEL_STRING_MAP[a]+" "+b:d+":"+this._libVersion()+"-"+this.CONSTANTS.LEVEL_STRING_MAP[a]+" "+b,c&&(e+="\nstack:\n"+c.stack),Logging.log(e)}},AuthenticationContext.prototype.error=function(a,b){this.log(this.CONSTANTS.LOGGING_LEVEL.ERROR,a,b)},AuthenticationContext.prototype.warn=function(a){this.log(this.CONSTANTS.LOGGING_LEVEL.WARN,a,null)},AuthenticationContext.prototype.info=function(a){this.log(this.CONSTANTS.LOGGING_LEVEL.INFO,a,null)},AuthenticationContext.prototype.verbose=function(a){this.log(this.CONSTANTS.LOGGING_LEVEL.VERBOSE,a,null)},AuthenticationContext.prototype._libVersion=function(){return"1.0.11"},"undefined"!=typeof module&&module.exports&&(module.exports=AuthenticationContext,module.exports.inject=function(a){return new AuthenticationContext(a)}),AuthenticationContext}();
\ No newline at end of file
+/*! adal-angular v1.0.12 2016-08-31 */
+var AuthenticationContext=function(){"use strict";return AuthenticationContext=function(a){if(this.REQUEST_TYPE={LOGIN:"LOGIN",RENEW_TOKEN:"RENEW_TOKEN",UNKNOWN:"UNKNOWN"},this.CONSTANTS={ACCESS_TOKEN:"access_token",EXPIRES_IN:"expires_in",ID_TOKEN:"id_token",ERROR_DESCRIPTION:"error_description",SESSION_STATE:"session_state",STORAGE:{TOKEN_KEYS:"adal.token.keys",ACCESS_TOKEN_KEY:"adal.access.token.key",EXPIRATION_KEY:"adal.expiration.key",STATE_LOGIN:"adal.state.login",STATE_RENEW:"adal.state.renew",NONCE_IDTOKEN:"adal.nonce.idtoken",SESSION_STATE:"adal.session.state",USERNAME:"adal.username",IDTOKEN:"adal.idtoken",ERROR:"adal.error",ERROR_DESCRIPTION:"adal.error.description",LOGIN_REQUEST:"adal.login.request",LOGIN_ERROR:"adal.login.error",RENEW_STATUS:"adal.token.renew.status"},RESOURCE_DELIMETER:"|",LOADFRAME_TIMEOUT:"6000",TOKEN_RENEW_STATUS_CANCELED:"Canceled",TOKEN_RENEW_STATUS_COMPLETED:"Completed",TOKEN_RENEW_STATUS_IN_PROGRESS:"In Progress",LOGGING_LEVEL:{ERROR:0,WARN:1,INFO:2,VERBOSE:3},LEVEL_STRING_MAP:{0:"ERROR:",1:"WARNING:",2:"INFO:",3:"VERBOSE:"},POPUP_WIDTH:483,POPUP_HEIGHT:600},AuthenticationContext.prototype._singletonInstance)return AuthenticationContext.prototype._singletonInstance;if(AuthenticationContext.prototype._singletonInstance=this,this.instance="https://login.microsoftonline.com/",this.config={},this.callback=null,this.popUp=!1,this.isAngular=!1,this._user=null,this._activeRenewals={},this._loginInProgress=!1,this._renewStates=[],window.callBackMappedToRenewStates={},window.callBacksMappedToRenewStates={},a.displayCall&&"function"!=typeof a.displayCall)throw new Error("displayCall is not a function");if(!a.clientId)throw new Error("clientId is required");this.config=this._cloneConfig(a),this.config.popUp&&(this.popUp=!0),this.config.callback&&"function"==typeof this.config.callback&&(this.callback=this.config.callback),this.config.instance&&(this.instance=this.config.instance),this.config.loginResource||(this.config.loginResource=this.config.clientId),this.config.redirectUri||(this.config.redirectUri=window.location.href),this.config.anonymousEndpoints||(this.config.anonymousEndpoints=[]),this.config.isAngular&&(this.isAngular=this.config.isAngular)},window.Logging={level:0,log:function(a){}},AuthenticationContext.prototype.login=function(){if(this._loginInProgress)return void this.info("Login in progress");var a=this._guid();this.config.state=a,this._idTokenNonce=this._guid(),this.verbose("Expected state: "+a+" startPage:"+window.location),this._saveItem(this.CONSTANTS.STORAGE.LOGIN_REQUEST,window.location),this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR,""),this._saveItem(this.CONSTANTS.STORAGE.STATE_LOGIN,a),this._saveItem(this.CONSTANTS.STORAGE.NONCE_IDTOKEN,this._idTokenNonce),this._saveItem(this.CONSTANTS.STORAGE.ERROR,""),this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION,"");var b=this._getNavigateUrl("id_token",null)+"&nonce="+encodeURIComponent(this._idTokenNonce);return this._loginInProgress=!0,this.popUp?void this._loginPopup(b):void(this.config.displayCall?this.config.displayCall(b):this.promptUser(b))},AuthenticationContext.prototype._openPopup=function(a,b,c,d){try{var e=window.screenLeft?window.screenLeft:window.screenX,f=window.screenTop?window.screenTop:window.screenY,g=window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,h=window.innerHeight||document.documentElement.clientHeight||document.body.clientHeight,i=g/2-c/2+e,j=h/2-d/2+f,k=window.open(a,b,"width="+c+", height="+d+", top="+j+", left="+i);return k.focus&&k.focus(),k}catch(a){return this.warn("Error opening popup, "+a.message),this._loginInProgress=!1,null}},AuthenticationContext.prototype._loginPopup=function(a){var b=this._openPopup(a,"login",this.CONSTANTS.POPUP_WIDTH,this.CONSTANTS.POPUP_HEIGHT);if(null==b)return this.warn("Popup Window is null. This can happen if you are using IE"),this._saveItem(this.CONSTANTS.STORAGE.ERROR,"Error opening popup"),this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION,"Popup Window is null. This can happen if you are using IE"),this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR,"Popup Window is null. This can happen if you are using IE"),void(this.callback&&this.callback(this._getItem(this.CONSTANTS.STORAGE.LOGIN_ERROR),null));if(this.config.redirectUri.indexOf("#")!=-1)var c=this.config.redirectUri.split("#")[0];else var c=this.config.redirectUri;var d=this,e=window.setInterval(function(){b&&!b.closed&&void 0!==b.closed||(d._loginInProgress=!1,window.clearInterval(e));try{b.location.href.indexOf(c)!=-1&&(d.isAngular?window.location.hash=b.location.hash:d.handleWindowCallback(b.location.hash),window.clearInterval(e),d._loginInProgress=!1,d.info("Closing popup window"),b.close())}catch(a){}},20)},AuthenticationContext.prototype.loginInProgress=function(){return this._loginInProgress},AuthenticationContext.prototype._hasResource=function(a){var b=this._getItem(this.CONSTANTS.STORAGE.TOKEN_KEYS);return b&&!this._isEmpty(b)&&b.indexOf(a+this.CONSTANTS.RESOURCE_DELIMETER)>-1},AuthenticationContext.prototype.getCachedToken=function(a){if(!this._hasResource(a))return null;var b=this._getItem(this.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY+a),c=this._getItem(this.CONSTANTS.STORAGE.EXPIRATION_KEY+a),d=this.config.expireOffsetSeconds||120;return c&&c>this._now()+d?b:(this._saveItem(this.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY+a,""),this._saveItem(this.CONSTANTS.STORAGE.EXPIRATION_KEY+a,0),null)},AuthenticationContext.prototype.getCachedUser=function(){if(this._user)return this._user;var a=this._getItem(this.CONSTANTS.STORAGE.IDTOKEN);return this._user=this._createUser(a),this._user},AuthenticationContext.prototype.registerCallback=function(a,b,c){this._activeRenewals[b]=a,window.callBacksMappedToRenewStates[a]||(window.callBacksMappedToRenewStates[a]=[]);var d=this;window.callBacksMappedToRenewStates[a].push(c),window.callBackMappedToRenewStates[a]||(window.callBackMappedToRenewStates[a]=function(c,e){for(var f=0;f-1)){var b=this._user.profile.upn.split("@");a+="&domain_hint="+encodeURIComponent(b[b.length-1])}return a},AuthenticationContext.prototype._createUser=function(a){var b=null,c=this._extractIdToken(a);return c&&c.hasOwnProperty("aud")&&(c.aud.toLowerCase()===this.config.clientId.toLowerCase()?(b={userName:"",profile:c},c.hasOwnProperty("upn")?b.userName=c.upn:c.hasOwnProperty("email")&&(b.userName=c.email)):this.warn("IdToken has invalid aud field")),b},AuthenticationContext.prototype._getHash=function(a){return a.indexOf("#/")>-1?a=a.substring(a.indexOf("#/")+2):a.indexOf("#")>-1&&(a=a.substring(1)),a},AuthenticationContext.prototype.isCallback=function(a){a=this._getHash(a);var b=this._deserialize(a);return b.hasOwnProperty(this.CONSTANTS.ERROR_DESCRIPTION)||b.hasOwnProperty(this.CONSTANTS.ACCESS_TOKEN)||b.hasOwnProperty(this.CONSTANTS.ID_TOKEN)},AuthenticationContext.prototype.getLoginError=function(){return this._getItem(this.CONSTANTS.STORAGE.LOGIN_ERROR)},AuthenticationContext.prototype.getRequestInfo=function(a){a=this._getHash(a);var b=this._deserialize(a),c={valid:!1,parameters:{},stateMatch:!1,stateResponse:"",requestType:this.REQUEST_TYPE.UNKNOWN};if(b&&(c.parameters=b,b.hasOwnProperty(this.CONSTANTS.ERROR_DESCRIPTION)||b.hasOwnProperty(this.CONSTANTS.ACCESS_TOKEN)||b.hasOwnProperty(this.CONSTANTS.ID_TOKEN))){c.valid=!0;var d="";if(!b.hasOwnProperty("state"))return this.warn("No state returned"),c;if(this.verbose("State: "+b.state),d=b.state,c.stateResponse=d,d===this._getItem(this.CONSTANTS.STORAGE.STATE_LOGIN))return c.requestType=this.REQUEST_TYPE.LOGIN,c.stateMatch=!0,c;if(!c.stateMatch&&window.parent&&window.parent.AuthenticationContext())for(var e=window.parent.AuthenticationContext()._renewStates,f=0;f-1&&b+1-1)return this.config.endpoints[b];if(!(a.indexOf("http://")>-1||a.indexOf("https://")>-1)){if(this.config&&this.config.anonymousEndpoints)for(var c=0;c-1)return null;return this.config.loginResource}return this._getHostFromUri(a)===this._getHostFromUri(this.config.redirectUri)?this.config.loginResource:null},AuthenticationContext.prototype._getHostFromUri=function(a){var b=String(a).replace(/^(https?:)\/\//,"");return b=b.split("/")[0]},AuthenticationContext.prototype.handleWindowCallback=function(a){if(null==a&&(a=window.location.hash),this.isCallback(a)){var b=this.getRequestInfo(a);this.info("Returned from redirect url"),this.saveTokenFromHash(b);var c=null;if(b.requestType===this.REQUEST_TYPE.RENEW_TOKEN&&window.parent&&window.parent!==window)return this.verbose("Window is in iframe"),c=window.parent.callBackMappedToRenewStates[b.stateResponse],void(c&&c(this._getItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION),b.parameters[this.CONSTANTS.ACCESS_TOKEN]||b.parameters[this.CONSTANTS.ID_TOKEN]));b.requestType===this.REQUEST_TYPE.LOGIN&&(c=this.callback,c&&c(this._getItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION),b.parameters[this.CONSTANTS.ID_TOKEN])),this.popUp||(window.location=this._getItem(this.CONSTANTS.STORAGE.LOGIN_REQUEST))}},AuthenticationContext.prototype._getNavigateUrl=function(a,b){var c="common";this.config.tenant&&(c=this.config.tenant);var d=this.instance+c+"/oauth2/authorize"+this._serialize(a,this.config,b)+this._addLibMetadata();return this.info("Navigate url:"+d),d},AuthenticationContext.prototype._extractIdToken=function(a){var b=this._decodeJwt(a);if(!b)return null;try{var c=b.JWSPayload,d=this._base64DecodeStringUrlSafe(c);return d?JSON.parse(d):(this.info("The returned id_token could not be base64 url safe decoded."),null)}catch(a){this.error("The returned id_token could not be decoded",a)}return null},AuthenticationContext.prototype._base64DecodeStringUrlSafe=function(a){return a=a.replace(/-/g,"+").replace(/_/g,"/"),window.atob?decodeURIComponent(escape(window.atob(a))):decodeURIComponent(escape(this._decode(a)))},AuthenticationContext.prototype._decode=function(a){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";a=String(a).replace(/=+$/,"");var c=a.length;if(c%4===1)throw new Error("The token to be decoded is not correctly encoded.");for(var d,e,f,g,h,i,j,k,l="",m=0;m>16&255,j=h>>8&255,l+=String.fromCharCode(i,j);break}if(m+1===c-1){h=d<<18|e<<12,i=h>>16&255,l+=String.fromCharCode(i);break}h=d<<18|e<<12|f<<6|g,i=h>>16&255,j=h>>8&255,k=255&h,l+=String.fromCharCode(i,j,k)}return l},AuthenticationContext.prototype._decodeJwt=function(a){if(this._isEmpty(a))return null;var b=/^([^\.\s]*)\.([^\.\s]+)\.([^\.\s]*)$/,c=b.exec(a);if(!c||c.length<4)return this.warn("The returned id_token is not parseable."),null;var d={header:c[1],JWSPayload:c[2],JWSSig:c[3]};return d},AuthenticationContext.prototype._convertUrlSafeToRegularBase64EncodedString=function(a){return a.replace("-","+").replace("_","/")},AuthenticationContext.prototype._serialize=function(a,b,c){var d=[];if(null!==b){d.push("?response_type="+a),d.push("client_id="+encodeURIComponent(b.clientId)),c&&d.push("resource="+encodeURIComponent(c)),d.push("redirect_uri="+encodeURIComponent(b.redirectUri)),d.push("state="+encodeURIComponent(b.state)),b.hasOwnProperty("slice")&&d.push("slice="+encodeURIComponent(b.slice)),b.hasOwnProperty("extraQueryParameter")&&d.push(b.extraQueryParameter);var e=b.correlationId?b.correlationId:this._guid();d.push("client-request-id="+encodeURIComponent(e))}return d.join("&")},AuthenticationContext.prototype._deserialize=function(a){var b,c=/\+/g,d=/([^&=]+)=([^&]*)/g,e=function(a){return decodeURIComponent(a.replace(c," "))},f={};for(b=d.exec(a);b;)f[e(b[1])]=e(b[2]),b=d.exec(a);return f},AuthenticationContext.prototype._guid=function(){function a(a){for(var b=a.toString(16);b.length<2;)b="0"+b;return b}var b=window.crypto||window.msCrypto;if(b&&b.getRandomValues){var c=new Uint8Array(16);return b.getRandomValues(c),c[6]|=64,c[6]&=79,c[8]|=128,c[8]&=191,a(c[0])+a(c[1])+a(c[2])+a(c[3])+"-"+a(c[4])+a(c[5])+"-"+a(c[6])+a(c[7])+"-"+a(c[8])+a(c[9])+"-"+a(c[10])+a(c[11])+a(c[12])+a(c[13])+a(c[14])+a(c[15])}for(var d="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx",e="0123456789abcdef",f=0,g="",h=0;h<36;h++)"-"!==d[h]&&"4"!==d[h]&&(f=16*Math.random()|0),"x"===d[h]?g+=e[f]:"y"===d[h]?(f&=3,f|=8,g+=e[f]):g+=d[h];return g},AuthenticationContext.prototype._expiresIn=function(a){return this._now()+parseInt(a,10)},AuthenticationContext.prototype._now=function(){return Math.round((new Date).getTime()/1e3)},AuthenticationContext.prototype._addAdalFrame=function(a){if("undefined"!=typeof a){this.info("Add adal frame to document:"+a);var b=document.getElementById(a);if(!b){if(document.createElement&&document.documentElement&&(window.opera||window.navigator.userAgent.indexOf("MSIE 5.0")===-1)){var c=document.createElement("iframe");c.setAttribute("id",a),c.style.visibility="hidden",c.style.position="absolute",c.style.width=c.style.height=c.borderWidth="0px",b=document.getElementsByTagName("body")[0].appendChild(c)}else document.body&&document.body.insertAdjacentHTML&&document.body.insertAdjacentHTML("beforeEnd",'');window.frames&&window.frames[a]&&(b=window.frames[a])}return b}},AuthenticationContext.prototype._saveItem=function(a,b){return this.config&&this.config.cacheLocation&&"localStorage"===this.config.cacheLocation?this._supportsLocalStorage()?(localStorage.setItem(a,b),!0):(this.info("Local storage is not supported"),!1):this._supportsSessionStorage()?(sessionStorage.setItem(a,b),!0):(this.info("Session storage is not supported"),!1)},AuthenticationContext.prototype._getItem=function(a){return this.config&&this.config.cacheLocation&&"localStorage"===this.config.cacheLocation?this._supportsLocalStorage()?localStorage.getItem(a):(this.info("Local storage is not supported"),null):this._supportsSessionStorage()?sessionStorage.getItem(a):(this.info("Session storage is not supported"),null)},AuthenticationContext.prototype._supportsLocalStorage=function(){try{return"localStorage"in window&&window.localStorage}catch(a){return!1}},AuthenticationContext.prototype._supportsSessionStorage=function(){try{return"sessionStorage"in window&&window.sessionStorage}catch(a){return!1}},AuthenticationContext.prototype._cloneConfig=function(a){if(null===a||"object"!=typeof a)return a;var b={};for(var c in a)a.hasOwnProperty(c)&&(b[c]=a[c]);return b},AuthenticationContext.prototype._addLibMetadata=function(){return"&x-client-SKU=Js&x-client-Ver="+this._libVersion()},AuthenticationContext.prototype.log=function(a,b,c){if(a<=Logging.level){var d=(new Date).toUTCString(),e="";e=this.config.correlationId?d+":"+this.config.correlationId+"-"+this._libVersion()+"-"+this.CONSTANTS.LEVEL_STRING_MAP[a]+" "+b:d+":"+this._libVersion()+"-"+this.CONSTANTS.LEVEL_STRING_MAP[a]+" "+b,c&&(e+="\nstack:\n"+c.stack),Logging.log(e)}},AuthenticationContext.prototype.error=function(a,b){this.log(this.CONSTANTS.LOGGING_LEVEL.ERROR,a,b)},AuthenticationContext.prototype.warn=function(a){this.log(this.CONSTANTS.LOGGING_LEVEL.WARN,a,null)},AuthenticationContext.prototype.info=function(a){this.log(this.CONSTANTS.LOGGING_LEVEL.INFO,a,null)},AuthenticationContext.prototype.verbose=function(a){this.log(this.CONSTANTS.LOGGING_LEVEL.VERBOSE,a,null)},AuthenticationContext.prototype._libVersion=function(){return"1.0.12"},"undefined"!=typeof module&&module.exports&&(module.exports=AuthenticationContext,module.exports.inject=function(a){return new AuthenticationContext(a)}),AuthenticationContext}();
\ No newline at end of file
diff --git a/karma.conf.js b/karma.conf.js
index 1006a4e2..0feda166 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -13,8 +13,10 @@ module.exports = function(config) {
'bower_components/angular-mocks/angular-mocks.js',
'bower_components/angular-route/angular-route.js',
'bower_components/angular-resource/angular-resource.js',
+ 'bower_components/angular-ui-router/release/angular-ui-router.js',
'lib/*.js',
'tests/testApp.js',
+ 'tests/stateApp.js',
'tests/angularModuleSpec.js'
],
diff --git a/lib/adal-angular.js b/lib/adal-angular.js
index 20f6c0c3..a23f7f62 100644
--- a/lib/adal-angular.js
+++ b/lib/adal-angular.js
@@ -1,5 +1,5 @@
-//----------------------------------------------------------------------
-// AdalJS v1.0.11
+//----------------------------------------------------------------------
+// AdalJS v1.0.12
// @preserve Copyright (c) Microsoft Open Technologies, Inc.
// All Rights Reserved
// Apache License 2.0
@@ -55,6 +55,7 @@
}
configOptions.redirectUri = configOptions.redirectUri || pathDefault;
configOptions.postLogoutRedirectUri = configOptions.postLogoutRedirectUri || pathDefault;
+ configOptions.isAngular = true;
if (httpProvider && httpProvider.interceptors) {
httpProvider.interceptors.push('ProtectedResourceInterceptor');
@@ -72,7 +73,7 @@
// special function that exposes methods in Angular controller
// $rootScope, $window, $q, $location, $timeout are injected by Angular
- this.$get = ['$rootScope', '$window', '$q', '$location', '$timeout', function ($rootScope, $window, $q, $location, $timeout) {
+ this.$get = ['$rootScope', '$window', '$q', '$location', '$timeout','$injector', function ($rootScope, $window, $q, $location, $timeout,$injector) {
var locationChangeHandler = function (event, newUrl, oldUrl) {
_adal.verbose('Location change event from ' + oldUrl + ' to ' + newUrl);
@@ -84,34 +85,28 @@
var requestInfo = _adal.getRequestInfo(hash);
_adal.saveTokenFromHash(requestInfo);
- if (requestInfo.requestType !== _adal.REQUEST_TYPE.LOGIN) {
- _adal.callback = $window.parent.AuthenticationContext().callback;
- if (requestInfo.requestType === _adal.REQUEST_TYPE.RENEW_TOKEN) {
- _adal.callback = $window.parent.callBackMappedToRenewStates[requestInfo.stateResponse];
- }
- }
-
// Return to callback if it is sent from iframe
if (requestInfo.stateMatch) {
- if (typeof _adal.callback === 'function') {
+ if (requestInfo.requestType === _adal.REQUEST_TYPE.RENEW_TOKEN) {
+ var callback = $window.parent.callBackMappedToRenewStates[requestInfo.stateResponse];
// since this is a token renewal request in iFrame, we don't need to proceed with the location change.
event.preventDefault();
// Call within the same context without full page redirect keeps the callback
- if (requestInfo.requestType === _adal.REQUEST_TYPE.RENEW_TOKEN) {
+ if (callback && typeof callback === 'function') {
// id_token or access_token can be renewed
if (requestInfo.parameters['access_token']) {
- _adal.callback(_adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters['access_token']);
+ callback(_adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters['access_token']);
return;
} else if (requestInfo.parameters['id_token']) {
- _adal.callback(_adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters['id_token']);
+ callback(_adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters['id_token']);
return;
} else if (requestInfo.parameters['error']) {
- _adal.callback(_adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), null);
+ callback(_adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), null);
return;
}
}
- } else {
+ } else if (requestInfo.requestType === _adal.REQUEST_TYPE.LOGIN) {
// normal full login redirect happened on the page
updateDataFromCache(_adal.config.loginResource);
if (_oauthData.userName) {
@@ -121,21 +116,26 @@
$rootScope.userInfo = _oauthData;
}, 1);
- $rootScope.$broadcast('adal:loginSuccess');
+ $rootScope.$broadcast('adal:loginSuccess', _adal._getItem(_adal.CONSTANTS.STORAGE.IDTOKEN));
} else {
$rootScope.$broadcast('adal:loginFailure', _adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION));
}
+ if (_adal.callback && typeof _adal.callback === 'function')
+ _adal.callback(_adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), _adal._getItem(_adal.CONSTANTS.STORAGE.IDTOKEN));
+
+ event.preventDefault();
// redirect to login start page
- var loginStartPage = _adal._getItem(_adal.CONSTANTS.STORAGE.LOGIN_REQUEST);
- if (loginStartPage) {
- // prevent the current location change and redirect the user back to the login start page
- _adal.verbose('Redirecting to start page: ' + loginStartPage);
- event.preventDefault();
- if (!$location.$$html5 && loginStartPage.indexOf('#') > -1) {
- $location.url(loginStartPage.substring(loginStartPage.indexOf('#') + 1));
+ if (!_adal.popUp) {
+ var loginStartPage = _adal._getItem(_adal.CONSTANTS.STORAGE.LOGIN_REQUEST);
+ if (loginStartPage) {
+ // prevent the current location change and redirect the user back to the login start page
+ _adal.verbose('Redirecting to start page: ' + loginStartPage);
+ if (!$location.$$html5 && loginStartPage.indexOf('#') > -1) {
+ $location.url(loginStartPage.substring(loginStartPage.indexOf('#') + 1));
+ }
+ $window.location = loginStartPage;
}
- $window.location = loginStartPage;
}
}
}
@@ -197,6 +197,29 @@
return false;
}
+ function getStates(toState) {
+ var state = null;
+ var states = [];
+ if (toState.hasOwnProperty('parent')) {
+ state = toState;
+ while (state) {
+ states.unshift(state);
+ state = $injector.get('$state').get(state.parent);
+ }
+ }
+ else {
+ var stateNames = toState.name.split('.');
+ for (var i = 0, stateName = stateNames[0]; i < stateNames.length; i++) {
+ state = $injector.get('$state').get(stateName);
+ if (state) {
+ states.push(state);
+ }
+ stateName += '.' + stateNames[i + 1];
+ }
+ }
+ return states;
+ }
+
var routeChangeHandler = function (e, nextRoute) {
if (nextRoute && nextRoute.$$route) {
if (isADLoginRequired(nextRoute.$$route, _adal.config)) {
@@ -208,8 +231,15 @@
}
}
else {
- if (nextRoute.$$route.templateUrl && !isAnonymousEndpoint(nextRoute.$$route.templateUrl)) {
- _adal.config.anonymousEndpoints.push(nextRoute.$$route.templateUrl);
+ var nextRouteUrl;
+ if(typeof nextRoute.$$route.templateUrl === "function") {
+ nextRouteUrl = nextRoute.$$route.templateUrl(nextRoute.params);
+ } else {
+ nextRouteUrl = nextRoute.$$route.templateUrl;
+ }
+
+ if (nextRouteUrl && !isAnonymousEndpoint(nextRouteUrl)) {
+ _adal.config.anonymousEndpoints.push(nextRouteUrl);
}
}
}
@@ -217,17 +247,29 @@
var stateChangeHandler = function (e, toState, toParams, fromState, fromParams) {
if (toState) {
- if (isADLoginRequired(toState, _adal.config)) {
- if (!_oauthData.isAuthenticated) {
- if (!_adal._renewActive && !_adal.loginInProgress()) {
- _adal.info('State change event for:' + $location.$$url);
- loginHandler();
+ var states = getStates(toState);
+ var state = null;
+ for (var i = 0; i < states.length; i++) {
+ state = states[i];
+ if (isADLoginRequired(state, _adal.config)) {
+ if (!_oauthData.isAuthenticated) {
+ if (!_adal._renewActive && !_adal.loginInProgress()) {
+ _adal.info('State change event for:' + $location.$$url);
+ loginHandler();
+ }
}
}
- }
- else {
- if (toState.templateUrl && !isAnonymousEndpoint(toState.templateUrl)) {
- _adal.config.anonymousEndpoints.push(toState.templateUrl);
+ else if (state.templateUrl) {
+ var nextStateUrl;
+ if (typeof state.templateUrl === 'function'){
+ nextStateUrl = state.templateUrl(toParams);
+ }
+ else {
+ nextStateUrl = state.templateUrl;
+ }
+ if (nextStateUrl && !isAnonymousEndpoint(nextStateUrl)) {
+ _adal.config.anonymousEndpoints.push(nextStateUrl);
+ }
}
}
}
@@ -344,9 +386,23 @@
else {
// Cancel request if login is starting
if (authService.loginInProgress()) {
- authService.info('login is in progress.');
- config.data = 'login in progress, cancelling the request for ' + config.url;
- return $q.reject(config);
+ if (authService.config.popUp) {
+ authService.info('Url: ' + config.url + ' will be loaded after login is successful');
+ var delayedRequest = $q.defer();
+ $rootScope.$on('adal:loginSuccess', function (event, token) {
+ if (token) {
+ authService.info('Login completed, sending request for ' + config.url);
+ config.headers.Authorization = 'Bearer ' + tokenStored;
+ delayedRequest.resolve(config);
+ }
+ });
+ return delayedRequest.promise;
+ }
+ else {
+ authService.info('login is in progress.');
+ config.data = 'login in progress, cancelling the request for ' + config.url;
+ return $q.reject(config);
+ }
}
else {
// delayed request to return after iframe completes
diff --git a/lib/adal.js b/lib/adal.js
index f9db922a..083390d6 100644
--- a/lib/adal.js
+++ b/lib/adal.js
@@ -1,5 +1,5 @@
-//----------------------------------------------------------------------
-// AdalJS v1.0.11
+//----------------------------------------------------------------------
+// AdalJS v1.0.12
// @preserve Copyright (c) Microsoft Open Technologies, Inc.
// All Rights Reserved
// Apache License 2.0
@@ -72,7 +72,6 @@ var AuthenticationContext = (function () {
EXPIRATION_KEY: 'adal.expiration.key',
STATE_LOGIN: 'adal.state.login',
STATE_RENEW: 'adal.state.renew',
- STATE_RENEW_RESOURCE: 'adal.state.renew.resource',
NONCE_IDTOKEN: 'adal.nonce.idtoken',
SESSION_STATE: 'adal.session.state',
USERNAME: 'adal.username',
@@ -84,9 +83,6 @@ var AuthenticationContext = (function () {
RENEW_STATUS: 'adal.token.renew.status'
},
RESOURCE_DELIMETER: '|',
- ERR_MESSAGES: {
- NO_TOKEN: 'User is not authorized'
- },
LOADFRAME_TIMEOUT: '6000',
TOKEN_RENEW_STATUS_CANCELED: 'Canceled',
TOKEN_RENEW_STATUS_COMPLETED: 'Completed',
@@ -102,7 +98,9 @@ var AuthenticationContext = (function () {
1: 'WARNING:',
2: 'INFO:',
3: 'VERBOSE:'
- }
+ },
+ POPUP_WIDTH: 483,
+ POPUP_HEIGHT: 600
};
if (AuthenticationContext.prototype._singletonInstance) {
@@ -115,6 +113,7 @@ var AuthenticationContext = (function () {
this.config = {};
this.callback = null;
this.popUp = false;
+ this.isAngular = false;
// private
this._user = null;
@@ -136,6 +135,12 @@ var AuthenticationContext = (function () {
this.config = this._cloneConfig(config);
+ if (this.config.popUp)
+ this.popUp = true;
+
+ if (this.config.callback && typeof this.config.callback === 'function')
+ this.callback = this.config.callback;
+
if (this.config.instance) {
this.instance = this.config.instance;
}
@@ -152,6 +157,10 @@ var AuthenticationContext = (function () {
if (!this.config.anonymousEndpoints) {
this.config.anonymousEndpoints = [];
}
+
+ if (this.config.isAngular) {
+ this.isAngular = this.config.isAngular;
+ }
};
window.Logging = {
@@ -165,6 +174,10 @@ var AuthenticationContext = (function () {
*/
AuthenticationContext.prototype.login = function () {
// Token is not present and user needs to login
+ if (this._loginInProgress) {
+ this.info("Login in progress");
+ return;
+ }
var expectedState = this._guid();
this.config.state = expectedState;
this._idTokenNonce = this._guid();
@@ -175,18 +188,86 @@ var AuthenticationContext = (function () {
this._saveItem(this.CONSTANTS.STORAGE.NONCE_IDTOKEN, this._idTokenNonce);
this._saveItem(this.CONSTANTS.STORAGE.ERROR, '');
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, '');
-
-
var urlNavigate = this._getNavigateUrl('id_token', null) + '&nonce=' + encodeURIComponent(this._idTokenNonce);
- this.frameCallInProgress = false;
this._loginInProgress = true;
+ if (this.popUp) {
+ this._loginPopup(urlNavigate);
+ return;
+ }
if (this.config.displayCall) {
// User defined way of handling the navigation
this.config.displayCall(urlNavigate);
} else {
this.promptUser(urlNavigate);
}
- // callback from redirected page will receive fragment. It needs to call oauth2Callback
+ };
+
+ AuthenticationContext.prototype._openPopup = function (urlNavigate, title, popUpWidth, popUpHeight) {
+ try {
+ /**
+ * adding winLeft and winTop to account for dual monitor
+ * using screenLeft and screenTop for IE8 and earlier
+ */
+ var winLeft = window.screenLeft ? window.screenLeft : window.screenX;
+ var winTop = window.screenTop ? window.screenTop : window.screenY;
+ /**
+ * window.innerWidth displays browser window's height and width excluding toolbars
+ * using document.documentElement.clientWidth for IE8 and earlier
+ */
+ var width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
+ var height = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
+ var left = ((width / 2) - (popUpWidth / 2)) + winLeft;
+ var top = ((height / 2) - (popUpHeight / 2)) + winTop;
+
+ var popupWindow = window.open(urlNavigate, title, 'width=' + popUpWidth + ', height=' + popUpHeight + ', top=' + top + ', left=' + left);
+ if (popupWindow.focus) {
+ popupWindow.focus();
+ }
+ return popupWindow;
+ } catch (e) {
+ this.warn('Error opening popup, ' + e.message);
+ this._loginInProgress = false;
+ return null;
+ }
+ }
+
+ AuthenticationContext.prototype._loginPopup = function (urlNavigate) {
+ var popupWindow = this._openPopup(urlNavigate, "login", this.CONSTANTS.POPUP_WIDTH, this.CONSTANTS.POPUP_HEIGHT);
+ if (popupWindow == null) {
+ this.warn('Popup Window is null. This can happen if you are using IE');
+ this._saveItem(this.CONSTANTS.STORAGE.ERROR, 'Error opening popup');
+ this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, 'Popup Window is null. This can happen if you are using IE');
+ this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, 'Popup Window is null. This can happen if you are using IE');
+ if (this.callback)
+ this.callback(this._getItem(this.CONSTANTS.STORAGE.LOGIN_ERROR), null);
+ return;
+ }
+ if (this.config.redirectUri.indexOf('#') != -1)
+ var registeredRedirectUri = this.config.redirectUri.split("#")[0];
+ else
+ var registeredRedirectUri = this.config.redirectUri;
+ var that = this;
+ var pollTimer = window.setInterval(function () {
+ if (!popupWindow || popupWindow.closed || popupWindow.closed === undefined) {
+ that._loginInProgress = false;
+ window.clearInterval(pollTimer);
+ }
+ try {
+ if (popupWindow.location.href.indexOf(registeredRedirectUri) != -1) {
+ if (that.isAngular) {
+ window.location.hash = popupWindow.location.hash;
+ }
+ else {
+ that.handleWindowCallback(popupWindow.location.hash);
+ }
+ window.clearInterval(pollTimer);
+ that._loginInProgress = false;
+ that.info("Closing popup window");
+ popupWindow.close();
+ }
+ } catch (e) {
+ }
+ }, 20);
};
AuthenticationContext.prototype.loginInProgress = function () {
@@ -278,7 +359,6 @@ var AuthenticationContext = (function () {
var urlNavigate = this._getNavigateUrl('token', resource) + '&prompt=none';
urlNavigate = this._addHintParameters(urlNavigate);
- this.callback = callback;
this.registerCallback(expectedState, resource, callback);
this.verbose('Navigate to:' + urlNavigate);
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_REQUEST, '');
@@ -491,11 +571,9 @@ var AuthenticationContext = (function () {
throw new Error('callback is not a function');
}
- this.callback = callback;
-
// user in memory
if (this._user) {
- this.callback(null, this._user);
+ callback(null, this._user);
return;
}
@@ -504,10 +582,10 @@ var AuthenticationContext = (function () {
if (!this._isEmpty(idtoken)) {
this.info('User exists in cache: ');
this._user = this._createUser(idtoken);
- this.callback(null, this._user);
+ callback(null, this._user);
} else {
this.warn('User information is not available');
- this.callback('User information is not available');
+ callback('User information is not available');
}
};
@@ -790,10 +868,11 @@ var AuthenticationContext = (function () {
};
/*exported oauth2Callback */
- AuthenticationContext.prototype.handleWindowCallback = function () {
+ AuthenticationContext.prototype.handleWindowCallback = function (hash) {
// This is for regular javascript usage for redirect handling
// need to make sure this is for callback
- var hash = window.location.hash;
+ if (hash == null)
+ hash = window.location.hash;
if (this.isCallback(hash)) {
var requestInfo = this.getRequestInfo(hash);
this.info('Returned from redirect url');
@@ -806,11 +885,13 @@ var AuthenticationContext = (function () {
if (callback)
callback(this._getItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters[this.CONSTANTS.ACCESS_TOKEN] || requestInfo.parameters[this.CONSTANTS.ID_TOKEN]);
return;
- } else if (window && window.oauth2Callback) {
- this.verbose('Window is redirecting');
+ } else if (requestInfo.requestType === this.REQUEST_TYPE.LOGIN) {
callback = this.callback;
+ if (callback)
+ callback(this._getItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters[this.CONSTANTS.ID_TOKEN]);
}
- window.location = this._getItem(this.CONSTANTS.STORAGE.LOGIN_REQUEST);
+ if (!this.popUp)// No need to redirect user in case of popup
+ window.location = this._getItem(this.CONSTANTS.STORAGE.LOGIN_REQUEST);
}
};
@@ -1001,29 +1082,49 @@ var AuthenticationContext = (function () {
// Format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
// y could be 1000, 1001, 1010, 1011 since most significant two bits needs to be 10
// y values are 8, 9, A, B
- var guidHolder = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
- var hex = '0123456789abcdef';
- var r = 0;
- var guidResponse = "";
- for (var i = 0; i < 36; i++) {
- if (guidHolder[i] !== '-' && guidHolder[i] !== '4') {
- // each x and y needs to be random
- r = Math.random() * 16 | 0;
+ var cryptoObj = window.crypto || window.msCrypto; // for IE 11
+ if (cryptoObj && cryptoObj.getRandomValues) {
+ var buffer = new Uint8Array(16);
+ cryptoObj.getRandomValues(buffer);
+ //buffer[6] and buffer[7] represents the time_hi_and_version field. We will set the four most significant bits (4 through 7) of buffer[6] to represent decimal number 4 (UUID version number).
+ buffer[6] |= 0x40; //buffer[6] | 01000000 will set the 6 bit to 1.
+ buffer[6] &= 0x4f; //buffer[6] & 01001111 will set the 4, 5, and 7 bit to 0 such that bits 4-7 == 0100 = "4".
+ //buffer[8] represents the clock_seq_hi_and_reserved field. We will set the two most significant bits (6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively.
+ buffer[8] |= 0x80; //buffer[8] | 10000000 will set the 7 bit to 1.
+ buffer[8] &= 0xbf; //buffer[8] & 10111111 will set the 6 bit to 0.
+ function decimalToHex(num) {
+ var hex = num.toString(16);
+ while (hex.length < 2) {
+ hex = '0' + hex;
+ }
+ return hex;
}
-
- if (guidHolder[i] === 'x') {
- guidResponse += hex[r];
- } else if (guidHolder[i] === 'y') {
- // clock-seq-and-reserved first hex is filtered and remaining hex values are random
- r &= 0x3; // bit and with 0011 to set pos 2 to zero ?0??
- r |= 0x8; // set pos 3 to 1 as 1???
- guidResponse += hex[r];
- } else {
- guidResponse += guidHolder[i];
+ return decimalToHex(buffer[0]) + decimalToHex(buffer[1]) + decimalToHex(buffer[2]) + decimalToHex(buffer[3]) + '-' + decimalToHex(buffer[4]) + decimalToHex(buffer[5]) + '-' + decimalToHex(buffer[6]) + decimalToHex(buffer[7]) + '-' +
+ decimalToHex(buffer[8]) + decimalToHex(buffer[9]) + '-' + decimalToHex(buffer[10]) + decimalToHex(buffer[11]) + decimalToHex(buffer[12]) + decimalToHex(buffer[13]) + decimalToHex(buffer[14]) + decimalToHex(buffer[15]);
+ }
+ else {
+ var guidHolder = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
+ var hex = '0123456789abcdef';
+ var r = 0;
+ var guidResponse = "";
+ for (var i = 0; i < 36; i++) {
+ if (guidHolder[i] !== '-' && guidHolder[i] !== '4') {
+ // each x and y needs to be random
+ r = Math.random() * 16 | 0;
+ }
+ if (guidHolder[i] === 'x') {
+ guidResponse += hex[r];
+ } else if (guidHolder[i] === 'y') {
+ // clock-seq-and-reserved first hex is filtered and remaining hex values are random
+ r &= 0x3; // bit and with 0011 to set pos 2 to zero ?0??
+ r |= 0x8; // set pos 3 to 1 as 1???
+ guidResponse += hex[r];
+ } else {
+ guidResponse += guidHolder[i];
+ }
}
+ return guidResponse;
}
-
- return guidResponse;
};
/* jshint ignore:end */
@@ -1182,7 +1283,7 @@ var AuthenticationContext = (function () {
};
AuthenticationContext.prototype._libVersion = function () {
- return '1.0.11';
+ return '1.0.12';
};
if (typeof module !== 'undefined' && module.exports) {
@@ -1195,3 +1296,4 @@ var AuthenticationContext = (function () {
return AuthenticationContext;
}());
+
diff --git a/package.json b/package.json
index d8b615e3..7e61824e 100644
--- a/package.json
+++ b/package.json
@@ -10,7 +10,7 @@
"type": "git",
"url": "https://github.com/AzureAD/azure-activedirectory-library-for-js.git"
},
- "version": "1.0.11",
+ "version": "1.0.12",
"description": "Windows Azure Active Directory Client Library for js",
"keywords": [
"implicit",
diff --git a/tests/angularModuleSpec.js b/tests/angularModuleSpec.js
index 9204ac17..dd2456fb 100644
--- a/tests/angularModuleSpec.js
+++ b/tests/angularModuleSpec.js
@@ -397,3 +397,62 @@ describe('TaskCtl', function () {
expect(Logging.level).toEqual(2);
});
});
+
+describe('StateCtrl', function () {
+ var $httpBackend, adalServiceProvider, rootScope, $state, location, $templateCache, $stateParams;
+
+ //mock Application to allow us to inject our own dependencies
+ beforeEach(angular.mock.module('StateApplication'));
+
+ //mock the controller for the same reason and include $scope and $controller
+ beforeEach(angular.mock.inject(function (_adalAuthenticationService_, _$rootScope_, _$httpBackend_, _$state_, _$location_, _$templateCache_, _$stateParams_) {
+ adalServiceProvider = _adalAuthenticationService_;
+ rootScope = _$rootScope_;
+ $httpBackend = _$httpBackend_;
+ $state = _$state_;
+ location = _$location_;
+ $templateCache = _$templateCache_;
+ $stateParams = _$stateParams_;
+ $httpBackend.expectGET('settings.html').respond(200);
+ $httpBackend.expectGET('profile.html').respond(200);
+ $httpBackend.expectGET('name.html').respond(200);
+ $httpBackend.expectGET('account.html').respond(200);
+ $templateCache.put('profile.html', '');
+ $templateCache.put('settings.html', '');
+ $templateCache.put('account.html', '');
+ $templateCache.put('name.html', '');
+ adalServiceProvider.config.anonymousEndpoints = [];
+ }));
+
+ it('checks if anonymous endpoints are populated on statechange event if states are nested and separated by .', function () {
+ var state;
+ rootScope.$on('$stateChangeSuccess', function (event, toState) {
+ state = toState;
+ });
+ var urlNavigate = 'settings/profile/name';
+ location.url(urlNavigate);
+ rootScope.$digest();
+ expect(state.name).toEqual('settings.profile.name');
+ var states = urlNavigate.split('/');
+ for (var i = 0; i < states.length; i++) {
+ expect(adalServiceProvider.config.anonymousEndpoints[i]).toEqual(states[i] + '.html');
+ }
+ });
+
+ it('checks if state is resolved when templateUrl is a function which depends on stateParams and states have parent property', function () {
+ var state;
+ rootScope.$on('$stateChangeSuccess', function (event, toState) {
+ state = toState;
+ });
+ var urlNavigate = 'settings/account/Id/testId/name/Name/testName';
+ location.url(urlNavigate);
+ rootScope.$digest();
+ expect($stateParams.accountId).toEqual('testId');
+ expect($stateParams.accountName).toEqual('testName');
+ expect(state.name).toEqual('settings.account.name');
+ var states = state.name.split('.');
+ for (var i = 0; i < states.length ; i++) {
+ expect(adalServiceProvider.config.anonymousEndpoints[i]).toEqual(states[i] + '.html');
+ }
+ });
+});
diff --git a/tests/browser.manual.runner.html b/tests/browser.manual.runner.html
index 49c2f78a..91d5bc6a 100644
--- a/tests/browser.manual.runner.html
+++ b/tests/browser.manual.runner.html
@@ -8,11 +8,13 @@
+
+
diff --git a/tests/stateApp.js b/tests/stateApp.js
new file mode 100644
index 00000000..c593ded4
--- /dev/null
+++ b/tests/stateApp.js
@@ -0,0 +1,76 @@
+//----------------------------------------------------------------------
+// @preserve Copyright (c) Microsoft Open Technologies, Inc.
+// All Rights Reserved
+// Apache License 2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//----------------------------------------------------------------------
+
+'use strict';
+// Test app Ui-Router
+var stateApp = angular.module("StateApplication", ['ui.router', 'AdalAngular'])
+.config(['$stateProvider', '$urlRouterProvider', '$httpProvider', 'adalAuthenticationServiceProvider', function ($stateProvider, $urlRouterProvider, $httpProvider, adalProvider) {
+ $stateProvider
+ .state('settings', {
+ url: '/settings',
+ templateUrl: 'settings.html'
+ })
+ .state('settings.profile', {
+ url: '/profile',
+ templateUrl: 'profile.html',
+ })
+ .state('settings.profile.name', {
+ url: '/name',
+ templateUrl: 'name.html',
+ })
+ .state('settings.profile.email', {
+ url: '/email',
+ templateUrl: 'email.html',
+ })
+ .state('settings.account', {
+ parent: 'settings',
+ url: '/account/Id/:accountId',
+ templateUrl: function (stateParams) {
+ if (stateParams.accountId == 'testId')
+ return 'account.html';
+ },
+ })
+ .state('settings.account.name', {
+ parent: 'settings.account',
+ url: '/name/Name/:accountName',
+ templateUrl: function (stateParams) {
+ if (stateParams.accountName == 'testName')
+ return 'name.html';
+ }
+ })
+ .state('settings.account.email', {
+ url: '/email',
+ templateUrl: 'email.html',
+ });
+
+ $urlRouterProvider.otherwise('/settings');
+ var endpoints = {};
+
+ adalProvider.init(
+ {
+ tenant: 'tenantid123',
+ clientId: 'clientid123',
+ loginResource: 'loginResource123',
+ redirectUri: 'https://myapp.com/page',
+ endpoints: endpoints // optional
+ },
+ $httpProvider // pass http provider to inject request interceptor to attach tokens
+ );
+}]);
+
+app.controller('StateCtrl', ['$scope', '$location', 'adalAuthenticationService', function ($scope, $location, adalAuthenticationService) { }]);
diff --git a/tests/unit/spec/AdalSpec.js b/tests/unit/spec/AdalSpec.js
index fefdcd89..bf4e7a99 100644
--- a/tests/unit/spec/AdalSpec.js
+++ b/tests/unit/spec/AdalSpec.js
@@ -36,7 +36,9 @@ describe('Adal', function () {
},
localStorage: {},
sessionStorage: {},
- atob: atobHelper
+ atob: atobHelper,
+ innerWidth: 100,
+ innerHeight: 100
};
var mathMock = {
random: function () {
@@ -69,7 +71,7 @@ describe('Adal', function () {
var SECONDS_TO_EXPIRE = 3600;
var DEFAULT_INSTANCE = "https://login.microsoftonline.com/";
var IDTOKEN_MOCK = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjVUa0d0S1JrZ2FpZXpFWTJFc0xDMmdPTGpBNCJ9.eyJhdWQiOiJlOWE1YThiNi04YWY3LTQ3MTktOTgyMS0wZGVlZjI1NWY2OGUiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLXBwZS5uZXQvNTJkNGIwNzItOTQ3MC00OWZiLTg3MjEtYmMzYTFjOTkxMmExLyIsImlhdCI6MTQxMTk1OTAwMCwibmJmIjoxNDExOTU5MDAwLCJleHAiOjE0MTE5NjI5MDAsInZlciI6IjEuMCIsInRpZCI6IjUyZDRiMDcyLTk0NzAtNDlmYi04NzIxLWJjM2ExYzk5MTJhMSIsImFtciI6WyJwd2QiXSwib2lkIjoiZmEzYzVmYTctN2Q5OC00Zjk3LWJmYzQtZGJkM2E0YTAyNDMxIiwidXBuIjoidXNlckBvYXV0aGltcGxpY2l0LmNjc2N0cC5uZXQiLCJ1bmlxdWVfbmFtZSI6InVzZXJAb2F1dGhpbXBsaWNpdC5jY3NjdHAubmV0Iiwic3ViIjoiWTdUbXhFY09IUzI0NGFHa3RjbWpicnNrdk5tU1I4WHo5XzZmbVc2NXloZyIsImZhbWlseV9uYW1lIjoiYSIsImdpdmVuX25hbWUiOiJ1c2VyIiwibm9uY2UiOiI4MGZmYTkwYS1jYjc0LTRkMGYtYTRhYy1hZTFmOTNlMzJmZTAiLCJwd2RfZXhwIjoiNTc3OTkxMCIsInB3ZF91cmwiOiJodHRwczovL3BvcnRhbC5taWNyb3NvZnRvbmxpbmUuY29tL0NoYW5nZVBhc3N3b3JkLmFzcHgifQ.WHsl8TH1rQ3dQbRkV0TS6GBVAxzNOpG3nGG6mpEBCwAOCbyW6qRsSoo4qq8I5IGyerDf2cvcS-zzatHEROpRC9dcpwkRm6ta5dFZuouFyZ_QiYVKSMwfzEC_FI-6p7eT8gY6FbV51bp-Ah_WKJqEmaXv-lqjIpgsMGeWDgZRlB9cPODXosBq-PEk0q27Be-_A-KefQacJuWTX2eEhECLyuAu-ETVJb7s19jQrs_LJXz_ISib4DdTKPa7XTBDJlVGdCI18ctB67XwGmGi8MevkeKqFI8dkykTxeJ0MXMmEQbE6Fw-gxmP7uJYbZ61Jqwsw24zMDMeXatk2VWMBPCuhA';
- var STATE = '585bd348-4d52-4689-b9c7-d8480564f368';
+ var STATE = '33333333-3333-4333-b333-333333333333';
var SESSION_STATE = '451c6916-27cf-4eae-81cd-accf96126398';
var VALID_URLFRAGMENT = 'id_token=' + IDTOKEN_MOCK + '' + '&state=' + STATE + '&session_state=' + SESSION_STATE;
var INVALID_URLFRAGMENT = 'id_token' + IDTOKEN_MOCK + '' + '&state=' + STATE + '&session_state=' + SESSION_STATE;
@@ -106,7 +108,6 @@ describe('Adal', function () {
window.localStorage = storageFake;
window.sessionStorage = storageFake;
-
// Init adal
global.window = window;
@@ -182,7 +183,7 @@ describe('Adal', function () {
it('calls displaycall if given for login', function () {
storageFake.setItem(adal.CONSTANTS.STORAGE.USERNAME, 'test user');
-
+ adal._loginInProgress = false;
adal.config.clientId = 'client';
adal.config.redirectUri = 'contoso_site';
var urlToGo = '';
@@ -235,9 +236,9 @@ describe('Adal', function () {
token = valToken;
};
adal._renewStates = [];
- adal._user = { profile: { 'upn': 'test@testuser.com' }, userName: 'test@domain.com'};
+ adal._user = { profile: { 'upn': 'test@testuser.com' }, userName: 'test@domain.com' };
adal.acquireToken(RESOURCE1, callback);
- expect(adal.callback).toBe(callback);
+ expect(adal.callback).toBe(null);
expect(storageFake.getItem(adal.CONSTANTS.STORAGE.LOGIN_REQUEST)).toBe('');
expect(adal._renewStates.length).toBe(1);
// Wait for initial timeout load
@@ -718,12 +719,13 @@ describe('Adal', function () {
runs(function () {
expect(mockFrames['adalIdTokenFrame'].src).toBe(DEFAULT_INSTANCE + conf.tenant + '/oauth2/authorize?response_type=id_token&client_id=' + adal.config.clientId + '&redirect_uri=contoso_site&state=33333333-3333-4333-b333-333333333333%7Cclient'
- + '&client-request-id=33333333-3333-4333-b333-333333333333' + adal._addLibMetadata() + '&prompt=none&login_hint=test%40testuser.com&domain_hint=testuser.com' + '&nonce=33333333-3333-4333-b333-333333333333');
+ + '&client-request-id=33333333-3333-4333-b333-333333333333' + adal._addLibMetadata() + '&prompt=none&login_hint=test%40testuser.com&domain_hint=testuser.com' + '&nonce=33333333-3333-4333-b333-333333333333');
});
});
it('tests handleWindowCallback function for RENEW_TOKEN', function () {
window.location.hash = '#/id_token=' + IDTOKEN_MOCK;
+ var _getRequestInfo = adal.getRequestInfo;
adal.getRequestInfo = function (hash) {
return {
valid: true,
@@ -745,12 +747,14 @@ describe('Adal', function () {
adal.handleWindowCallback();
expect(err).toBe('error description');
expect(token).toBe(IDTOKEN_MOCK);
+ adal.getRequestInfo = _getRequestInfo;
});
it('tests handleWindowCallback function for LOGIN_REQUEST', function () {
window.location = {};
window.location.hash = '#/id_token=' + IDTOKEN_MOCK;
+ var _getRequestInfo = adal.getRequestInfo;
adal.getRequestInfo = function () {
return {
valid: true,
@@ -764,6 +768,7 @@ describe('Adal', function () {
window.oauth2Callback = {};
adal.handleWindowCallback();
expect(window.location).toBe('www.test.com');
+ adal.getRequestInfo = _getRequestInfo;
});
@@ -830,20 +835,20 @@ describe('Adal', function () {
var deserialize = adal._deserialize;//save initial state of function
adal._deserialize = function (query) {
- var match,
- pl = /\+/g, // Regex for replacing addition symbol with a space
- search = /([^&=]+)=?([^&]*)/g,
- decode = function (s) {
- return decodeURIComponent(s.replace(pl, ' '));
- },
- obj = {};
- match = search.exec(query);
- while (match) {
+ var match,
+ pl = /\+/g, // Regex for replacing addition symbol with a space
+ search = /([^&=]+)=?([^&]*)/g,
+ decode = function (s) {
+ return decodeURIComponent(s.replace(pl, ' '));
+ },
+ obj = {};
+ match = search.exec(query);
+ while (match) {
obj[decode(match[1])] = decode(match[2]);
match = search.exec(query);
- }
+ }
- return obj;
+ return obj;
}
obj = adal._deserialize(INVALID_URLFRAGMENT);
expect(obj['id_token' + IDTOKEN_MOCK]).toBe('');//This additional property is parsed because of ? operator in regex
@@ -852,6 +857,105 @@ describe('Adal', function () {
expect(obj.session_state).toBe(SESSION_STATE);
adal._deserialize = deserialize;//reassign state to original function
});
+
+ it('tests if callback is called after login, if popup window is null', function () {
+ adal.popUp = true;
+ adal.config.clientId = 'client';
+ adal.config.redirectUri = 'contoso_site';
+ var err;
+ var token;
+ var callback = function (valErr, valToken) {
+ err = valErr;
+ token = valToken;
+ };
+ window.open = function () {
+ return null;
+ }
+ adal.callback = callback;
+ adal.login();
+ expect(err).toBe('Popup Window is null. This can happen if you are using IE');
+ expect(token).toBe(null);
+ expect(adal.loginInProgress()).toBe(false);
+ });
+
+ it('tests login functionality in case of popup window', function () {
+ var timercallback;
+ window.clearInterval = function () {
+ };
+ window.setInterval = function (method, timer) {
+ timercallback = method;
+ };
+ adal.popUp = true;
+ adal.config.clientId = 'client';
+ adal.config.redirectUri = 'contoso_site';
+ var popupWindow;
+ window.open = function () {
+ popupWindow = {
+ location: {
+ hash: VALID_URLFRAGMENT,
+ href: 'hrefcontoso_site'
+ },
+ closed: false,
+ close: function () {
+ this.closed = true;
+ }
+ };
+ return popupWindow;
+ };
+ var err;
+ var token;
+ var callback = function (valErr, valToken) {
+ err = valErr;
+ token = valToken;
+ };
+ adal.callback = callback;
+ mathMock.random = function () {
+ return 0.2;
+ };
+ adal.login();
+ waitsFor(function () {
+ timercallback();
+ storageFake.setItem(adal.CONSTANTS.STORAGE.LOGIN_REQUEST, 'home page');
+ return popupWindow.closed == true;
+ }, 'error closing popup window', 2000);
+
+ runs(function () {
+ expect(adal.loginInProgress()).toBe(false);
+ expect(token).toBe(IDTOKEN_MOCK);
+ expect(window.location.href).not.toBe('home page');
+ });
+
+ });
+
+ it('ensures that adal.callback is not overridden in calls to getUser', function () {
+ var _callback = adal.callback;
+ adal.callback = null;
+ var err = '';
+ var user = {};
+ var callback = function (valErr, valResult) {
+ err = valErr;
+ user = valResult;
+ };
+ adal._user = { profile: { 'upn': 'test@testuser.com' }, userName: 'test@domain.com' };
+ adal.getUser(callback);
+ expect(user).toBe(adal._user);
+ expect(adal.callback).toBe(null);
+ adal.callback = _callback;
+ });
+
+ it('tests _guid function if window.crypto is defined in the browser', function () {
+ var buffer = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
+ window.msCrypto = null;
+ window.crypto = {
+ getRandomValues: function (_buffer) {
+ for (var i = 0; i < _buffer.length; i++) {
+ _buffer[i] = buffer[i];
+ }
+ }
+ };
+ expect(adal._guid()).toBe('00010203-0405-4607-8809-0a0b0c0d0e0f');
+ window.crypto = null;
+ });
// TODO angular intercepptor
// TODO angular authenticationService
});