Search code examples
angularjsmsal-angular

How to access and import from msalService in class, as import from (and not module)?


Got an issue in one specific file where accessing the token happens before setting the token. (I know i should/could rewrite and dig deep down to fix it, but we dont have time...)

Accessing and importing from msal works well in modules :

angular.module('app')
.controller('appCtrl', ['$scope', 'msalAuthenticationService', '$location', '$log', '$http', '$rootScope', function ($scope, msalService, $location, $log, $http, $rootScope) {
    $scope.login = function () {
        msalService.login();
    };

}]);

but in classes it doesn't allow me to import it like this :

import axios from 'axios';
import msalService from '@azure/msal-angularjs';

export class SuperDuperApiClient {
baseUrl = 'xxxxxx/api';
uiUrl = 'xxxxx';

constructor() {
  const apiAccessToken = sessionStorage.getItem("api-access-token");
  console.log("API TOKEN : ", apiAccessToken);
  if (!apiAccessToken) {
    msalService.acquireTokenSilent(['xxxxx/Api/user_impersonation']).then(function (token) {
    sessionStorage.setItem('api-access-token', token);
  })
}

So wanted result is the if check... If the token isn't set, i need to call msal to aquire the token, and set it in sessionstorage.

Error :

enter image description here

Any ideas ?


Solution

  • The imports at the top of a Javascript file is not the same as AngularJS's dependency injection system, which your controller uses. Your import is not importing the instance of the service, but rather the service class itself. To solve this, you have a couple options:

    Pass the MSAL Service as an argument of the constructor

    A simple way to do this would be to have the constructor of your SuperDuperApiClient class to accept a MSAL Service instance, and use that instance to acquire your token. This allows you to keep your class out of the AngularJS ecosystem, which helps migration if you were to move away from it in the future.

    This approach does require you to have access to a MSAL Service instance when creating the instance of your API client.

    export class SuperDuperApiClient {
        baseUrl = 'xxxxxx/api';
        uiUrl = 'xxxxx';
    
        constructor(msalService) {
            const apiAccessToken = sessionStorage.getItem("api-access-token");
            console.log("API TOKEN : ", apiAccessToken);
            if (!apiAccessToken) {
                msalService.acquireTokenSilent(['xxxxx/Api/user_impersonation']).then(function (token) {
                    sessionStorage.setItem('api-access-token', token);
                })
            }
        }
    }
    

    Make SuperDuperApiClient AngularJS-aware

    If you are fine with adding more dependency to AngularJS, you could wrap your class in an AngularJS service, which would give it access to its dependency injection system. This would allow you to access it in the same way you are in your controller.

    function SuperDuperApiClientService(msalService) {
        class SuperDuperApiClient {
            baseUrl = 'xxxxxx/api';
            uiUrl = 'xxxxx';
    
            constructor() {
                const apiAccessToken = sessionStorage.getItem("api-access-token");
                console.log("API TOKEN : ", apiAccessToken);
                if (!apiAccessToken) {
                    msalService.acquireTokenSilent(['xxxxx/Api/user_impersonation']).then(function (token) {
                        sessionStorage.setItem('api-access-token', token);
                    });
                }
            }
        }
        
        return SuperDuperApiClient;
    }
    
    angular.module('someModule')
        .service('SuperDuperApiClient', SuperDuperApiClientService);