Search code examples
c#asp.net-coreasp.net-core-mvcmicrosoft-graph-api

Patch Request for Password Change in Graph API gives 403: Forbidden


I am trying to reset the password of a native AAD User using below code

HttpResponseMessage createResponse = new();
RequestBodyPwReset requestBodyPWReset = new ()
{
    passwordProfile = new PasswordProfile()
    {
        forceChangePasswordNextSignIn = true,
        password = newPassword
    }
};
string jsonObj = JsonConvert.SerializeObject(requestBodyPWReset);
string uri = Constants.UrlToCreateLocalUserInAad + "/"+userAadId;

var httpRequestMessage = new HttpRequestMessage
{
    Method = HttpMethod.Patch,
    RequestUri = new Uri(uri),
    Headers =
            {
                { HttpRequestHeader.Accept.ToString(), "application/json" }
            },
    Content = new StringContent(jsonObj, Encoding.UTF8, "application/json")
};
httpRequestMessage.Headers.Add("Authorization", token);

try
{
    createResponse = new HttpClient().Send(httpRequestMessage);
    if (createResponse != null && createResponse.IsSuccessStatusCode)
    {
        string createResponseData = createResponse.Content.ReadAsStringAsync().Result;
        return "OK";
    }
    else
    {
        return "Sorry, try again. " + createResponse.ReasonPhrase + "---------- " + createResponse.RequestMessage.ToString();
    }
}
catch (Exception ex)
{
    string exMsg = ex.Message ?? "";
    string exInner = ex.InnerException == null ? "" : ex.InnerException.Message ?? "";
    return "Sorry, try again.";
}

The json request for my code translates as below. I have obtained the access-token by using my AAD Tenant Id and Client Id of my Enterprise application. I am sending the obtained access token with the request headers to serve my purpose.

{
    Method: PATCH, RequestUri: 'https://graph.microsoft.com/v1.0/users/8e91ee04-0e5d-4628-b555-f8caca9e4a27', Version: 1.1, Content: System.Net.Http.StringContent, Headers:
  {
      Accept: application/json
      Authorization: Bearer eyJ0exxxxxxxxxxxxxxxxxxxxxxxxxxxx
      traceparent: 00-a7e3149c95c5f61e53c154d57eabd0cd-12e7097ec88ffaaa-00
      Content-Type: application/json; charset=utf-8
      Content-Length: 81
  }
}

This is the response I am getting

{
    StatusCode: 403, ReasonPhrase: 'Forbidden', Version: 1.1, Content:             System.Net.Http.HttpConnectionResponseContent, Headers:
    {
        Cache-Control: no-cache
        Transfer-Encoding: chunked
        Strict-Transport-Security: max-age=31536000
        request-id: 24908338-9c31-4d96-9828-6427d7d1d938
        client-request-id: 24908338-xxxx-xxxx-xxxx-6427d7d1d938
        x-ms-ags-diagnostic: {"ServerInfo":{"DataCenter":"South   India","Slice":"E","Ring":"2","ScaleUnit":"002","RoleInstance":"MA1PEPF0000273D"}}
        x-ms-resource-unit: 1
        Date: Thu, 06 Jul 2023 09:52:04 GMT
        Content-Type: application/json
    }, Trailing Headers:
{
}}

Any clues where I am going wrong or what I may do differently to achieve this ?


Solution

  • Your access token contains the User.ReadWrite.All application permission, and it alone is not enough according to documentation for updating a user: https://learn.microsoft.com/en-us/graph/api/user-update?view=graph-rest-1.0&tabs=http#request-body.

    Quoting from docs for passwordProfile property:

    In application-only access, the calling app must be assigned the User.ReadWrite.All application permission and at least the User Administrator Azure AD role.

    So your application needs to be assigned a directory role as well. This can be done through Roles and Administrators -> User Administrator -> Add assignments.

    Do note this will not be enough to reset passwords for global administrators. If you needed that, the application would need a higher role.