diff --git a/.gitignore b/.gitignore
index 0b016ab5..8de9a7af 100644
--- a/.gitignore
+++ b/.gitignore
@@ -90,4 +90,7 @@ bower_components
samples/owin/OwinSample/Scripts/adal.js
#webstorm
-.idea
\ No newline at end of file
+.idea
+
+#codecoverage
+coverage
\ No newline at end of file
diff --git a/README.md b/README.md
index 1665313f..be12f3d0 100644
--- a/README.md
+++ b/README.md
@@ -7,17 +7,21 @@ This library is optimized for working together with AngularJS.
## The Library
-This is a GA released version. The current version is **1.0.8**.
+This is a GA released version. The current version is **1.0.9**.
You have multiple ways of getting ADAL JS:
+Via NPM:
+
+ npm install adal-angular
+
Via CDN:
-
-
+
+
-CDN will be updated to latest version 1.0.8.
+CDN will be updated to latest version 1.0.9.
Via Bower:
diff --git a/bower.json b/bower.json
index fa29ceaa..7687ca65 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
{
"name": "adal-angular",
- "version": "1.0.8",
+ "version": "1.0.9",
"homepage": "https://github.com/AzureAD/azure-activedirectory-library-for-js",
"authors": [
"MSOpentech"
diff --git a/changelog.txt b/changelog.txt
index ab569256..87a977fc 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,7 @@
+Version 1.0.9
+==========================
+Adding events for state mismatch and error responses. Fix for token renewal for app's backend. Making library available on NPM
+
Version 1.0.8
==========================
Fix for persisting route parameters of protected state
diff --git a/dist/adal-angular.min.js b/dist/adal-angular.min.js
index 8257a5d7..5ff878b4 100644
--- a/dist/adal-angular.min.js
+++ b/dist/adal-angular.min.js
@@ -1,2 +1,2 @@
-/*! adal-angular v1.0.8 2016-02-22 */
-"use strict";"undefined"!=typeof module&&module.exports&&(module.exports.inject=function(a){return new AuthenticationContext(a)}),function(){if(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}var j=function(){var f=e.location.hash;if(a.isCallback(f)){var i=a.getRequestInfo(f);if(a.saveTokenFromHash(i),g.$$html5?e.location=e.location.origin+e.location.pathname:e.location.hash="",i.requestType!==a.REQUEST_TYPE.LOGIN&&(a.callback=e.parent.AuthenticationContext().callback,i.requestType===a.REQUEST_TYPE.RENEW_TOKEN&&(a.callback=e.parent.callBackMappedToRenewStates[i.stateResponse])),i.stateMatch)if("function"==typeof a.callback){if(i.requestType===a.REQUEST_TYPE.RENEW_TOKEN){if(i.parameters.access_token)return void a.callback(a._getItem(a.CONSTANTS.STORAGE.ERROR_DESCRIPTION),i.parameters.access_token);if(i.parameters.id_token)return void a.callback(a._getItem(a.CONSTANTS.STORAGE.ERROR_DESCRIPTION),i.parameters.id_token)}}else c(a.config.loginResource),b.userName?(h(function(){c(a.config.loginResource),d.userInfo=b;var e=a._getItem(a.CONSTANTS.STORAGE.START_PAGE);if(e){var f=a._getItem(a.CONSTANTS.STORAGE.START_PAGE_PARAMS);if(f){var h=JSON.parse(f);g.url(e).search(h)}else g.url(e)}},1),d.$broadcast("adal:loginSuccess")):d.$broadcast("adal:loginFailure",a._getItem(a.CONSTANTS.STORAGE.ERROR_DESCRIPTION))}else c(a.config.loginResource),a._renewActive||b.isAuthenticated||!b.userName||a._getItem(a.CONSTANTS.STORAGE.FAILED_RENEW)||a.acquireToken(a.config.loginResource,function(a,c){a?d.$broadcast("adal:loginFailure","auto renew failure"):c&&(b.isAuthenticated=!0)});h(function(){c(a.config.loginResource),d.userInfo=b},1)},k=function(){a.info("Login event for:"+g.$$url),a.config&&a.config.localLoginUrl?g.path(a.config.localLoginUrl):(a._saveItem(a.CONSTANTS.STORAGE.START_PAGE,g.$$url),a.info("Start login at:"+window.location.href),d.$broadcast("adal:loginRedirect"),a.login())},l=function(c,d){d&&d.$$route&&i(d.$$route,a.config)&&(b.isAuthenticated||(a.info("Route change event for:"+g.$$url),k()))},m=function(c,d,e,f,h){d&&i(d,a.config)&&(b.isAuthenticated||(g.$$url=d.url,a._saveItem(a.CONSTANTS.STORAGE.START_PAGE_PARAMS,JSON.stringify(e)),a.info("State change event for:"+g.$$url),k()))};return d.$on("$routeChangeStart",l),d.$on("$stateChangeStart",m),d.$on("$locationChangeStart",j),c(a.config.loginResource),d.userInfo=b,{config:a.config,login:function(){a.login()},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.acquireToken(b,function(d,e){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(null===d)return c;var e=a.getCachedToken(d),f=!1;if(e)return a.info("Token is avaliable for this url "+c.url),c.headers.Authorization="Bearer "+e,c;if(a.config)for(var g in a.config.endpoints)c.url.indexOf(g)>-1&&(f=!0);if(a.loginInProgress())return a.info("login already start."),b.reject();if(a.config&&f){var h=b.defer();return a.acquireToken(d).then(function(b){a.verbose("Token is avaliable"),c.headers.Authorization="Bearer "+b,h.resolve(c)},function(a){h.reject(a)}),h.promise}return c}},responseError:function(d){if(a.info("Getting error in the response"),d&&401===d.status){var e=a.getResourceForEndpoint(d.config.url);a.clearCacheForResource(e),c.$broadcast("adal:notAuthorized",d,e)}return b.reject(d)}}}])}else console.error("Angular.JS is not included")}();
\ No newline at end of file
+/*! adal-angular v1.0.9 2016-04-04 */
+"use strict";"undefined"!=typeof module&&module.exports&&(module.exports.inject=function(a){return new AuthenticationContext(a)}),function(){if(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}var j=function(){var f=e.location.hash;if(a.isCallback(f)){var i=a.getRequestInfo(f);if(a.saveTokenFromHash(i),g.$$html5?e.location=e.location.origin+e.location.pathname:e.location.hash="",i.requestType!==a.REQUEST_TYPE.LOGIN&&(a._renewActive=!1,a.callback=e.parent.AuthenticationContext().callback,i.requestType===a.REQUEST_TYPE.RENEW_TOKEN&&(a.callback=e.parent.callBackMappedToRenewStates[i.stateResponse])),i.stateMatch)if("function"==typeof a.callback){if(i.requestType===a.REQUEST_TYPE.RENEW_TOKEN){if(i.parameters.access_token)return void a.callback(a._getItem(a.CONSTANTS.STORAGE.ERROR_DESCRIPTION),i.parameters.access_token);if(i.parameters.id_token)return void a.callback(a._getItem(a.CONSTANTS.STORAGE.ERROR_DESCRIPTION),i.parameters.id_token)}}else c(a.config.loginResource),b.userName?(h(function(){c(a.config.loginResource),d.userInfo=b;var e=a._getItem(a.CONSTANTS.STORAGE.START_PAGE);if(e){var f=a._getItem(a.CONSTANTS.STORAGE.START_PAGE_PARAMS);if(f){var h=JSON.parse(f);g.url(e).search(h)}else g.url(e)}},1),d.$broadcast("adal:loginSuccess")):d.$broadcast("adal:loginFailure",a._getItem(a.CONSTANTS.STORAGE.ERROR_DESCRIPTION));else d.$broadcast("adal:stateMismatch",a._getItem(a.CONSTANTS.STORAGE.ERROR_DESCRIPTION))}else c(a.config.loginResource),a._renewActive||b.isAuthenticated||!b.userName||(a._renewActive=!0,a.acquireToken(a.config.loginResource,function(a,c){a?d.$broadcast("adal:loginFailure","auto renew failure"):c&&(b.isAuthenticated=!0)}));h(function(){c(a.config.loginResource),d.userInfo=b},1)},k=function(){a.info("Login event for:"+g.$$url),a.config&&a.config.localLoginUrl?g.path(a.config.localLoginUrl):(a._saveItem(a.CONSTANTS.STORAGE.START_PAGE,g.$$url),a.info("Start login at:"+window.location.href),d.$broadcast("adal:loginRedirect"),a.login())},l=function(c,d){d&&d.$$route&&i(d.$$route,a.config)&&(b.isAuthenticated||a._renewActive||(a.info("Route change event for:"+g.$$url),k()))},m=function(c,d,e,f,h){d&&i(d,a.config)&&(b.isAuthenticated||a._renewActive||(g.$$url=d.url,a._saveItem(a.CONSTANTS.STORAGE.START_PAGE_PARAMS,JSON.stringify(e)),a.info("State change event for:"+g.$$url),k()))};return d.$on("$routeChangeStart",l),d.$on("$stateChangeStart",m),d.$on("$locationChangeStart",j),c(a.config.loginResource),d.userInfo=b,{config:a.config,login:function(){a.login()},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(null===d)return c;var e=a.getCachedToken(d);if(e)return a.info("Token is avaliable for this url "+c.url),c.headers.Authorization="Bearer "+e,c;if(a.loginInProgress())return a.info("login already start."),b.reject("login in progress, cancelling the request");var f=b.defer();return a.acquireToken(d).then(function(b){a.verbose("Token is avaliable"),c.headers.Authorization="Bearer "+b,f.resolve(c)},function(a){f.reject(a)}),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
diff --git a/dist/adal.min.js b/dist/adal.min.js
index b12bcd6d..28047928 100644
--- a/dist/adal.min.js
+++ b/dist/adal.min.js
@@ -1,2 +1,2 @@
-/*! adal-angular v1.0.8 2016-02-22 */
-"use strict";var Logging={level:0,log:function(){}},AuthenticationContext;"undefined"!=typeof module&&module.exports&&(module.exports.inject=function(a){return new AuthenticationContext(a)}),AuthenticationContext=function(a){if(this.REQUEST_TYPE={LOGIN:"LOGIN",RENEW_TOKEN:"RENEW_TOKEN",ID_TOKEN:"ID_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",START_PAGE:"adal.start.page",START_PAGE_PARAMS:"adal.start.page.params",FAILED_RENEW:"adal.failed.renew",STATE_LOGIN:"adal.state.login",STATE_RENEW:"adal.state.renew",STATE_RENEW_RESOURCE:"adal.state.renew.resource",STATE_IDTOKEN:"adal.state.idtoken",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"},RESOURCE_DELIMETER:"|",ERR_MESSAGES:{NO_TOKEN:"User is not authorized"},LOGGING_LEVEL:{ERROR:0,WARN:1,INFO:2,VERBOSE:3},LEVEL_STRING_MAP:{0:"ERROR:",1:"WARNING:",2:"INFO:",3:"VERBOSE:"}},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._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");a.correlationId||(a.correlationId=this._guid()),this.config=this._cloneConfig(a),this.config.loginResource||(this.config.loginResource=this.config.clientId),this.config.redirectUri||(this.config.redirectUri=window.location.href),this.config.resource=this.config.loginResource||""},AuthenticationContext.prototype.login=function(){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.FAILED_RENEW,""),this._saveItem(this.CONSTANTS.STORAGE.ERROR,""),this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION,"");var b=this._getNavigateUrl("id_token",null)+"&nonce="+encodeURIComponent(this._idTokenNonce);this.frameCallInProgress=!1,this._loginInProgress=!0,this.config.displayCall?this.config.displayCall(b):this.promptUser(b)},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 a=this._user.userName.split("@");return a[a.length-1]}return""},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="";switch(b.hasOwnProperty("state")?(this.verbose("State: "+b.state),d=b.state):this.verbose("No state returned"),c.stateResponse=d,d){case this._getItem(this.CONSTANTS.STORAGE.STATE_LOGIN):c.requestType=this.REQUEST_TYPE.LOGIN,c.stateMatch=!0;break;case this._getItem(this.CONSTANTS.STORAGE.STATE_IDTOKEN):c.requestType=this.REQUEST_TYPE.ID_TOKEN,this._saveItem(this.CONSTANTS.STORAGE.STATE_IDTOKEN,""),c.stateMatch=!0}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];return a.indexOf("http://")>-1||a.indexOf("https://")>-1?this._getHostFromUri(a)===this._getHostFromUri(this.config.redirectUri)?this.config.loginResource:null:this.config.loginResource},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&&b.requestType!==this.REQUEST_TYPE.ID_TOKEN||!window.parent?window&&window.oauth2Callback&&(this.verbose("Window is redirecting"),c=this.callback):(this.verbose("Window is in iframe"),c=window.parent.callBackMappedToRenewStates[b.stateResponse],window.src=""),window.location.hash="",window.location=this._getItem(this.CONSTANTS.STORAGE.LOGIN_REQUEST),b.requestType===this.REQUEST_TYPE.RENEW_TOKEN)return void c(this._getItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION),b.parameters[this.CONSTANTS.ACCESS_TOKEN]||b.parameters[this.CONSTANTS.ID_TOKEN]);if(b.requestType===this.REQUEST_TYPE.ID_TOKEN)return void c(this._getItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION),this._createUser(this._getItem(this.CONSTANTS.STORAGE.IDTOKEN)))}},AuthenticationContext.prototype._getNavigateUrl=function(a,b){var c="common";this.config.tenant&&(c=this.config.tenant),this.config.instance&&(this.instance=this.config.instance);var d=this.instance+c+"/oauth2/authorize"+this._serialize(a,this.config,b)+this._addClientId();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._extractUserName=function(a){try{var b=this._extractIdToken(a);if(b){if(b.hasOwnProperty("upn"))return b.upn;if(b.hasOwnProperty("email"))return b.email}}catch(c){this.error("The returned id_token could not be decoded",c)}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(null===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=[];return 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),b.correlationId&&d.push("client-request-id="+encodeURIComponent(b.correlationId))),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._addClientId=function(){return"&x-client-SKU=Js&x-client-Ver="+this._libVersion()},AuthenticationContext.prototype.log=function(a,b,c){if(a<=Logging.level){var d=this.config.correlationId,e=(new Date).toUTCString(),f=e+":"+d+"-"+this.CONSTANTS.LEVEL_STRING_MAP[a]+" "+b;c&&(f+="\nstack:\n"+c.stack),Logging.log(f)}},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.8"};
\ No newline at end of file
+/*! adal-angular v1.0.9 2016-04-04 */
+"use strict";var Logging={level:0,log:function(){}},AuthenticationContext;"undefined"!=typeof module&&module.exports&&(module.exports.inject=function(a){return new AuthenticationContext(a)}),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",START_PAGE:"adal.start.page",START_PAGE_PARAMS:"adal.start.page.params",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",IDTOKEN:"adal.idtoken",ERROR:"adal.error",ERROR_DESCRIPTION:"adal.error.description",LOGIN_REQUEST:"adal.login.request",LOGIN_ERROR:"adal.login.error"},RESOURCE_DELIMETER:"|",ERR_MESSAGES:{NO_TOKEN:"User is not authorized"},LOGGING_LEVEL:{ERROR:0,WARN:1,INFO:2,VERBOSE:3},LEVEL_STRING_MAP:{0:"ERROR:",1:"WARNING:",2:"INFO:",3:"VERBOSE:"}},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._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");a.correlationId||(a.correlationId=this._guid()),this.config=this._cloneConfig(a),this.config.loginResource||(this.config.loginResource=this.config.clientId),this.config.redirectUri||(this.config.redirectUri=window.location.href)},AuthenticationContext.prototype.login=function(){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);this.frameCallInProgress=!1,this._loginInProgress=!0,this.config.displayCall?this.config.displayCall(b):this.promptUser(b)},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 a=this._user.userName.split("@");return a[a.length-1]}return""},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];return a.indexOf("http://")>-1||a.indexOf("https://")>-1?this._getHostFromUri(a)===this._getHostFromUri(this.config.redirectUri)?this.config.loginResource:null:this.config.loginResource},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?(this.verbose("Window is in iframe"),c=window.parent.callBackMappedToRenewStates[b.stateResponse],window.src=""):window&&window.oauth2Callback&&(this.verbose("Window is redirecting"),c=this.callback),window.location.hash="",window.location=this._getItem(this.CONSTANTS.STORAGE.LOGIN_REQUEST),b.requestType===this.REQUEST_TYPE.RENEW_TOKEN)return void c(this._getItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION),b.parameters[this.CONSTANTS.ACCESS_TOKEN]||b.parameters[this.CONSTANTS.ID_TOKEN])}},AuthenticationContext.prototype._getNavigateUrl=function(a,b){var c="common";this.config.tenant&&(c=this.config.tenant),this.config.instance&&(this.instance=this.config.instance);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._extractUserName=function(a){try{var b=this._extractIdToken(a);if(b){if(b.hasOwnProperty("upn"))return b.upn;if(b.hasOwnProperty("email"))return b.email}}catch(c){this.error("The returned id_token could not be decoded",c)}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=[];return 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),b.correlationId&&d.push("client-request-id="+encodeURIComponent(b.correlationId))),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=this.config.correlationId,e=(new Date).toUTCString(),f=e+":"+d+"-"+this._libVersion()+"-"+this.CONSTANTS.LEVEL_STRING_MAP[a]+" "+b;c&&(f+="\nstack:\n"+c.stack),Logging.log(f)}},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.9"};
\ No newline at end of file
diff --git a/lib/adal-angular.js b/lib/adal-angular.js
index 4bb0b2e1..565da7e6 100644
--- a/lib/adal-angular.js
+++ b/lib/adal-angular.js
@@ -1,5 +1,5 @@
//----------------------------------------------------------------------
-// AdalJS v1.0.8
+// AdalJS v1.0.9
// @preserve Copyright (c) Microsoft Open Technologies, Inc.
// All Rights Reserved
// Apache License 2.0
@@ -89,6 +89,7 @@ if (typeof module !== 'undefined' && module.exports) {
}
if (requestInfo.requestType !== _adal.REQUEST_TYPE.LOGIN) {
+ _adal._renewActive = false;
_adal.callback = $window.parent.AuthenticationContext().callback;
if (requestInfo.requestType === _adal.REQUEST_TYPE.RENEW_TOKEN) {
_adal.callback = $window.parent.callBackMappedToRenewStates[requestInfo.stateResponse];
@@ -121,8 +122,7 @@ if (typeof module !== 'undefined' && module.exports) {
var loginStartPage = _adal._getItem(_adal.CONSTANTS.STORAGE.START_PAGE);
if (loginStartPage) {
// Check to see if any params were stored
- var paramsJSON = _adal._getItem(_adal.CONSTANTS.STORAGE.START_PAGE_PARAMS);
-
+ var paramsJSON = _adal._getItem(_adal.CONSTANTS.STORAGE.START_PAGE_PARAMS);
if (paramsJSON) {
// If params were stored redirect to the page and then
// initialize the params
@@ -139,23 +139,26 @@ if (typeof module !== 'undefined' && module.exports) {
}
}
}
+ else {
+ // state did not match, broadcast an error
+ $rootScope.$broadcast('adal:stateMismatch', _adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION));
+ }
} else {
// No callback. App resumes after closing or moving to new page.
// Check token and username
updateDataFromCache(_adal.config.loginResource);
if (!_adal._renewActive && !_oauthData.isAuthenticated && _oauthData.userName) {
- if (!_adal._getItem(_adal.CONSTANTS.STORAGE.FAILED_RENEW)) {
- // Idtoken is expired or not present
- _adal.acquireToken(_adal.config.loginResource, function (error, tokenOut) {
- if (error) {
- $rootScope.$broadcast('adal:loginFailure', 'auto renew failure');
- } else {
- if (tokenOut) {
- _oauthData.isAuthenticated = true;
- }
+ // Idtoken is expired or not present
+ _adal._renewActive = true;
+ _adal.acquireToken(_adal.config.loginResource, function (error, tokenOut) {
+ if (error) {
+ $rootScope.$broadcast('adal:loginFailure', 'auto renew failure');
+ } else {
+ if (tokenOut) {
+ _oauthData.isAuthenticated = true;
}
- });
- }
+ }
+ });
}
}
@@ -184,25 +187,25 @@ if (typeof module !== 'undefined' && module.exports) {
var routeChangeHandler = function (e, nextRoute) {
if (nextRoute && nextRoute.$$route && isADLoginRequired(nextRoute.$$route, _adal.config)) {
- if (!_oauthData.isAuthenticated) {
+ if (!_oauthData.isAuthenticated && !_adal._renewActive) {
_adal.info('Route change event for:' + $location.$$url);
loginHandler();
}
}
};
- var stateChangeHandler = function (e, toState, toParams, fromState, fromParams){
+ var stateChangeHandler = function (e, toState, toParams, fromState, fromParams) {
if (toState && isADLoginRequired(toState, _adal.config)) {
- if (!_oauthData.isAuthenticated) {
+ if (!_oauthData.isAuthenticated && !_adal._renewActive) {
// $location.$$url is set as the page we are coming from
// Update it so we can store the actual location we want to
// redirect to upon returning
$location.$$url = toState.url;
-
+
// Parameters are not stored in the url on stateChange so
// we store them
_adal._saveItem(_adal.CONSTANTS.STORAGE.START_PAGE_PARAMS, JSON.stringify(toParams));
-
+
_adal.info('State change event for:' + $location.$$url);
loginHandler();
}
@@ -239,7 +242,9 @@ if (typeof module !== 'undefined' && module.exports) {
acquireToken: function (resource) {
// automated token request call
var deferred = $q.defer();
+ _adal._renewActive = true;
_adal.acquireToken(resource, function (error, tokenOut) {
+ _adal._renewActive = false;
if (error) {
_adal.error('Error when acquiring token for resource: ' + resource, error);
deferred.reject(error);
@@ -271,11 +276,11 @@ if (typeof module !== 'undefined' && module.exports) {
},
clearCacheForResource: function (resource) {
_adal.clearCacheForResource(resource);
- },
- info: function(message) {
+ },
+ info: function (message) {
_adal.info(message);
- },
- verbose: function(message) {
+ },
+ verbose: function (message) {
_adal.verbose(message);
}
};
@@ -300,27 +305,17 @@ if (typeof module !== 'undefined' && module.exports) {
}
var tokenStored = authService.getCachedToken(resource);
- var isEndpoint = false;
if (tokenStored) {
authService.info('Token is avaliable for this url ' + config.url);
// check endpoint mapping if provided
config.headers.Authorization = 'Bearer ' + tokenStored;
return config;
} else {
-
- if (authService.config) {
- for (var endpointUrl in authService.config.endpoints) {
- if (config.url.indexOf(endpointUrl) > -1) {
- isEndpoint = true;
- }
- }
- }
-
// Cancel request if login is starting
if (authService.loginInProgress()) {
authService.info('login already start.');
- return $q.reject();
- } else if (authService.config && isEndpoint) {
+ return $q.reject('login in progress, cancelling the request');
+ } else {
// external endpoints
// delayed request to return after iframe completes
var delayedRequest = $q.defer();
@@ -328,9 +323,9 @@ if (typeof module !== 'undefined' && module.exports) {
authService.verbose('Token is avaliable');
config.headers.Authorization = 'Bearer ' + token;
delayedRequest.resolve(config);
- }, function (err) {
- delayedRequest.reject(err);
- });
+ }, function (err) {
+ delayedRequest.reject(err);
+ });
return delayedRequest.promise;
}
@@ -341,13 +336,17 @@ if (typeof module !== 'undefined' && module.exports) {
},
responseError: function (rejection) {
authService.info('Getting error in the response');
- if (rejection && rejection.status === 401) {
- var resource = authService.getResourceForEndpoint(rejection.config.url);
- authService.clearCacheForResource(resource);
- $rootScope.$broadcast('adal:notAuthorized', rejection, resource);
+ if (rejection) {
+ if (rejection.status === 401) {
+ var resource = authService.getResourceForEndpoint(rejection.config.url);
+ authService.clearCacheForResource(resource);
+ $rootScope.$broadcast('adal:notAuthorized', rejection, resource);
+ }
+ else {
+ $rootScope.$broadcast('adal:errorResponse', rejection);
+ }
+ return $q.reject(rejection);
}
-
- return $q.reject(rejection);
}
};
}]);
diff --git a/lib/adal.js b/lib/adal.js
index 25b79dd6..d02d88f0 100644
--- a/lib/adal.js
+++ b/lib/adal.js
@@ -1,5 +1,5 @@
//----------------------------------------------------------------------
-// AdalJS v1.0.8
+// AdalJS v1.0.9
// @preserve Copyright (c) Microsoft Open Technologies, Inc.
// All Rights Reserved
// Apache License 2.0
@@ -63,10 +63,9 @@ AuthenticationContext = function (config) {
this.REQUEST_TYPE = {
LOGIN: 'LOGIN',
RENEW_TOKEN: 'RENEW_TOKEN',
- ID_TOKEN: 'ID_TOKEN',
UNKNOWN: 'UNKNOWN'
};
-
+
/**
* Enum for storage constants
* @enum {string}
@@ -83,11 +82,9 @@ AuthenticationContext = function (config) {
EXPIRATION_KEY: 'adal.expiration.key',
START_PAGE: 'adal.start.page',
START_PAGE_PARAMS: 'adal.start.page.params',
- FAILED_RENEW: 'adal.failed.renew',
STATE_LOGIN: 'adal.state.login',
STATE_RENEW: 'adal.state.renew',
STATE_RENEW_RESOURCE: 'adal.state.renew.resource',
- STATE_IDTOKEN: 'adal.state.idtoken',
NONCE_IDTOKEN: 'adal.nonce.idtoken',
SESSION_STATE: 'adal.session.state',
USERNAME: 'adal.username',
@@ -114,18 +111,18 @@ AuthenticationContext = function (config) {
3: 'VERBOSE:'
}
};
-
+
if (AuthenticationContext.prototype._singletonInstance) {
return AuthenticationContext.prototype._singletonInstance;
}
AuthenticationContext.prototype._singletonInstance = this;
-
+
// public
this.instance = 'https://login.microsoftonline.com/';
this.config = {};
this.callback = null;
this.popUp = false;
-
+
// private
this._user = null;
this._activeRenewals = {};
@@ -139,27 +136,25 @@ AuthenticationContext = function (config) {
if (config.displayCall && typeof config.displayCall !== 'function') {
throw new Error('displayCall is not a function');
}
-
+
if (!config.clientId) {
throw new Error('clientId is required');
}
-
+
if (!config.correlationId) {
config.correlationId = this._guid();
}
-
+
this.config = this._cloneConfig(config);
-
+
// App can request idtoken for itself using clientid as resource
if (!this.config.loginResource) {
this.config.loginResource = this.config.clientId;
}
-
+
if (!this.config.redirectUri) {
this.config.redirectUri = window.location.href;
}
-
- this.config.resource = this.config.loginResource || '';
};
/**
@@ -176,11 +171,9 @@ AuthenticationContext.prototype.login = function () {
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, '');
this._saveItem(this.CONSTANTS.STORAGE.STATE_LOGIN, expectedState);
this._saveItem(this.CONSTANTS.STORAGE.NONCE_IDTOKEN, this._idTokenNonce);
- this._saveItem(this.CONSTANTS.STORAGE.FAILED_RENEW, '');
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;
@@ -211,13 +204,13 @@ AuthenticationContext.prototype.getCachedToken = function (resource) {
if (!this._hasResource(resource)) {
return null;
}
-
+
var token = this._getItem(this.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY + resource);
var expired = this._getItem(this.CONSTANTS.STORAGE.EXPIRATION_KEY + resource);
-
+
// If expiration is within offset, it will force renew
var offset = this.config.expireOffsetSeconds || 120;
-
+
if (expired && (expired > this._now() + offset)) {
return token;
} else {
@@ -235,7 +228,7 @@ AuthenticationContext.prototype.getCachedUser = function () {
if (this._user) {
return this._user;
}
-
+
var idtoken = this._getItem(this.CONSTANTS.STORAGE.IDTOKEN);
this._user = this._createUser(idtoken);
return this._user;
@@ -273,20 +266,13 @@ AuthenticationContext.prototype._renewToken = function (resource, callback) {
// use iframe to try refresh token
// use given resource to create new authz url
this.info('renewToken is called for resource:' + resource);
- if (!this._hasResource(resource)) {
- var keys = this._getItem(this.CONSTANTS.STORAGE.TOKEN_KEYS) || '';
- this._saveItem(this.CONSTANTS.STORAGE.TOKEN_KEYS, keys + resource + this.CONSTANTS.RESOURCE_DELIMETER);
- }
-
var frameHandle = this._addAdalFrame('adalRenewFrame' + resource);
var expectedState = this._guid() + '|' + resource;
this._idTokenNonce = this._guid();
this.config.state = expectedState;
// renew happens in iframe, so it keeps javascript context
this._renewStates.push(expectedState);
-
- this._saveItem(this.CONSTANTS.STORAGE.FAILED_RENEW, '');
-
+
this.verbose('Renew token Expected state: ' + expectedState);
var urlNavigate = this._getNavigateUrl('token', resource) + '&prompt=none&login_hint=' + encodeURIComponent(this._user.userName);
@@ -308,11 +294,6 @@ AuthenticationContext.prototype._renewToken = function (resource, callback) {
AuthenticationContext.prototype._renewIdToken = function (callback) {
// use iframe to try refresh token
this.info('renewIdToken is called');
- if (!this._hasResource(this.config.clientId)) {
- var keys = this._getItem(this.CONSTANTS.STORAGE.TOKEN_KEYS) || '';
- this._saveItem(this.CONSTANTS.STORAGE.TOKEN_KEYS, keys + this.config.clientId + this.CONSTANTS.RESOURCE_DELIMETER);
- }
-
var frameHandle = this._addAdalFrame('adalIdTokenFrame');
var expectedState = this._guid() + '|' + this.config.clientId;
this._idTokenNonce = this._guid();
@@ -320,9 +301,7 @@ AuthenticationContext.prototype._renewIdToken = function (callback) {
this.config.state = expectedState;
// renew happens in iframe, so it keeps javascript context
this._renewStates.push(expectedState);
- this._saveItem(this.CONSTANTS.STORAGE.STATE_RENEW, expectedState);
- this._saveItem(this.CONSTANTS.STORAGE.FAILED_RENEW, '');
-
+
this.verbose('Renew Idtoken Expected state: ' + expectedState);
var urlNavigate = this._getNavigateUrl('id_token', null) + '&prompt=none&login_hint=' + encodeURIComponent(this._user.userName);
@@ -372,26 +351,20 @@ AuthenticationContext.prototype.acquireToken = function (resource, callback) {
callback('resource is required', null);
return;
}
-
+
var token = this.getCachedToken(resource);
if (token) {
this.info('Token is already in cache for resource:' + resource);
callback(null, token);
return;
}
-
- if (this._getItem(this.CONSTANTS.STORAGE.FAILED_RENEW)) {
- this.info('renewToken is failed for resource ' + resource + ':' + this._getItem(this.CONSTANTS.STORAGE.FAILED_RENEW));
- callback(this._getItem(this.CONSTANTS.STORAGE.FAILED_RENEW), null);
- return;
- }
-
+
if (!this._user) {
this.warn('User login is required');
callback('User login is required', null);
return;
}
-
+
// refresh attept with iframe
//Already renewing for this resource, callback when we get the token.
if (this._activeRenewals[resource]) {
@@ -429,11 +402,9 @@ AuthenticationContext.prototype.promptUser = function (urlNavigate) {
AuthenticationContext.prototype.clearCache = function () {
this._saveItem(this.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY, '');
this._saveItem(this.CONSTANTS.STORAGE.EXPIRATION_KEY, 0);
- this._saveItem(this.CONSTANTS.STORAGE.FAILED_RENEW, '');
this._saveItem(this.CONSTANTS.STORAGE.SESSION_STATE, '');
this._saveItem(this.CONSTANTS.STORAGE.STATE_LOGIN, '');
this._renewStates = [];
- this._saveItem(this.CONSTANTS.STORAGE.STATE_IDTOKEN, '');
this._saveItem(this.CONSTANTS.STORAGE.START_PAGE, '');
this._saveItem(this.CONSTANTS.STORAGE.START_PAGE_PARAMS, '');
this._saveItem(this.CONSTANTS.STORAGE.USERNAME, '');
@@ -441,7 +412,7 @@ AuthenticationContext.prototype.clearCache = function () {
this._saveItem(this.CONSTANTS.STORAGE.ERROR, '');
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, '');
var keys = this._getItem(this.CONSTANTS.STORAGE.TOKEN_KEYS);
-
+
if (!this._isEmpty(keys)) {
keys = keys.split(this.CONSTANTS.RESOURCE_DELIMETER);
for (var i = 0; i < keys.length; i++) {
@@ -456,9 +427,7 @@ AuthenticationContext.prototype.clearCache = function () {
* Clear cache items for a resource.
*/
AuthenticationContext.prototype.clearCacheForResource = function (resource) {
- this._saveItem(this.CONSTANTS.STORAGE.FAILED_RENEW, '');
this._saveItem(this.CONSTANTS.STORAGE.STATE_RENEW, '');
- this._saveItem(this.CONSTANTS.STORAGE.STATE_IDTOKEN, '');
this._saveItem(this.CONSTANTS.STORAGE.ERROR, '');
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, '');
if (this._hasResource(resource)) {
@@ -479,15 +448,15 @@ AuthenticationContext.prototype.logOut = function () {
if (this.config.tenant) {
tenant = this.config.tenant;
}
-
+
if (this.config.instance) {
this.instance = this.config.instance;
}
-
+
if (this.config.postLogoutRedirectUri) {
logout = 'post_logout_redirect_uri=' + encodeURIComponent(this.config.postLogoutRedirectUri);
}
-
+
var urlNavigate = this.instance + tenant + '/oauth2/logout?' + logout;
this.info('Logout navigate to: ' + urlNavigate);
this.promptUser(urlNavigate);
@@ -513,15 +482,15 @@ AuthenticationContext.prototype.getUser = function (callback) {
if (typeof callback !== 'function') {
throw new Error('callback is not a function');
}
-
+
this.callback = callback;
-
+
// user in memory
if (this._user) {
this.callback(null, this._user);
return;
}
-
+
// frame is used to get idtoken
var idtoken = this._getItem(this.CONSTANTS.STORAGE.IDTOKEN);
if (!this._isEmpty(idtoken)) {
@@ -540,7 +509,7 @@ AuthenticationContext.prototype._getDomainHint = function () {
// local part can include @ in quotes. Sending last part handles that.
return parts[parts.length - 1];
}
-
+
return '';
};
@@ -548,14 +517,14 @@ AuthenticationContext.prototype._createUser = function (idToken) {
var user = null;
var parsedJson = this._extractIdToken(idToken);
if (parsedJson && parsedJson.hasOwnProperty('aud')) {
-
+
if (parsedJson.aud.toLowerCase() === this.config.clientId.toLowerCase()) {
-
+
user = {
userName: '',
profile: parsedJson
};
-
+
if (parsedJson.hasOwnProperty('upn')) {
user.userName = parsedJson.upn;
} else if (parsedJson.hasOwnProperty('email')) {
@@ -566,7 +535,7 @@ AuthenticationContext.prototype._createUser = function (idToken) {
}
}
-
+
return user;
};
@@ -576,7 +545,7 @@ AuthenticationContext.prototype._getHash = function (hash) {
} else if (hash.indexOf('#') > -1) {
hash = hash.substring(1);
}
-
+
return hash;
};
@@ -622,35 +591,29 @@ AuthenticationContext.prototype.getRequestInfo = function (hash) {
if (parameters.hasOwnProperty(this.CONSTANTS.ERROR_DESCRIPTION) ||
parameters.hasOwnProperty(this.CONSTANTS.ACCESS_TOKEN) ||
parameters.hasOwnProperty(this.CONSTANTS.ID_TOKEN)) {
-
+
requestInfo.valid = true;
-
+
// which call
var stateResponse = '';
if (parameters.hasOwnProperty('state')) {
this.verbose('State: ' + parameters.state);
stateResponse = parameters.state;
} else {
- this.verbose('No state returned');
+ this.warn('No state returned');
+ return requestInfo;
}
-
+
requestInfo.stateResponse = stateResponse;
-
+
// async calls can fire iframe and login request at the same time if developer does not use the API as expected
// incoming callback needs to be looked up to find the request type
- switch (stateResponse) {
- case this._getItem(this.CONSTANTS.STORAGE.STATE_LOGIN):
- requestInfo.requestType = this.REQUEST_TYPE.LOGIN;
- requestInfo.stateMatch = true;
- break;
-
- case this._getItem(this.CONSTANTS.STORAGE.STATE_IDTOKEN):
- requestInfo.requestType = this.REQUEST_TYPE.ID_TOKEN;
- this._saveItem(this.CONSTANTS.STORAGE.STATE_IDTOKEN, '');
- requestInfo.stateMatch = true;
- break;
+ if (stateResponse === this._getItem(this.CONSTANTS.STORAGE.STATE_LOGIN)) {
+ requestInfo.requestType = this.REQUEST_TYPE.LOGIN;
+ requestInfo.stateMatch = true;
+ return requestInfo;
}
-
+
// external api requests may have many renewtoken requests for different resource
if (!requestInfo.stateMatch && window.parent && window.parent.AuthenticationContext()) {
var statesInParentContext = window.parent.AuthenticationContext()._renewStates;
@@ -664,7 +627,7 @@ AuthenticationContext.prototype.getRequestInfo = function (hash) {
}
}
}
-
+
return requestInfo;
};
@@ -675,7 +638,7 @@ AuthenticationContext.prototype._getResourceFromState = function (state) {
return state.substring(splitIndex + 1);
}
}
-
+
return '';
};
@@ -688,20 +651,19 @@ AuthenticationContext.prototype.saveTokenFromHash = function (requestInfo) {
this.info('State status:' + requestInfo.stateMatch + '; Request type:' + requestInfo.requestType);
this._saveItem(this.CONSTANTS.STORAGE.ERROR, '');
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, '');
-
+
// Record error
if (requestInfo.parameters.hasOwnProperty(this.CONSTANTS.ERROR_DESCRIPTION)) {
this.info('Error :' + requestInfo.parameters.error + '; Error description:' + requestInfo.parameters[this.CONSTANTS.ERROR_DESCRIPTION]);
- this._saveItem(this.CONSTANTS.STORAGE.FAILED_RENEW, requestInfo.parameters[this.CONSTANTS.ERROR_DESCRIPTION]);
this._saveItem(this.CONSTANTS.STORAGE.ERROR, requestInfo.parameters.error);
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, requestInfo.parameters[this.CONSTANTS.ERROR_DESCRIPTION]);
-
+
if (requestInfo.requestType === this.REQUEST_TYPE.LOGIN) {
this._loginInProgress = false;
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, requestInfo.parameters.errorDescription);
}
} else {
-
+
// It must verify the state from redirect
if (requestInfo.stateMatch) {
// record tokens to storage if exists
@@ -709,28 +671,23 @@ AuthenticationContext.prototype.saveTokenFromHash = function (requestInfo) {
if (requestInfo.parameters.hasOwnProperty(this.CONSTANTS.SESSION_STATE)) {
this._saveItem(this.CONSTANTS.STORAGE.SESSION_STATE, requestInfo.parameters[this.CONSTANTS.SESSION_STATE]);
}
-
+
var keys, resource;
-
+
if (requestInfo.parameters.hasOwnProperty(this.CONSTANTS.ACCESS_TOKEN)) {
this.info('Fragment has access token');
- // default resource
- resource = this.config.loginResource;
+ resource = this._getResourceFromState(requestInfo.stateResponse);
if (!this._hasResource(resource)) {
keys = this._getItem(this.CONSTANTS.STORAGE.TOKEN_KEYS) || '';
this._saveItem(this.CONSTANTS.STORAGE.TOKEN_KEYS, keys + resource + this.CONSTANTS.RESOURCE_DELIMETER);
}
-
- if (requestInfo.requestType === this.REQUEST_TYPE.RENEW_TOKEN) {
- resource = this._getResourceFromState(requestInfo.stateResponse);
- }
-
// save token with related resource
this._saveItem(this.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY + resource, requestInfo.parameters[this.CONSTANTS.ACCESS_TOKEN]);
this._saveItem(this.CONSTANTS.STORAGE.EXPIRATION_KEY + resource, this._expiresIn(requestInfo.parameters[this.CONSTANTS.EXPIRES_IN]));
}
-
+
if (requestInfo.parameters.hasOwnProperty(this.CONSTANTS.ID_TOKEN)) {
+ this.info('Fragment has id token');
this._loginInProgress = false;
this._user = this._createUser(requestInfo.parameters[this.CONSTANTS.ID_TOKEN]);
if (this._user && this._user.profile) {
@@ -739,7 +696,7 @@ AuthenticationContext.prototype.saveTokenFromHash = function (requestInfo) {
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, 'Nonce is not same as ' + this._idTokenNonce);
} else {
this._saveItem(this.CONSTANTS.STORAGE.IDTOKEN, requestInfo.parameters[this.CONSTANTS.ID_TOKEN]);
-
+
// Save idtoken as access token for app itself
resource = this.config.loginResource ? this.config.loginResource : this.config.clientId;
if (!this._hasResource(resource)) {
@@ -753,10 +710,7 @@ AuthenticationContext.prototype.saveTokenFromHash = function (requestInfo) {
}
} else {
this._saveItem(this.CONSTANTS.STORAGE.ERROR, 'Invalid_state');
- this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, 'Invalid_state');
- if (requestInfo.requestType === this.REQUEST_TYPE.LOGIN) {
- this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, 'State is not same as ' + requestInfo.stateResponse);
- }
+ this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, 'Invalid_state. state: ' + requestInfo.stateResponse);
}
}
};
@@ -775,7 +729,7 @@ AuthenticationContext.prototype.getResourceForEndpoint = function (endpoint) {
}
}
}
-
+
// default resource will be clientid if nothing specified
// App will use idtoken for calls to itself
// check if it's staring from http or https, needs to match with app host
@@ -784,12 +738,12 @@ AuthenticationContext.prototype.getResourceForEndpoint = function (endpoint) {
return this.config.loginResource;
}
}
+ // in angular level, the url for $http interceptor call could be relative url,
+ // if it's relative call, we'll treat it as app backend call.
else {
- // in angular level, the url for $http interceptor call could be relative url,
- // if it's relative call, we'll treat it as app backend call.
return this.config.loginResource;
}
-
+
// if not the app's own backend or not a domain listed in the endpoints structure
return null;
};
@@ -812,9 +766,7 @@ AuthenticationContext.prototype.handleWindowCallback = function () {
this.info('Returned from redirect url');
this.saveTokenFromHash(requestInfo);
var callback = null;
- if ((requestInfo.requestType === this.REQUEST_TYPE.RENEW_TOKEN ||
- requestInfo.requestType === this.REQUEST_TYPE.ID_TOKEN) &&
- window.parent) {
+ if ((requestInfo.requestType === this.REQUEST_TYPE.RENEW_TOKEN && window.parent)) {
// iframe call but same single page
this.verbose('Window is in iframe');
callback = window.parent.callBackMappedToRenewStates[requestInfo.stateResponse];
@@ -823,16 +775,12 @@ AuthenticationContext.prototype.handleWindowCallback = function () {
this.verbose('Window is redirecting');
callback = this.callback;
}
-
+
window.location.hash = '';
window.location = this._getItem(this.CONSTANTS.STORAGE.LOGIN_REQUEST);
if (requestInfo.requestType === this.REQUEST_TYPE.RENEW_TOKEN) {
callback(this._getItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters[this.CONSTANTS.ACCESS_TOKEN] || requestInfo.parameters[this.CONSTANTS.ID_TOKEN]);
return;
- } else if (requestInfo.requestType === this.REQUEST_TYPE.ID_TOKEN) {
- // JS context may not have the user if callback page was different, so parse idtoken again to callback
- callback(this._getItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION), this._createUser(this._getItem(this.CONSTANTS.STORAGE.IDTOKEN)));
- return;
}
}
};
@@ -842,12 +790,12 @@ AuthenticationContext.prototype._getNavigateUrl = function (responseType, resour
if (this.config.tenant) {
tenant = this.config.tenant;
}
-
+
if (this.config.instance) {
this.instance = this.config.instance;
}
-
- var urlNavigate = this.instance + tenant + '/oauth2/authorize' + this._serialize(responseType, this.config, resource) + this._addClientId();
+
+ var urlNavigate = this.instance + tenant + '/oauth2/authorize' + this._serialize(responseType, this.config, resource) + this._addLibMetadata();
this.info('Navigate url:' + urlNavigate);
return urlNavigate;
};
@@ -858,7 +806,7 @@ AuthenticationContext.prototype._extractIdToken = function (encodedIdToken) {
if (!decodedToken) {
return null;
}
-
+
try {
var base64IdToken = decodedToken.JWSPayload;
var base64Decoded = this._base64DecodeStringUrlSafe(base64IdToken);
@@ -866,13 +814,13 @@ AuthenticationContext.prototype._extractIdToken = function (encodedIdToken) {
this.info('The returned id_token could not be base64 url safe decoded.');
return null;
}
-
+
// ECMA script has JSON built-in support
return JSON.parse(base64Decoded);
} catch (err) {
this.error('The returned id_token could not be decoded', err);
}
-
+
return null;
};
@@ -890,7 +838,7 @@ AuthenticationContext.prototype._extractUserName = function (encodedIdToken) {
} catch (err) {
this.error('The returned id_token could not be decoded', err);
}
-
+
return null;
};
@@ -914,7 +862,7 @@ AuthenticationContext.prototype._decode = function (base64IdToken) {
if (length % 4 === 1) {
throw new Error('The token to be decoded is not correctly encoded.');
}
-
+
var h1, h2, h3, h4, bits, c1, c2, c3, decoded = '';
for (var i = 0; i < length; i += 4) {
//Every 4 base64 encoded character will be converted to 3 byte string, which is 24 bits
@@ -923,7 +871,7 @@ AuthenticationContext.prototype._decode = function (base64IdToken) {
h2 = codes.indexOf(base64IdToken.charAt(i + 1));
h3 = codes.indexOf(base64IdToken.charAt(i + 2));
h4 = codes.indexOf(base64IdToken.charAt(i + 3));
-
+
// For padding, if last two are '='
if (i + 2 === length - 1) {
bits = h1 << 18 | h2 << 12 | h3 << 6;
@@ -939,40 +887,40 @@ AuthenticationContext.prototype._decode = function (base64IdToken) {
decoded += String.fromCharCode(c1);
break;
}
-
+
bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
-
+
// then convert to 3 byte chars
c1 = bits >> 16 & 255;
c2 = bits >> 8 & 255;
c3 = bits & 255;
-
+
decoded += String.fromCharCode(c1, c2, c3);
}
-
+
return decoded;
};
// Adal.node js crack function
AuthenticationContext.prototype._decodeJwt = function (jwtToken) {
- if (jwtToken === null) {
- return null;
+ if (this._isEmpty(jwtToken)) {
+ return null;
};
var idTokenPartsRegex = /^([^\.\s]*)\.([^\.\s]+)\.([^\.\s]*)$/;
-
+
var matches = idTokenPartsRegex.exec(jwtToken);
if (!matches || matches.length < 4) {
this.warn('The returned id_token is not parseable.');
return null;
}
-
+
var crackedToken = {
header: matches[1],
JWSPayload: matches[2],
JWSSig: matches[3]
};
-
+
return crackedToken;
};
@@ -988,23 +936,23 @@ AuthenticationContext.prototype._serialize = function (responseType, obj, resour
if (resource) {
str.push('resource=' + encodeURIComponent(resource));
}
-
+
str.push('redirect_uri=' + encodeURIComponent(obj.redirectUri));
str.push('state=' + encodeURIComponent(obj.state));
-
+
if (obj.hasOwnProperty('slice')) {
str.push('slice=' + encodeURIComponent(obj.slice));
}
-
+
if (obj.hasOwnProperty('extraQueryParameter')) {
str.push(obj.extraQueryParameter);
}
-
+
if (obj.correlationId) {
str.push('client-request-id=' + encodeURIComponent(obj.correlationId));
}
}
-
+
return str.join('&');
};
@@ -1021,7 +969,7 @@ AuthenticationContext.prototype._deserialize = function (query) {
obj[decode(match[1])] = decode(match[2]);
match = search.exec(query);
}
-
+
return obj;
};
@@ -1056,7 +1004,7 @@ AuthenticationContext.prototype._guid = function () {
// 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') {
@@ -1068,7 +1016,7 @@ AuthenticationContext.prototype._guid = function () {
guidResponse += guidHolder[i];
}
}
-
+
return guidResponse;
};
/* jshint ignore:end */
@@ -1086,10 +1034,10 @@ AuthenticationContext.prototype._addAdalFrame = function (iframeId) {
if (typeof iframeId === 'undefined') {
return;
}
-
+
this.info('Add adal frame to document:' + iframeId);
var adalFrame = document.getElementById(iframeId);
-
+
if (!adalFrame) {
if (document.createElement && document.documentElement &&
(window.opera || window.navigator.userAgent.indexOf('MSIE 5.0') === -1)) {
@@ -1098,7 +1046,7 @@ AuthenticationContext.prototype._addAdalFrame = function (iframeId) {
ifr.style.visibility = 'hidden';
ifr.style.position = 'absolute';
ifr.style.width = ifr.style.height = ifr.borderWidth = '0px';
-
+
adalFrame = document.getElementsByTagName('body')[0].appendChild(ifr);
}
else if (document.body && document.body.insertAdjacentHTML) {
@@ -1108,52 +1056,52 @@ AuthenticationContext.prototype._addAdalFrame = function (iframeId) {
adalFrame = window.frames[iframeId];
}
}
-
+
return adalFrame;
};
AuthenticationContext.prototype._saveItem = function (key, obj) {
-
+
if (this.config && this.config.cacheLocation && this.config.cacheLocation === 'localStorage') {
-
+
if (!this._supportsLocalStorage()) {
this.info('Local storage is not supported');
return false;
}
-
+
localStorage.setItem(key, obj);
-
+
return true;
}
-
+
// Default as session storage
if (!this._supportsSessionStorage()) {
this.info('Session storage is not supported');
return false;
}
-
+
sessionStorage.setItem(key, obj);
return true;
};
AuthenticationContext.prototype._getItem = function (key) {
-
+
if (this.config && this.config.cacheLocation && this.config.cacheLocation === 'localStorage') {
-
+
if (!this._supportsLocalStorage()) {
this.info('Local storage is not supported');
return null;
}
-
+
return localStorage.getItem(key);
}
-
+
// Default as session storage
if (!this._supportsSessionStorage()) {
this.info('Session storage is not supported');
return null;
}
-
+
return sessionStorage.getItem(key);
};
@@ -1177,7 +1125,7 @@ AuthenticationContext.prototype._cloneConfig = function (obj) {
if (null === obj || 'object' !== typeof obj) {
return obj;
}
-
+
var copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) {
@@ -1187,7 +1135,7 @@ AuthenticationContext.prototype._cloneConfig = function (obj) {
return copy;
};
-AuthenticationContext.prototype._addClientId = function () {
+AuthenticationContext.prototype._addLibMetadata = function () {
// x-client-SKU
// x-client-Ver
return '&x-client-SKU=Js&x-client-Ver=' + this._libVersion();
@@ -1197,13 +1145,13 @@ AuthenticationContext.prototype.log = function (level, message, error) {
if (level <= Logging.level) {
var correlationId = this.config.correlationId;
var timestamp = new Date().toUTCString();
-
- var formattedMessage = timestamp + ':' + correlationId + '-' + this.CONSTANTS.LEVEL_STRING_MAP[level] + ' ' + message;
-
+
+ var formattedMessage = timestamp + ':' + correlationId + '-' + this._libVersion() + '-' + this.CONSTANTS.LEVEL_STRING_MAP[level] + ' ' + message;
+
if (error) {
formattedMessage += '\nstack:\n' + error.stack;
}
-
+
Logging.log(formattedMessage);
}
};
@@ -1225,5 +1173,5 @@ AuthenticationContext.prototype.verbose = function (message) {
};
AuthenticationContext.prototype._libVersion = function () {
- return '1.0.8';
+ return '1.0.9';
};
\ No newline at end of file
diff --git a/package.json b/package.json
index 7005593d..86bdd03f 100644
--- a/package.json
+++ b/package.json
@@ -1,21 +1,16 @@
{
"name": "adal-angular",
"author": {
- "name": "Microsoft Open Technologies Inc",
- "email": "msopentech@microsoft.com",
- "url": "https://github.com/AzureAD/azure-activedirectory-library-for-js"
+ "name": "Microsoft",
+ "email": "nugetaad@microsoft.com",
+ "url": "https://www.microsoft.com"
},
- "licenses": [
- {
- "type": "Apache 2.0",
- "url": "http://www.apache.org/licenses/LICENSE-2.0"
- }
- ],
+ "license": "Apache-2.0",
"repository": {
"type": "git",
- "url": ""
+ "url": "https://github.com/AzureAD/azure-activedirectory-library-for-js.git"
},
- "version": "1.0.8",
+ "version": "1.0.9",
"description": "Windows Azure Active Directory Client Library for js",
"keywords": [
"implicit",
diff --git a/tests/angularModuleSpec.js b/tests/angularModuleSpec.js
index 97f95c0f..769df3ee 100644
--- a/tests/angularModuleSpec.js
+++ b/tests/angularModuleSpec.js
@@ -23,17 +23,19 @@
'use strict';
describe('TaskCtl', function () {
- var scope, $httpBackend, adalServiceProvider, rootScope, controller;
+ var scope, $httpBackend, adalServiceProvider, rootScope, controller, q, window;
//mock Application to allow us to inject our own dependencies
beforeEach(angular.mock.module('TestApplication'));
//mock the controller for the same reason and include $rootScope and $controller
- beforeEach(angular.mock.inject(function (_adalAuthenticationService_, _$rootScope_, _$controller_, _$httpBackend_) {
+ beforeEach(angular.mock.inject(function (_adalAuthenticationService_, _$rootScope_, _$controller_, _$httpBackend_, _$q_, _$window_) {
adalServiceProvider = _adalAuthenticationService_;
rootScope = _$rootScope_;
controller = _$controller_;
$httpBackend = _$httpBackend_;
+ q = _$q_;
+ window = _$window_;
//create an empty scope
scope = rootScope.$new();
@@ -56,6 +58,23 @@ describe('TaskCtl', function () {
return '';
};
+ adalServiceProvider.acquireToken = function (resource) {
+ console.log('acquire token for resource:' + resource);
+ var token = '';
+ if (resource === 'resource1') {
+ token = 'RenewToken3434';
+ }
+
+ if (resource === 'resource2') {
+ token = 'RenewToken123';
+ }
+
+ if (resource === adalServiceProvider.config.loginResource) {
+ token = 'RenewToken456';
+ }
+ return q.when(token);
+ };
+
controller('TaskCtl', { $scope: scope, adalAuthenticationService: adalServiceProvider });
}));
@@ -64,7 +83,7 @@ describe('TaskCtl', function () {
expect(scope.user.isAuthenticated).toBe(true);
});
- it('injects tokens for webapi call for given endpoint', function () {
+ it('send tokens for webapi call in endpoints list', function () {
$httpBackend.expectGET('/api/Todo/5', function (headers) {
return headers.Authorization === 'Bearer Token3434';
}).respond(200, { id: 5, name: 'TODOItem1' });
@@ -75,7 +94,7 @@ describe('TaskCtl', function () {
expect(task.name).toBe('TODOItem1');
});
- it('does not sent tokens for other webapi calls', function () {
+ it('send tokens for webapi call in endpoints list', function () {
$httpBackend.expectGET('/anotherApi/Item/13', function (headers) {
console.log('headers test' + headers.Authorization);
return headers.Authorization === 'Bearer Token123';
@@ -87,6 +106,14 @@ describe('TaskCtl', function () {
expect(task.itemName).toBe('ItemWithoutAuth');
});
+ it('send tokens for webapi call in endpoints list', function () {
+ $httpBackend.expectGET('https://testapi.com/', function (headers) {
+ return headers.Authorization === 'Bearer Token3434';
+ }).respond(200);
+ scope.taskCall3();
+ $httpBackend.flush();
+ });
+
it('does not send tokens for webapi(https) call not in endpoints list', function () {
$httpBackend.expectGET('https://test.com/', function (headers) {
return headers.hasOwnProperty('Authorization') === false;
@@ -103,14 +130,6 @@ describe('TaskCtl', function () {
$httpBackend.flush();
});
- it('send tokens for webapi call in endpoints list', function () {
- $httpBackend.expectGET('https://testapi.com/', function (headers) {
- return headers.Authorization === 'Bearer Token3434';
- }).respond(200);
- scope.taskCall3();
- $httpBackend.flush();
- });
-
it ('send tokens for app backend call not in endpoints list', function () {
$httpBackend.expectGET('/someapi/item', function (headers) {
return headers.Authorization === 'Bearer Token456'
@@ -126,4 +145,83 @@ describe('TaskCtl', function () {
scope.taskCall5();
$httpBackend.flush();
});
-});
\ No newline at end of file
+
+ it('renews tokens for app backend', function () {
+ // This makes adal to try renewing the token since no token is returned from cache
+ adalServiceProvider.getCachedToken = function () {
+ return '';
+ };
+ $httpBackend.expectGET('https://myapp.com/someapi/item', function (headers) {
+ return headers.Authorization === 'Bearer RenewToken456';
+ }).respond(200, { id: 5, name: 'TODOItem2' });
+ scope.taskCall5();
+ $httpBackend.flush();
+
+ var task = scope.task;
+ expect(task.name).toBe('TODOItem2');
+ });
+
+ it('renews tokens for webapi in endpoint list', function () {
+ adalServiceProvider.getCachedToken = function () {
+ return '';
+ };
+ $httpBackend.expectGET('/anotherApi/Item/13', function (headers) {
+ console.log('headers test' + headers.Authorization);
+ return headers.Authorization === 'Bearer RenewToken123';
+ }).respond(200, { id: 5, itemName: 'ItemWithoutAuth' });
+ scope.itemCall();
+ $httpBackend.flush();
+
+ var task = scope.item;
+ expect(task.itemName).toBe('ItemWithoutAuth');
+ });
+
+ it('renews tokens for webapi in endpoint list', function () {
+ adalServiceProvider.getCachedToken = function () {
+ return '';
+ };
+ $httpBackend.expectGET('https://testapi.com/', function (headers) {
+ return headers.Authorization === 'Bearer RenewToken3434';
+ }).respond(200);
+ scope.taskCall3();
+ $httpBackend.flush();
+ });
+
+ it('tests errorResponse broadcast when login is in progress', function () {
+ adalServiceProvider.getCachedToken = function () {
+ return '';
+ };
+ adalServiceProvider.loginInProgress = function () {
+ return true;
+ };
+ spyOn(rootScope, '$broadcast').andCallThrough();
+ $httpBackend.expectGET('https://myapp.com/someapi/item', function (headers) {
+ return headers.Authorization === 'Bearer Token456'
+ }).respond(200);
+
+ rootScope.$on('adal:errorResponse', function (event, message) {
+ expect(event.name).toBe('adal:errorResponse');
+ expect(message).toBe('login in progress, cancelling the request');
+ });
+ scope.taskCall5();
+ rootScope.$apply();
+ expect(rootScope.$broadcast).toHaveBeenCalledWith('adal:errorResponse', 'login in progress, cancelling the request');
+ });
+
+ it('tests stateMismatch broadcast when state does not match', function () {
+ window.parent.AuthenticationContext = function () {
+ return {
+ callback: function () { },
+ _renewStates: { }
+ };
+ };
+ window.location.hash = 'id_token=sample&state=4343';
+ spyOn(rootScope, '$broadcast').andCallThrough();
+ rootScope.$on('adal:stateMismatch', function (event, message) {
+ expect(event.name).toBe('adal:stateMismatch');
+ expect(message).toBe('Invalid_state. state: 4343');
+ });
+ rootScope.$apply();
+ expect(rootScope.$broadcast).toHaveBeenCalled();
+ });
+});
diff --git a/tests/testApp.js b/tests/testApp.js
index de4ce0c1..ca6ada6c 100644
--- a/tests/testApp.js
+++ b/tests/testApp.js
@@ -32,8 +32,8 @@ app.config(['$httpProvider', '$routeProvider', 'adalAuthenticationServiceProvide
var endpoints = {
'/api/Todo/': 'resource1',
- '/anotherApi/Item/': 'resource2',
- 'https://testapi.com/' : 'resource1'
+ '/anotherApi/Item/': 'resource2',
+ 'https://testapi.com/': 'resource1'
};
adalAuthenticationServiceProvider.init(
@@ -72,22 +72,6 @@ app.factory('TaskFactory', ['$http', function ($http) {
return serviceFactory;
}]);
-app.factory('TaskFactory2', ['$http', function ($http) {
- var serviceFactory = {};
- var _getItem = function () {
- return $http.get('https://test.com');
- };
-
- serviceFactory.getItem = _getItem;
- return serviceFactory;
-}]);
-
-app.controller('WidgetCtl', function ($scope, WidgetFactory) {
- $scope.text = 'Hello Test!';
-
- $scope.widget = WidgetFactory.get();
-});
-
app.controller('TaskCtl', ['$scope', '$location', 'adalAuthenticationService', 'TaskFactory', 'ItemFactory', function ($scope, $location, adalAuthenticationService, TaskFactory, ItemFactory) {
$scope.taskCall = function () {
@@ -113,17 +97,17 @@ app.controller('TaskCtl', ['$scope', '$location', 'adalAuthenticationService', '
$scope.task = data;
}).error(function (err) {
$scope.error = err;
- $scope.loaingMsg = "";
- });
+ $scope.loadingMsg = "";
+ });
}
-
+
$scope.taskCall3 = function () {
TaskFactory.getItem2('https://testapi.com/').success(function (data) {
$scope.task = data;
- }).error(function (err) {
+ }).error(function (err) {
$scope.error = err;
- $scope.loaingMsg = "";
- });
+ $scope.loadingMsg = "";
+ });
}
$scope.taskCall4 = function () {
@@ -131,7 +115,7 @@ app.controller('TaskCtl', ['$scope', '$location', 'adalAuthenticationService', '
$scope.task = data;
}).error(function (err) {
$scope.error = err;
- $scope.loaingMsg = "";
+ $scope.loadingMsg = "";
});
}
@@ -140,7 +124,7 @@ app.controller('TaskCtl', ['$scope', '$location', 'adalAuthenticationService', '
$scope.task = data;
}).error(function (err) {
$scope.error = err;
- $scope.loaingMsg = "";
+ $scope.loadingMsg = "";
});
}
@@ -152,5 +136,6 @@ app.controller('TaskCtl', ['$scope', '$location', 'adalAuthenticationService', '
$scope.loaingMsg = "";
});
}
+
$scope.user = adalAuthenticationService.userInfo;
}]);
diff --git a/tests/unit/spec/AdalSpec.js b/tests/unit/spec/AdalSpec.js
index 7d4b3cd9..0a5efa7d 100644
--- a/tests/unit/spec/AdalSpec.js
+++ b/tests/unit/spec/AdalSpec.js
@@ -124,10 +124,6 @@ describe('Adal', function () {
expect(adal.getResourceForEndpoint('b')).toBe('default resource');
});
- it('sets default resource', function () {
- expect(adal.config.resource).toBe('default resource');
- });
-
it('says token expired', function () {
adal.config.expireOffsetSeconds = SECONDS_TO_EXPIRE - 100;
expect(adal.getCachedToken(RESOURCE1)).toEqual('access_token_in_cache' + RESOURCE1);
@@ -153,7 +149,7 @@ describe('Adal', function () {
console.log('instance:' + adal.instance);
adal.login();
expect(adal.promptUser).toHaveBeenCalledWith(DEFAULT_INSTANCE + conf.tenant + '/oauth2/authorize?response_type=id_token&client_id=client&redirect_uri=contoso_site&state=33333333-3333-4333-b333-333333333333'
- + '&client-request-id=33333333-3333-4333-b333-333333333333' + adal._addClientId() + '&nonce=33333333-3333-4333-b333-333333333333');
+ + '&client-request-id=33333333-3333-4333-b333-333333333333' + adal._addLibMetadata() + '&nonce=33333333-3333-4333-b333-333333333333');
expect(adal.config.state).toBe('33333333-3333-4333-b333-333333333333');
});
@@ -180,7 +176,7 @@ describe('Adal', function () {
adal.login();
expect(adal.config.displayCall).toHaveBeenCalledWith(DEFAULT_INSTANCE + conf.tenant + '/oauth2/authorize?response_type=id_token&client_id=client&redirect_uri=contoso_site&state=33333333-3333-4333-b333-333333333333'
+ '&client-request-id=33333333-3333-4333-b333-333333333333'
- + adal._addClientId()
+ + adal._addLibMetadata()
+ '&nonce=33333333-3333-4333-b333-333333333333'
);
expect(adal.config.state).toBe('33333333-3333-4333-b333-333333333333');
@@ -210,21 +206,7 @@ describe('Adal', function () {
expect(err).toBe('resource is required');
});
- it('returns err msg if token expired and renew failed before', function () {
- storageFake.setItem(adal.CONSTANTS.STORAGE.FAILED_RENEW, 'renew has failed');
- adal.config.expireOffsetSeconds = SECONDS_TO_EXPIRE + 100;
- var err = '';
- var token = '';
- var callback = function (valErr, valToken) {
- err = valErr;
- token = valToken;
- };
- adal.acquireToken(RESOURCE1, callback);
- expect(err).toBe('renew has failed');
- });
-
it('attempts to renew if token expired and renew is allowed', function () {
- storageFake.setItem(adal.CONSTANTS.STORAGE.FAILED_RENEW, '');
adal.config.redirectUri = 'contoso_site';
adal.config.clientId = 'client';
adal.config.expireOffsetSeconds = SECONDS_TO_EXPIRE + 100;
@@ -247,14 +229,13 @@ describe('Adal', function () {
runs(function () {
console.log('Frame src:' + frameMock.src);
expect(frameMock.src).toBe(DEFAULT_INSTANCE + conf.tenant + '/oauth2/authorize?response_type=token&client_id=client&resource=' + RESOURCE1 + '&redirect_uri=contoso_site&state=33333333-3333-4333-b333-333333333333%7Ctoken.resource1'
- + '&client-request-id=33333333-3333-4333-b333-333333333333' + adal._addClientId() + '&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');
});
});
//Necessary for integration with Angular when multiple http calls are queued.
it('allows multiple callers to be notified when the token is renewed', function () {
- storageFake.setItem(adal.CONSTANTS.STORAGE.FAILED_RENEW, '');
adal.config.redirectUri = 'contoso_site';
adal.config.clientId = 'client';
adal.config.expireOffsetSeconds = SECONDS_TO_EXPIRE + 100;
@@ -285,7 +266,7 @@ describe('Adal', function () {
runs(function () {
console.log('Frame src:' + frameMock.src);
expect(frameMock.src).toBe(DEFAULT_INSTANCE + conf.tenant + '/oauth2/authorize?response_type=token&client_id=client&resource=' + RESOURCE1 + '&redirect_uri=contoso_site&state=33333333-3333-4333-b333-333333333333%7Ctoken.resource1'
- + '&client-request-id=33333333-3333-4333-b333-333333333333' + adal._addClientId() + '&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');
});
//Simulate callback from the frame.
@@ -344,10 +325,8 @@ describe('Adal', function () {
storageFake.setItem(adal.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY + 'key2', 'value2');
storageFake.setItem(adal.CONSTANTS.STORAGE.EXPIRATION_KEY, 3);
storageFake.setItem(adal.CONSTANTS.STORAGE.EXPIRATION_KEY, 3);
- storageFake.setItem(adal.CONSTANTS.STORAGE.FAILED_RENEW, 'failed renew');
storageFake.setItem(adal.CONSTANTS.STORAGE.SESSION_STATE, 'session_state');
storageFake.setItem(adal.CONSTANTS.STORAGE.STATE_LOGIN, 'state login');
- storageFake.setItem(adal.CONSTANTS.STORAGE.STATE_IDTOKEN, 'state idtoken');
storageFake.setItem(adal.CONSTANTS.STORAGE.START_PAGE, 'start page');
storageFake.setItem(adal.CONSTANTS.STORAGE.USERNAME, 'username');
storageFake.setItem(adal.CONSTANTS.STORAGE.ERROR, 'error');
@@ -364,9 +343,7 @@ describe('Adal', function () {
storageFake.setItem(adal.CONSTANTS.STORAGE.TOKEN_KEYS, 'key1|' + RESOURCE1 + '|');
storageFake.setItem(adal.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY + 'key1', 'value1');
storageFake.setItem(adal.CONSTANTS.STORAGE.EXPIRATION_KEY + 'key1', 3);
- storageFake.setItem(adal.CONSTANTS.STORAGE.FAILED_RENEW, 'failed renew');
storageFake.setItem(adal.CONSTANTS.STORAGE.STATE_RENEW, 'state renew');
- storageFake.setItem(adal.CONSTANTS.STORAGE.STATE_IDTOKEN, 'state idtoken');
storageFake.setItem(adal.CONSTANTS.STORAGE.ERROR, 'error');
storageFake.setItem(adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION, 'error description');
adal.clearCacheForResource(RESOURCE1);
@@ -458,7 +435,6 @@ describe('Adal', function () {
expect(requestInfo.stateMatch).toBe(false);
checkStateType(adal.CONSTANTS.STORAGE.STATE_LOGIN, '1234', adal.REQUEST_TYPE.LOGIN);
- checkStateType(adal.CONSTANTS.STORAGE.STATE_IDTOKEN, '1236', adal.REQUEST_TYPE.ID_TOKEN);
});
var checkStateType = function (state, stateExpected, requestType) {
@@ -491,10 +467,9 @@ describe('Adal', function () {
valid: true,
parameters: { 'access_token': 'token123', 'state': '123' },
stateMatch: true,
- stateResponse: '123',
- requestType: adal.REQUEST_TYPE.LOGIN
+ stateResponse: '123|loginResource1',
+ requestType: adal.REQUEST_TYPE.RENEW_TOKEN
};
- adal.config.loginResource = 'loginResource1';
adal.saveTokenFromHash(requestInfo);
expect(storageFake.getItem(adal.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY + 'loginResource1')).toBe('token123');
@@ -505,10 +480,9 @@ describe('Adal', function () {
valid: true,
parameters: { 'access_token': 'token123', 'state': '123', 'expires_in': 3589 },
stateMatch: true,
- stateResponse: '123',
- requestType: adal.REQUEST_TYPE.LOGIN
+ stateResponse: '123|loginResource1',
+ requestType: adal.REQUEST_TYPE.RENEW_TOKEN
};
- adal.config.loginResource = 'loginResource1';
adal.saveTokenFromHash(requestInfo);
expect(storageFake.getItem(adal.CONSTANTS.STORAGE.EXPIRATION_KEY + 'loginResource1')).toBe(mathMock.round(1) + 3589 + '');
});
@@ -623,6 +597,25 @@ describe('Adal', function () {
expect(adal._getHostFromUri('http://localhost:8080')).toBe('localhost:8080');
});
+ it('test decode jwt', function () {
+ expect(adal._decodeJwt('')).toBe(null);
+ expect(adal._decodeJwt(null)).toBe(null);
+ })
+
+ it('saves error if state mismatch', function () {
+ var requestInfo = {
+ valid: true,
+ parameters: { 'access_token': 'token123', 'state': '123' },
+ stateMatch: false,
+ stateResponse: '64532',
+ requestType: adal.REQUEST_TYPE.UNKNOWN
+ };
+ adal.config.loginResource = 'loginResource1';
+ adal.saveTokenFromHash(requestInfo);
+
+ expect(storageFake.getItem(adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION)).toBe('Invalid_state. state: ' + requestInfo.stateResponse);
+ });
+
// TODO angular intercepptor
// TODO angular authenticaitonService