Question

I have a requirement to use an iFrame for a SAML authentication solution. Basically, I have to create an Iframe on the page, which calls our SAML service. Once the iFrame gets the Auth object back, it fires off parent.postMessage(authObj) to alert the parent window that the auth info has been returned.

I want to capture that object in an angular service and make it available to the rest of the app.

Because the angular app bootstraps long before the SAML token is returned, the Angular Auth Service I wrote needs to have an event listener on the $window to process this token. It works, but I'm not seeing my 2-way-bound view update when the Service updates. Here is some code to show what I'm doing:

Angular Service:

angular.module('common.AuthDataModule', [])

.service('AuthDataService', function($window, $rootScope) {
var AuthObj = {};
AuthObj.params = {
  "isAuthorized": false,
  "principal": '',
  "description": '',
  "authorizationToken": ''
};


  function setAttributes(element, attributes) {
    for (var attr in attributes) { element.setAttribute(attr, attributes[attr]); }
  }

  var createIframe = (function () {
    var iframe = document.createElement("iframe");
    setAttributes(iframe, {
      "name": "auth",
      "id": "myFrame",
      "src": "https://my.dev.SAML2AuthService",
      "height": "0",
      "width": "0",
      "border": "0"
    });

    document.body.appendChild(iframe);
  })();

  // Listen to auth iframe message
  var authEvent = function(event) {
      data = event.data;
      if (data.isAuthorized === "true" && data.authorizationToken !== null) {
        AuthObj.params.isAuthorized = data.isAuthorized;
        AuthObj.params.principal = data.principal;
        AuthObj.params.description = data.description;
        AuthObj.params.authorizationToken = data.authorizationToken;
    }
  }
};

$window.addEventListener ("message", authEvent, false);

var getAuth = function() {return AuthObj;};

return {getAuth: getAuth};
});

Controller:

angular.module("container.WorkspaceCtrl", ['common.AuthDataModule'])
  .controller("WorkspaceCtrl", function ($scope, AuthDataService, $rootScope) {
    $scope.AuthObj = AuthDataService.getAuth();
});

HTML

<pre style="height:250px" class="testinject">
    {{AuthObj.params.authorizationToken | json}}
</pre>

I can debug and see that the auth obj is being returned, and if I force a $digest the view updates. This makes me think that Angular just doesn't "see" the change, so it never bothers to update the view. Am I missing something?

Était-ce utile?

La solution

From the documentation:

$rootScope.Scope#$apply([exp]);

$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). Because we are calling into the angular framework we need to perform proper scope life cycle of exception handling, executing watches.

If you look at the pseudo-code for $apply() you see that it triggers a $digest(), which is why you forcing a $digest on your own makes it work.

function $apply(expr) {
  try {
    return $eval(expr);
  } catch (e) {
    $exceptionHandler(e);
  } finally {
    $root.$digest();
  }
}

Try something like (untested):

var authEvent = function(event) {
  data = event.data;
  if (data.isAuthorized === "true" && data.authorizationToken !== null) {
    $rootScope.$apply(function () {
      AuthObj.params.isAuthorized = data.isAuthorized;
      AuthObj.params.principal = data.principal;
      AuthObj.params.description = data.description;
      AuthObj.params.authorizationToken = data.authorizationToken;
    });
  }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top