Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Microsoft Graph API - Update password - Insufficient privileges to complete the operation

I am trying to update a user via Microsoft Graph API, I am able to update the DisplayName but the PasswordProfile I get an error:

Insufficient privileges to complete the operation.

Here are the roles associated to the token when I decoded the JWT token at http://jwt.io :

"roles": [
    "User.ReadWrite.All",
    "Directory.ReadWrite.All",
    "Group.ReadWrite.All"
],

Based on the documentation it seems these permissions should suffice.

Here is my code (taken from a console app), I was able to figure out the call is failing via Fiddler, the UpdateAsync does not throw an exception.

try
{
    var userId = "9a5413cd-85ff-4ad1-ab2f-b443941abd8e";
    var token = GetToken().Result;
    System.Console.Write($"Token: {token}");

    var newPassword = "TwDx5zgHxe51DZZ";
    GraphServiceClient graphClient = GetAuthenticatedClient(token);

    // This works -- Updating Display name
    graphClient.Users[userId].Request().UpdateAsync(new User
    {
        DisplayName = "NewDisplayName"
    });

    // This does not work - Updating password
    graphClient.Users[userId].Request().UpdateAsync(new User
    {
        PasswordProfile = new PasswordProfile
        {
            Password = newPassword,
                ForceChangePasswordNextSignIn = true
        }
    });
    System.Console.WriteLine("---Update Complete---");
}
catch (Exception e)
{
    System.Console.WriteLine(e);
}

Method for getting token:

public async Task<string> GetToken()
{
    //  Constants
    var tenant = "dev-mytenantmydomaincom";
    var resource = "https://graph.microsoft.com/";
    var clientID = "XXXXXXXX-87ef-494d-b921-cf8956006b0e";
    var secret = "zgkzas2THJLiD5XXXXXX";

    //  Ceremony
    var authority = $"https://login.microsoftonline.com/{tenant}";
    var authContext = new AuthenticationContext(authority);
    var credentials = new ClientCredential(clientID, secret);
    var authResult = await authContext.AcquireTokenAsync(resource, credentials);
    return authResult.AccessToken;
}

Here is the full response via Fiddler:

HTTP/1.1 403 Forbidden
Cache-Control: private
Transfer-Encoding: chunked
Content-Type: application/json
request-id: 6edcf194-7705-4cd7-8144-767925cc9ee4
client-request-id: 6edcf194-7705-4cd7-8144-767925cc9ee4
x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"East US","Slice":"SliceB","ScaleUnit":"001","Host":"AGSFE_IN_27","ADSiteName":"EST"}}
Duration: 69.2849
Date: Thu, 31 Aug 2017 13:15:34 GMT

{
    "error": {
        "code": "Authorization_RequestDenied",
            "message": "Insufficient privileges to complete the operation.",
                "innerError": {
            "request-id": "6edcf194-7705-4cd7-8144-767925cc9ee4",
                "date": "2017-08-31T13:15:34"
        }
    }
}    
like image 850
TheWebGuy Avatar asked Aug 31 '17 13:08

TheWebGuy


People also ask

How do I grant access to Microsoft Graph API?

Go to the app's API permissions page. Select Add a permission and then choose Microsoft Graph in the flyout. Select Delegated permissions. Use the search box to find and select the required permissions.

What is Microsoft Graph API permissions?

Allows the app to manage permission grants for application permissions to any API (including Microsoft Graph) and application assignments for any app, on behalf of the signed-in user. Allows the app to manage delegated permission grants for any API (including Microsoft Graph), on behalf of the signed-in user.

How do I grant permissions in Azure API?

Select Azure Active Directory > App registrations, and then select your client application. Select API permissions > Add a permission > Microsoft Graph > Application permissions.

How do I grant access to Microsoft Graph Explorer?

You can consent to permissions in Graph Explorer by choosing either the Modify permissions tab or the Select permissions option in the settings gear next to your profile when you're signed in. The Modify permissions tab lists all the permissions that you need to run the query in the address bar.


3 Answers

Passwords are a particularly sensitive data set and therefore have some unique permissions to them. From the documentation:

When updating the passwordProfile property, the following scope is required: Directory.AccessAsUser.All.

The Directory.AccessAsUser.All is a Delegated Permission that requires an Admin. In other words, it allows someone a Global Administrator to change other another user's passwordProfile.

If you're looking to allow the end user to change their password themselves, there is also a baked in ChangePassword method in the SDK:

await graphClient.Me.ChangePassword("current-pwd, "new-pwd").Request().PostAsync();

Note: that this also requires that Admin Consent be granted for DirectoryAccessAsUser.All before a user can execute it)

Keep in mind that DirectoryAccessAsUser.All is a "Delegated" rather than an "Application" permission scope. This means it is only supported by the Authorization Code and Implicit flows; it will not work for daemon/service scenarios using the Client Credentials flow.

If you consider the potential exploits that could be achieved by a non-interactive application having the ability to change user's passwords at will, the reason for this restriction is pretty clear.

like image 127
Marc LaFleur Avatar answered Oct 03 '22 16:10

Marc LaFleur


An easy solution we found out is to add the application principal to the "Helpdesk administrator" role.

  1. Go to Azure Active Directory
  2. On the left click on Roles and administrators
  3. Search for the Helpdesk administrator role and click on it

enter image description here

  1. Click on Add assignments and paste your application object id
  2. Wait 5 minutes or so for azure to take the changes into account

enter image description here

like image 43
sashok_bg Avatar answered Oct 03 '22 16:10

sashok_bg


For anyone arriving at this Q&A in 2021 - there is a password reset endpoint in Graph:

POST /users/{id | userPrincipalName}/authentication/passwordMethods/{id}/resetPassword

You will have to retrieve the id of the password authentication method first.

The permission needed for this operation is UserAuthenticationMethod.ReadWrite.All which can be granted as an Application type permission.

See the documentation: https://docs.microsoft.com/en-us/graph/api/passwordauthenticationmethod-resetpassword

Update

Even though the permission can be granted as an Application type permission, the call will not succeed in Application Context. See https://docs.microsoft.com/en-us/answers/questions/246207/34upn-from-claims-with-value-null-is-not-a-valid-u.html

like image 28
mvreijn Avatar answered Oct 03 '22 16:10

mvreijn