1

I am trying to create a dropdown with all the users in my Office365 tenant. I created an app in Azure AD and gave it all the necessary permissions. I gave it all the permissions for Microsoft Graph actually, app and delegated. All of them.

Then I wrote up my script to query all users with https://graph.microsoft.com/v1.0/users.

I had my tenant admin go in and accept the permissions then output the list of users in the UI. Works fine for the admin

I'm not an admin but when I go to the page I get the following error:

This application requires application permissions to another application. Consent for application permissions can only be performed by an administrator. Sign out and sign in as an administrator or contact one of your organization's administrators.

I need to know if this will work for users with even lower permissions. From what I understand the API request and the App is running under the permissions given to the application in Azure. So even if the user as Read Only, the request isn't running under the user, it's running under the Application I set up. So why would I get the error regarding permissions?

This is the code I'm using:

(function () { "use strict"; // Some samples will use the tenant name here like "tenant.onmicrosoft.com" // I prefer to user the subscription Id var subscriptionId = "metenant.onmicrosoft.com"; // Copy the client ID of your AAD app here once you have registered one, configured the required permissions, and // allowed implicit flow https://msdn.microsoft.com/en-us/office/office365/howto/get-started-with-office-365-unified-api var clientId = "cccb1f2f-xxx-x-xxxxx-x-x-x-x-x-"; window.config = { // subscriptionId: subscriptionId, clientId: clientId, postLogoutRedirectUri: window.location.origin, endpoints: { graphApiUri: 'https://graph.microsoft.com' }, cacheLocation: 'localStorage' // enable this for IE, as sessionStorage does not work for localhost. }; var authContext = new AuthenticationContext(config); // Check For & Handle Redirect From AAD After Login var isCallback = authContext.isCallback(window.location.hash); authContext.handleWindowCallback(); if (isCallback && !authContext.getLoginError()) { window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST); } // If not logged in force login var user = authContext.getCachedUser(); // NOTE: you may want to render the page for anonymous users and render // a login button which runs the login function upon click. if (!user) authContext.login(); // Acquire token for Files resource. authContext.acquireToken(config.endpoints.graphApiUri, function (error, token) { // Handle ADAL Errors. if (error || !token) { console.log('ADAL error occurred: ' + error); return; } // Execute GET request to Files API. var filesUri = config.endpoints.graphApiUri + "/v1.0/users"; $.ajax({ type: "GET", url: filesUri, headers: { 'Authorization': 'Bearer ' + token, } }).done(function (response) { console.log('Successfully fetched from Graph.'); console.log(response); var container = $(".container") container.empty(); $.each(response.value, function(index, item) { container.append($('<li>').text(item.displayName + " " + item.mail + " " + item.mobilePhone)) }) }).fail(function (response) { var err = JSON.parse(response.responseText) console.log('Failed:', err.error.message); }); }); })(); 
2
  • Looks like you are passing the current logged in user's token to Graph API. You can give proper permission to a service principal in your tenant and then acquire a service principal token and use that for the Graph call. Commented Sep 27, 2016 at 19:09
  • If I'm using ADAL and the clientID for the app shouldn't I be passing in the App login to the Graph API and not the current user? I've added the code I'm using to do this Commented Sep 27, 2016 at 19:41

1 Answer 1

2

There are two kinds of permission/scope for Microsoft Graph. One is that require administrator’s consent. The other is not required.

What’s the permission you were config for this app? To list the users without administrator’s consent, we can use the scope User.ReadBasic.All like figure below: enter image description here

You can get more detail about the permission/scope from here.

Modify:

At present, the adal.js doesn’t provide the admin consent. If you want to use this feature, you can modify the code to add a prameter like below:

AuthenticationContext.prototype.login = function (prompt) { // Token is not present and user needs to login var expectedState = this._guid(); this.config.state = expectedState; this._idTokenNonce = this._guid(); this._logstatus('Expected state: ' + expectedState + ' 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, 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); if (prompt && prompt === "admin_consent") { urlNavigate = urlNavigate + "&prompt=admin_consent" } this.frameCallInProgress = false; this._loginInProgress = true; if (this.config.displayCall) { // User defined way of handling the navigation this.config.displayCall(urlNavigate); } else { this.promptUser(urlNavigate); } // callback from redirected page will receive fragment. It needs to call oauth2Callback }; 

And if you were using Angular, we also need to modify the adal-angular.js:

this.$get = ['$rootScope', '$window', '$q', '$location', '$timeout', function ($rootScope, $window, $q, $location, $timeout) { ... return { // public methods will be here that are accessible from Controller config: _adal.config, login: function (prompt) { _adal.login(prompt); }, ... } 

Then we can provide two button for users login in. One button is for the users sign-in with themselves. And the other is for admin to give the consent for the organization. Here is the code redirect to the login page for the admin consent in the control of Angular:

$scope.login = function () { adalService.login("admin_consent"); }; 
Sign up to request clarification or add additional context in comments.

5 Comments

I was giving it all the permissions because I was under the impression that the API request would be running under the App context, not the user context. It seems like I was wrong. The token sent through ADAL reflects the users permissions. Which is why it was failing because the user needed to be an admin since the app had permissions that required admin. When I give it only permissions that aren't admin it works fine.
Also, I've noticed that every time a user logs into the page where ADAL is running, it asks them to accept the App. But I thought if the admin approved the application it wouldn't behave like that. Any ideas?
It the permission of token depends which flow you acquire it. For the client credential, the token delegate the app permission without users sign-in. In your scenario, the token is delegate-token which require the users have the sufficient permission to give the consent to the app. For the second issue, if you want the admin approved the application for the organization, we need to specify it in the request explicitly at the first time. To achieve this it require to add the parameter prompt=admin_consent.
Where would I add that parameter in my javascript?
The adal.js doesn't support it at present, however we can modify the source code. I have updated the code in post.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.