diff --git a/src/module.js b/src/module.js index 0f08d0b..261b536 100644 --- a/src/module.js +++ b/src/module.js @@ -188,7 +188,8 @@ angular.module('stormpath', [ 'stormpath.userService', 'stormpath.viewModelService', 'stormpath.socialLogin', - 'stormpath.oauth' + 'stormpath.oauth', + 'stormpath.ui-router' ]) .factory('StormpathAgentInterceptor',['$isCurrentDomain', '$spHeaders', function($isCurrentDomain, $spHeaders){ @@ -246,10 +247,11 @@ angular.module('stormpath', [ */ this.$get = [ - '$user', '$injector', 'STORMPATH_CONFIG', '$rootScope', '$location', - function stormpathServiceFactory($user, $injector, STORMPATH_CONFIG, $rootScope, $location) { + 'StormpathUIRouter', '$user', '$injector', 'STORMPATH_CONFIG', '$rootScope', '$location', + function stormpathServiceFactory(StormpathUIRouter, $user, $injector, STORMPATH_CONFIG, $rootScope, $location) { var $state; var $route; + var $transitions; // ui-router 1 replacement for state events function StormpathService(){ var encoder = new UrlEncodedFormParser(); @@ -263,158 +265,16 @@ angular.module('stormpath', [ $route = $injector.get('$route'); } - return this; - } - function stateChangeUnauthenticatedEvent(toState, toParams){ - /** - * @ngdoc event - * - * @name stormpath.$stormpath#$stateChangeUnauthenticated - * - * @eventOf stormpath.$stormpath - * - * @eventType broadcast on root scope - * - * @param {Object} event - * - * Angular event object. - * - * @param {Object} toState The state that the user attempted to access. - * - * @param {Object} toParams The state params of the state that the user - * attempted to access. - * - * @description - * - * This event is broadcast when a UI state change is prevented, - * because the user is not logged in. - * - * Use this event if you want to implement your own strategy for - * presenting the user with a login form. - * - * To receive this event, you must be using the UI Router integration. - * - * @example - * - *
-         *   $rootScope.$on('$stateChangeUnauthenticated',function(e,toState,toParams){
-         *     // Your custom logic for deciding how the user should login, and
-         *     // if you want to redirect them to the desired state afterwards
-         *   });
-         * 
- */ - $rootScope.$broadcast(STORMPATH_CONFIG.STATE_CHANGE_UNAUTHENTICATED,toState,toParams); - } - function stateChangeUnauthorizedEvent(toState,toParams){ - /** - * @ngdoc event - * - * @name stormpath.$stormpath#$stateChangeUnauthorized - * - * @eventOf stormpath.$stormpath - * - * @eventType broadcast on root scope - * - * @param {Object} event - * - * Angular event object. - * - * @param {Object} toState The state that the user attempted to access. - * - * @param {Object} toParams The state params of the state that the user - * attempted to access. - * - * @description - * - * This event is broadcast when a UI state change is prevented, - * because the user is not authorized by the rules defined in the - * {@link stormpath.SpStateConfig:SpStateConfig Stormpath State Configuration} - * for the requested state. - * - * Use this event if you want to implement your own strategy for telling - * the user that they are forbidden from viewing that state. - * - * To receive this event, you must be using the UI Router integration. - * - * @example - * - *
-         *   $rootScope.$on('$stateChangeUnauthorized',function(e,toState,toParams){
-         *     // Your custom logic for deciding how the user should be
-         *     // notified that they are forbidden from this state
-         *   });
-         * 
- */ - $rootScope.$broadcast(STORMPATH_CONFIG.STATE_CHANGE_UNAUTHORIZED,toState,toParams); - } - StormpathService.prototype.stateChangeInterceptor = function stateChangeInterceptor(config) { - $rootScope.$on('$stateChangeStart', function(e,toState,toParams){ - var sp = toState.sp || {}; // Grab the sp config for this state - var authorities = (toState.data && toState.data.authorities) ? toState.data.authorities : undefined; - - if((sp.authenticate || sp.authorize || (authorities && authorities.length)) && (!$user.currentUser)){ - e.preventDefault(); - $user.get().then(function(){ - // The user is authenticated, continue to the requested state - if(sp.authorize || (authorities && authorities.length)){ - if(authorizeStateConfig(sp, authorities)){ - $state.go(toState.name,toParams); - }else{ - stateChangeUnauthorizedEvent(toState,toParams); - } - }else{ - $state.go(toState.name,toParams); - } - },function(){ - // The user is not authenticated, emit the necessary event - stateChangeUnauthenticatedEvent(toState,toParams); - }); - }else if(sp.waitForUser && ($user.currentUser===null)){ - e.preventDefault(); - $user.get().finally(function(){ - $state.go(toState.name,toParams); - }); - } - else if($user.currentUser && (sp.authorize || (authorities && authorities.length))){ - if(!authorizeStateConfig(sp, authorities)){ - e.preventDefault(); - stateChangeUnauthorizedEvent(toState,toParams); - } - }else if(toState.name===config.loginState){ - /* - If the user is already logged in, we will redirect - away from the login page and send the user to the - post login state. - */ - if($user.currentUser!==false){ - e.preventDefault(); - $user.get().finally(function(){ - if($user.currentUser && $user.currentUser.href){ - $state.go(config.defaultPostLoginState); - } else { - $state.go(toState.name,toParams); - } - }); - } - } - }); - }; - - function authorizeStateConfig(spStateConfig, authorities){ - var sp = spStateConfig; - if(sp && sp.authorize && sp.authorize.group) { - return $user.currentUser.inGroup(sp.authorize.group); - }else if(authorities){ - // add support for reading from JHipster's data: { authorities: ['ROLE_ADMIN'] } - // https://github.com/stormpath/stormpath-sdk-angularjs/issues/190 - var roles = authorities.filter(function(authority){ - return $user.currentUser.inGroup(authority); - }); - return roles.length > 0; - }else{ - console.error('Unknown authorize configuration for spStateConfig',spStateConfig); - return false; + if ($injector.has('$transitions')) { + $transitions = $injector.get('$transitions'); } + + this.uiRouterWrapper = StormpathUIRouter.registerUIRouterInternals( + $transitions, + $state + ); + + return this; } function routeChangeUnauthenticatedEvent(toRoute) { @@ -538,7 +398,7 @@ angular.module('stormpath', [ goToRoute(toRoute); }); } else if ($user.currentUser && sp.authorize) { - if (!authorizeStateConfig(sp)) { + if (!authorizeRouteConfig(sp)) { event.preventDefault(); routeChangeUnauthorizedEvent(toRoute); } @@ -620,10 +480,10 @@ angular.module('stormpath', [ * }); * */ - StormpathService.prototype.uiRouter = function uiRouter(config){ + StormpathService.prototype.uiRouter = function uiRouter(config) { var self = this; config = typeof config === 'object' ? config : {}; - this.stateChangeInterceptor(config); + this.uiRouterWrapper.registerInterceptor(config); if(config.loginState){ self.unauthenticatedWather = $rootScope.$on(STORMPATH_CONFIG.STATE_CHANGE_UNAUTHENTICATED,function(e,toState,toParams){ diff --git a/src/stormpath.oauth.js b/src/stormpath.oauth.js index b0668a4..e9ce965 100644 --- a/src/stormpath.oauth.js +++ b/src/stormpath.oauth.js @@ -65,9 +65,15 @@ function StormpathOAuthTokenProvider(STORMPATH_CONFIG) { * {@link stormpath.oauth.StormpathOAuthToken#setTokenStoreType StormpathOAuthToken.setTokenStoreType}. */ this.$get = function $get($q, $normalizeObjectKeys, TokenStoreManager, $injector) { - function StormpathOAuthToken() { - this.tokenStore = TokenStoreManager.getTokenStore(self._tokenStoreType); - } + function StormpathOAuthToken() {} + + StormpathOAuthToken.prototype.getTokenStore = function getTokenStore() { + if (angular.isUndefined(this.tokenStore)) { + this.tokenStore = TokenStoreManager.getTokenStore(self._tokenStoreType); + } + + return this.tokenStore; + }; /** * @ngdoc method @@ -102,7 +108,7 @@ function StormpathOAuthTokenProvider(STORMPATH_CONFIG) { var canonicalToken = $normalizeObjectKeys(token); // Store a time at which we should renew the token, subtract off one second to give us some buffer of time canonicalToken.exp = new Date(new Date().setMilliseconds(0)+((token.expires_in-1)*1000)); - return this.tokenStore.put(STORMPATH_CONFIG.OAUTH_TOKEN_STORAGE_NAME, canonicalToken); + return this.getTokenStore().put(STORMPATH_CONFIG.OAUTH_TOKEN_STORAGE_NAME, canonicalToken); }; /** @@ -120,7 +126,7 @@ function StormpathOAuthTokenProvider(STORMPATH_CONFIG) { * {@link stormpath.oauth.StormpathOAuthToken#setTokenResponse StormpathOAuthToken.setTokenResponse}. */ StormpathOAuthToken.prototype.getTokenResponse = function getTokenResponse() { - return this.tokenStore.get(STORMPATH_CONFIG.OAUTH_TOKEN_STORAGE_NAME); + return this.getTokenStore().get(STORMPATH_CONFIG.OAUTH_TOKEN_STORAGE_NAME); }; /** @@ -136,7 +142,7 @@ function StormpathOAuthTokenProvider(STORMPATH_CONFIG) { * implementation details. */ StormpathOAuthToken.prototype.removeToken = function removeToken() { - return this.tokenStore.remove(STORMPATH_CONFIG.OAUTH_TOKEN_STORAGE_NAME); + return this.getTokenStore().remove(STORMPATH_CONFIG.OAUTH_TOKEN_STORAGE_NAME); }; /** diff --git a/src/stormpath.ui-router.js b/src/stormpath.ui-router.js new file mode 100644 index 0000000..3a953fc --- /dev/null +++ b/src/stormpath.ui-router.js @@ -0,0 +1,544 @@ +'use strict'; + +/** +* @ngdoc overview +* +* @name stormpath.ui-router +* +* @description +* +* This module provides the {@link stormpath.ui.router:StormpathUIRouter StormpathUIRouter} +* service, which handles Stormpath's integration with UIRouter (both 0.*.* and 1.**). +*/ +angular + .module('stormpath.ui-router', ['stormpath.userService']) + .service('StormpathUIRouter', StormpathUIRouter); + +/** + * @ngdoc service + * @name stormpath.ui.router:StormpathUIRouterTransition + * @description + * + * Encapsulates UIRouter state transitions for both versions 0.*.* and 1.*.* + * Expects UIRouter's `$state` as the first argument, and the transition start + * `arguments` as the second argument. It provides normalised access to the + * standard state transition manually. + * + * Should never be instantiated in client code. This class is meant only + * for private use in the {@link stormpath.ui.router:StormpathUIRouter StormpathUIRouter} + * service. + */ +function StormpathUIRouterTransition($state, args) { + this.$state = $state; + this._parseArguments(args); +} + +StormpathUIRouterTransition.prototype._parseArguments = function _parseArguments(args) { + if (args.length >= 5) { + this.$event = args[0]; + this.$toState = args[1]; + this.$toParams = args[2]; + this.$fromState = args[3]; + this.$fromParams = args[4]; + } else { + this.$trans = args[0]; + } +}; + +/** + * @ngdoc method + * @name isLegacy + * @methodOf stormpath.ui.router:StormpathUIRouterTransition + * + * @returns {Boolean} Whether this is UI Router < 1.0.0 + * + * @description + * Checks whether the version of UIRouter being used is 0.*.* + */ +StormpathUIRouterTransition.prototype.isLegacy = function isLegacy() { + return typeof this.$trans === 'undefined'; +}; + +/** + * @ngdoc method + * @name sp + * @methodOf stormpath.ui.router:StormpathUIRouterTransition + * + * @returns {Object} + * Stormpath configuration for this state, or empty object if none is defined + * + * @description + * Accesses the Stormpath state configuration (set in the `sp` property) for + * the state currently being transitioned to. If no such configuration object + * is defined, an empty object is returned instead. + */ +StormpathUIRouterTransition.prototype.sp = function sp() { + return this.toState().sp || {}; +}; + +/** + * @ngdoc method + * @name authorities + * @methodOf stormpath.ui.router:StormpathUIRouterTransition + * + * @returns {Array} List of roles authorized to enter this state + * + * @description + * Retrieves a list of roles that are authorized to enter this state, defined + * in the state definition under `data.authorities`. If there is no such array, + * an empty array is returned. + */ +StormpathUIRouterTransition.prototype.authorities = function authorities() { + var toState = this.toState(); + + if (toState.data && toState.data.authorities) { + return toState.data.authorities; + } + + return []; +}; + +/** + * @ngdoc method + * @name fromState + * @methodOf stormpath.ui.router:StormpathUIRouterTransition + * + * @returns {State} State definition for state from which the transition is occurring + * + * @description + * Returns the state definition object for the state currently being transitioned + * from. This is a wrapper to provide uniform access to both UI Router 0.*.* and + * UI router 1.*.* state. + */ +StormpathUIRouterTransition.prototype.fromState = function fromState() { + return this.$fromState || this.$trans.from(); +}; + +/** + * @ngdoc method + * @name fromParams + * @methodOf stormpath.ui.router:StormpathUIRouterTransition + * + * @returns {Object} Parameters of the state currently being transitioned from + * + * @description + * Returns the state parameters for the state currently being transitioned + * from. This is a wrapper to provide uniform access to both UI Router 0.*.* and + * UI router 1.*.* state parameters. + */ +StormpathUIRouterTransition.prototype.fromParams = function fromParams() { + return this.$fromParams || this.$trans.params('from'); +}; + +/** + * @ngdoc method + * @name toState + * @methodOf stormpath.ui.router:StormpathUIRouterTransition + * + * @returns {State} State definition for state to which the transition is occurring + * + * @description + * Returns the state definition object for the state currently being transitioned + * to. This is a wrapper to provide uniform access to both UI Router 0.*.* and + * UI router 1.*.* state. + */ +StormpathUIRouterTransition.prototype.toState = function toState() { + return this.$toState || this.$trans.to(); +}; + +/** + * @ngdoc method + * @name toParams + * @methodOf stormpath.ui.router:StormpathUIRouterTransition + * + * @returns {Object} Parameters of the state currently being transitioned to + * + * @description + * Returns the state parameters for the state currently being transitioned + * to. This is a wrapper to provide uniform access to both UI Router 0.*.* and + * UI router 1.*.* state parameters. + */ +StormpathUIRouterTransition.prototype.toParams = function toParams() { + return this.$toParams || this.$trans.params(); +}; + +/** + * @ngdoc method + * @name pause + * @methodOf stormpath.ui.router:StormpathUIRouterTransition + * + * @returns {Boolean} Always `false` + * + * @description + * Prevents the current transition from happening. This actually only does so + * for UIRouter 0.*.*. For UIRouter 1.*.*, the resolution is promise-based and + * will not resolve when a promise is returned until that promise does. + * + * Returns `false` as a helper for UIRouter 1.*.* - in it, when `false` is returned, + * the transition is prevented. + */ +StormpathUIRouterTransition.prototype.pause = function pause() { + if (this.isLegacy()) { + this.$event.preventDefault(); + } + + return false; +}; + +/** + * @ngdoc method + * @name resume + * @methodOf stormpath.ui.router:StormpathUIRouterTransition + * + * @description + * Continues the transition to the original target state, if prevented. Otherwise + * a noop. + */ +StormpathUIRouterTransition.prototype.resume = function resume() { + this.redirect(this.toState(), this.toParams()); +}; + +/** + * @ngdoc method + * @name redirect + * @methodOf stormpath.ui.router:StormpathUIRouterTransition + * + * @param {State|String} state State definition or state name + * @param {Object} params State parameters + * + * @description + * Performs a transition to a given state with the given parameters. A wrapper + * around UIRouter's `$state.go`. + */ +StormpathUIRouterTransition.prototype.redirect = function redirect(state, params) { + return this.$state.go(state.name || state, params); +}; + + +/** + * @ngdoc service + * @name stormpath.ui.router:StormpathUIRouter + * + * @requires $rootScope + * @requires stormpath.userService.$user:$user + * @requires stormpath.STORMPATH_CONFIG:STORMPATH_CONFIG + * + * @description + * A wrapper around UI Router that allows Stormpath to connect to state transitions + * and perform authentication, authorization and redirection. + * + * Emits {@link stormpath.ui.router:StormpathUIRouter#events_$stateChangeUnauthorized $stateChangeUnauthorized} + * or {@link stormpath.ui.router:StormpathUIRouter#events_$stateChangeUnauthenticated $stateChangeUnauthenticated} + * in case of authorization or authentication failure, to allow for further client handling. + * + * When initialized, {@link stormpath.ui.router:StormpathUIRouter#methods_registerUIRouterInternals StormpathUIRouter.registerUIRouterInternals()} + * must be called to inject `$state` and `$transitions` (if UIRouter >= 1.0.0) into the service. + */ +function StormpathUIRouter($rootScope, $user, STORMPATH_CONFIG) { + this.$rootScope = $rootScope; + this.$user = $user; + this.STORMPATH_CONFIG = STORMPATH_CONFIG; +} + +/** + * @ngdoc metod + * @name registerUIRouterInternals + * @methodOf stormpath.ui.router:StormpathUIRouter + * + * @param {TransitionsService} $transitions UIRouter 1.*.* `$transitions` service. If undefined, UIRouter 0.*.* is assumed + * @param {StateService} $state UIRouter `$state` service + * @returns {StormpathUIRouter} This instance, to allow for chaining. + * + * @description + * Anguiar UIRouter services are passed to this method to allow for them to be used + * if and only if UIRouter is the routing system being used. + * + * This method must be called if UIRouter is used (but the client code should not + * have to do so), and when called, at least `$state` must be defined. + */ +StormpathUIRouter.prototype.registerUIRouterInternals = function registerInternals($transitions, $state) { + this.$transitions = $transitions; + this.$state = $state; + return this; +}; + +/** + * @ngdoc method + * @name isLegacy + * @methodOf stormpath.ui.router:StormpathUIRouter + * + * @returns {Boolean} Whether this is UI Router < 1.0.0 + * + * @description + * Checks whether the version of UIRouter being used is 0.*.* + */ +StormpathUIRouter.prototype.isLegacy = function isLegacy() { + return typeof this.$transitions === 'undefined'; +}; + +/** + * @ngdoc method + * @name authorizeStateConfig + * @methodOf stormpath.ui.router:StormpathUIRouter + * + * @param {StormpathUIRouterTransition} transition Current transition + * @returns {Boolean} Is the user authorized to make this state transition + * + * @description + * Checks whether the user has the proper authorization to make the current + * transition to the target state. Does so by reading the state configuration's + * `sp.authorize` or `data.authorities` permitted role arrays. + */ +StormpathUIRouter.prototype.authorizeStateConfig = function authorizeStateConfig(transition) { + var sp = transition.sp(); + var authorities = sp.authorities(); + + if (sp && sp.authorize && sp.authorize.group) { + return this.$user.currentUser.inGroup(sp.authorize.group); + } else if (authorities.length) { + // add support for reading from JHipster's data: { authorities: ['ROLE_ADMIN'] } + // https://github.com/stormpath/stormpath-sdk-angularjs/issues/190 + var roles = authorities.filter(function(authority) { + return this.$user.currentUser.inGroup(authority); + }); + return roles.length > 0; + } + + console.error('Unknown authorize configuration for spStateConfig', spStateConfig); + return false; +}; + +/** + * @ngdoc method + * @name onStateChange + * @methodOf stormpath.ui.router:StormpathUIRouter + * + * @param {StormpathUIRouterTransition} transition Current state transition + * + * @description + * Performs required checks and possibly redirects when transitioning to a state. + * The possible operations are: + * + * + * + * If none of these conditions apply, the handler does not modify the transition. + */ +StormpathUIRouter.prototype.onStateChange = function onStateChange(transition) { + var self = this; + var sp = transition.sp(); + var authorities = transition.authorities(); + var needsAuth = sp.authenticate + || sp.authorize + || authorities.length; + + if (needsAuth && !this.$user.currentUser) { + transition.pause(); + + return this.$user.get().then(function() { + // The user is authenticated, continue to the requested state + if (sp.authorize || authorities.length) { + if (this.authorizeStateConfig(transition)) { + return transition.resume(); + } + + self.emitUnauthorized(transition); + return false; + } + + return transition.resume(); + }).catch(function() { + self.emitUnauthenticated(transition); + return false; + }); + }else if (sp.waitForUser && (this.$user.currentUser===null)) { + transition.pause(); + + return this.$user.get().finally(function() { + return transition.resume(); + }); + } else if (this.$user.currentUser && (sp.authorize || authorities.length)) { + if (!this.authorizeStateConfig(transition)) { + this.emitUnauthorized(transition); + return transition.pause(); + } + } else if (transition.toState().name === this.config.loginState) { + /* + If the user is already logged in, we will redirect + away from the login page and send the user to the + post login state. + */ + if (this.$user.currentUser !== false) { + transition.pause(); + + return this.$user.get().finally(function() { + if (self.$user.currentUser && self.$user.currentUser.href) { + return transition.redirect(self.config.defaultPostLoginState, {}); + } + + return transition.resume(); + }); + } + } +}; + +/** + * @ngdoc method + * @name registerInterceptor + * @methodOf stormpath.ui.router:StormpathUIRouter + * + * @param {Object} config Stormpath UI router configuration + * + * @description + * Registers the UI Router interceptor to listen to state change events. + * Abstracts the operation's differences between UI Router 0.*.* and 1.*.*. + * The registered method is + * {@link stormpath.ui.router:StormpathUIRouter#onStateChange StormpathUIRouter.onStateChange()}. + */ +StormpathUIRouter.prototype.registerInterceptor = function registerInterceptor(config) { + var self = this; + this.config = config; + + if (this.isLegacy()) { + return this.$rootScope.$on('$stateChangeStart', function() { + self.onStateChange(new StormpathUIRouterTransition(self.$state, arguments)); + }); + } + + this.$transitions.onStart({}, function() { + self.onStateChange(new StormpathUIRouterTransition(self.$state, arguments)); + }); +}; + +/** + * @ngdoc method + * @name emitUnauthorized + * @methodOf stormpath.ui.router:StormpathUIRouter + * + * @param {StormpathUIRouterTransition} transition Current state transition + * + * @description + * Emits the {@link stormpath.ui.router:StormpathUIRouter#$stateChangeUnauthorized $stateChangeUnauthorized} + * event with the data about the target state to signify that the transition failed due to the user not + * being authorized. + */ +StormpathUIRouter.prototype.emitUnauthorized = function emitUnauthorized(transition) { + /** + * @ngdoc event + * @name stormpath.ui.router:StormpathUIRouter#$stateChangeUnauthorized + * + * @eventOf stormpath.ui.router:StormpathUIRouter + * + * @eventType broadcast on root scope + * + * @param {Object} event + * + * Angular event object. + * + * @param {Object} toState The state that the user attempted to access. + * + * @param {Object} toParams The state params of the state that the user + * attempted to access. + * + * @description + * + * This event is broadcast when a UI state change is prevented, + * because the user is not authorized by the rules defined in the + * {@link stormpath.SpStateConfig:SpStateConfig Stormpath State Configuration} + * for the requested state. + * + * Use this event if you want to implement your own strategy for telling + * the user that they are forbidden from viewing that state. + * + * To receive this event, you must be using the UI Router integration. + * + * @example + * + *
+   *   $rootScope.$on('$stateChangeUnauthorized',function(e,toState,toParams){
+   *     // Your custom logic for deciding how the user should be
+   *     // notified that they are forbidden from this state
+   *   });
+   * 
+ */ + this.$rootScope.$broadcast( + this.STORMPATH_CONFIG.STATE_CHANGE_UNAUTHORIZED, + transition.toState(), + transition.toParams() + ); +}; + +/** + * @ngdoc method + * @name emitUnauthenticated + * @methodOf stormpath.ui.router:StormpathUIRouter + * + * @param {StormpathUIRouterTransition} transition Current state transition + * + * @description + * Emits the {@link stormpath.ui.router:StormpathUIRouter#$stateChangeUnauthenticated $stateChangeUnauthenticated} + * event with the data about the target state to signify that the transition failed due to the user not + * being authenticated (i.e. having the permissions to access this route). + */ +StormpathUIRouter.prototype.emitUnauthenticated = function emitUnauthenticated(transition) { + /** + * @ngdoc event + * + * @name stormpath.ui.router:StormpathUIRouter#$stateChangeUnauthenticated + * + * @eventOf stormpath.ui.router:StormpathUIRouter + * + * @eventType broadcast on root scope + * + * @param {Object} event + * + * Angular event object. + * + * @param {Object} toState The state that the user attempted to access. + * + * @param {Object} toParams The state params of the state that the user + * attempted to access. + * + * @description + * + * This event is broadcast when a UI state change is prevented, + * because the user is not logged in. + * + * Use this event if you want to implement your own strategy for + * presenting the user with a login form. + * + * To receive this event, you must be using the UI Router integration. + * + * @example + * + *
+   *   $rootScope.$on('$stateChangeUnauthenticated',function(e,toState,toParams){
+   *     // Your custom logic for deciding how the user should login, and
+   *     // if you want to redirect them to the desired state afterwards
+   *   });
+   * 
+ */ + this.$rootScope.$broadcast( + this.STORMPATH_CONFIG.STATE_CHANGE_UNAUTHENTICATED, + transition.toState(), + transition.toParams() + ); +}; + +StormpathUIRouter.$inject = ['$rootScope', '$user', 'STORMPATH_CONFIG'];