AzureAD / azure-activedirectory-library-for-js

The code for ADAL.js and ADAL Angular has been moved to the MSAL.js repo. Please open any issues or PRs at the link below.
https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/maintenance/adal-angular
Apache License 2.0
627 stars 372 forks source link

adal-angular.js Token renewal failed due to timeout & The user or administrator has not consented to use the application #686

Closed DevarshiDave closed 6 years ago

DevarshiDave commented 6 years ago

Hello, my app is using adal-angular.js to communicate with MS Graph API, I am using the requireADLogin to call the login page when my app loads, after getting logged in successfully, i am redirected back to my app with the id_token appended to my url, logged the userInfo object and it is getting the correct data, after login i am calling the graph api on a button click, but i am receiving the "Token renewal failed due to timeout" error always after the default timeout (6s)...even in the incognito window...i log in and not a single time my app can call graph api. this has been really frustrating for me and i spent days researching and viewing other similar issues. I have checked a few things by reading some other issues.

I am attaching the failed response.

Please help. Thank you. capture

rohitnarula7176 commented 6 years ago

@DevarshiDave Can you please attach the fiddler trace for the above scenario so I can confirm whether you are getting a response for the acquireToken request.

DevarshiDave commented 6 years ago

Hi @rohitnarula7176 , first of all thank you for your quick response, i am attaching two fiddler files, the larger one has all the request logs of my application (from authentication to the failed request of graph API), and the smaller one has the only request logs of Graph API request. Please let me know if you need anything else, i am also willing to share my code with you if needed, its just a simple project i created using "yo generator for office" (outlook add-in)

fiddler.zip

rohitnarula7176 commented 6 years ago

@DevarshiDave The trace shows that you are sending duplicate prompt parameters for the acquireToken call (AADSTS90004: The request is not properly formatted. The parameter 'prompt' is duplicated) . One is prompt = admin_consent which is provided by you through the adal config and the other is prompt=none which is added by the library. By design, the prompt parameter is not configurable by the user. AcquireToken happens in a hidden iframe and its prompt value cannot be anything other than none. In the latest version 1.0.16 we added code to remove the prompt parameter if provided by the user through extraQueryParameter in the adal config before making any api calls. Can you please try the latest version and see if it solves your issue.

DevarshiDave commented 6 years ago

@rohitnarula7176 thanks for your feedback, i have updated my adal library and that resolved the above issue "Token Renewal failed", but i am now having another error, i have removed the extraQueryParameter from my code (FYI the error stays same if add the extraQueryParameter). I have also done "Grant Permissions" in the azure portal. Can you please advise on what is wrong? capture1

rohitnarula7176 commented 6 years ago

@DevarshiDave You need to consent for the app once using admin account by calling acquireTokenRedirect which will try to acquire the token using a UI to allow you to consent for the app. You can use the coding structure below to first catch the error you attached above using acquireToken and then call acquireTokenRedirect.

adal.acquireToken(resource, function(errorDesc, token, error){
    if(errorDesc){
        adal.acquireTokenRedirect(resource);
    }
});
DevarshiDave commented 6 years ago

@rohitnarula7176 Thank you again,i am using adal-angular...and i have used the requireADLogin: true in my default route, so everything is being handled by adal-angular...where should i put this code ? or do i need to manually use the acquireToken method?

DevarshiDave commented 6 years ago

I am using my admin account, i have done something as per your suggestion in my graph api call, i have placed a catch() which catches this error and inside that i have placed

adalAuthenticationService.acquireTokenRedirect(adalAuthenticationService.config.clientId);

Now after doing a redirect i am getting an access_token appended in my redirected page, but when i call the api after that, i still get the same error, so my app again does acquireTokenRedirect() and it keeps doing this in circles.

also tried using adalAuthenticationService.acquireTokenPopup(adalAuthenticationService.config.clientId);

DevarshiDave commented 6 years ago

@rohitnarula7176 any updates on my queries?

wy193777 commented 6 years ago

@DevarshiDave How did you initialize adal on your project to get a token that could be used for Graph API?

DevarshiDave commented 6 years ago

@wy193777 i have used the same approach given in the documentation https://github.com/AzureAD/azure-activedirectory-library-for-js

You need to create an App in Azure Active Directory (https://portal.azure.com) and in Microsoft office dev center (https://apps.dev.microsoft.com) <-- this app's appId will be used in our endpoints array which is passed in to initialize the adal.

give permission to your azure app to access graph apis also change the oauth2AllowImplicitFlow to true in the manifest file of your azure app

below is the my code to initialize adal in angularjs .config(). You'll need to inject the adalAuthenticationServiceProvider.

adalAuthenticationServiceProvider.init({
        // clientId is the identifier assigned to your app by Azure Active Directory.
        clientId: "xxx-xxx-xxx-xxx",
        endpoints: {
          "https://graph.microsoft.com/": "xxx-xxx-xxx-xxx"
        },
        extraQueryParameter: 'prompt=admin_consent',
        redirectUri: location.origin,
        cacheLocation: 'localStorage'
      }, $httpProvider);

later to get the token, you simply need to pass requireADLogin: true to your route where you want authentication, or you can manually call the login() and logout() function of adalAuthenticationService passed down to your controller.

below is an example of my route definition where i have used requireADLogin,

 $routeProvider
        .when("/", {
          controller: "LoginController",
          templateUrl: "/views/login.html",
          requireADLogin: true
        })
        .when("/home", {
          controller: "HomeController",
          templateUrl: "/views/home.html"
        });

as you can see i have used the requireADLogin: true attribute in my root route so my app will first try to login the user then render the root view.

Once the user is logged in, your token will be stored in sessionStorage or localStorage depending on your choice, in my app i have used localStorage.

and if you have noticed that we are passing the $httpProvider object in our adal initialization code, so adal will append all the necessary data (token) on an outgoing request. so you simply need to do an http request in your code, and that's it.

$scope.getGroups = function () {      
      $http.defaults.useXDomain = true;
      delete $http.defaults.headers.common['X-Requested-With'];

      $http.get('https://graph.microsoft.com/v1.0/groups?$orderby=displayName',{
        headers: { Accept: "application/json;odata.metadata=minimal" }
      }).then(function (res) {
        $scope.result = res;
      });
    }

Hope this helps!

wy193777 commented 6 years ago

@DevarshiDave Thanks a lot. But I guess this isn't works for my case. I want to get a token that could be passed to backend so the backend service could represent the user to query graph.