2
votes

I'm trying to write a web portal that users can use to reset their own Azure AD password. Because of the requirements of my client, the Azure AD SSPR is not an option.

To achieve this I'm using Microsoft Graph. According to the documentation, it is possible to reset a users password using Microsoft Graph if you have User.ReadWrite.All or Directory.AccessAsUser.All permissions.

Then the permissions documentation, the remarks it states that even if you have the Directory.ReadWrite.All permissions you won't be able to reset a users password.

I've done a test to see if this will work but I get an HTTP 403 Forbidden response.

The code I'm using is:

string ResourceUrl = "https://graph.windows.net/";
string AuthorityUrl = "https://login.microsoftonline.com/companyxxx.onmicrosoft.com/oauth2/authorize/";

//Create a user password cradentials.
var credential = new Microsoft.IdentityModel
    .Clients
    .ActiveDirectory
    .UserPasswordCredential("[email protected]", "passwordxxx");

// Authenticate using created credentials
var authenticationContext = new AuthenticationContext(AuthorityUrl);

var authenticationResult = authenticationContext
    .AcquireTokenAsync(ResourceUrl, "xxxxxxxx-3017-4833-9923-30d05726b32f", credential)
    .Result;

string jwtToken = authenticationResult.AccessToken;
var cred = new Microsoft.Rest
    .TokenCredentials(authenticationResult.AccessToken, "Bearer");

HttpClient client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
queryString["api-version"] = "1.6";
client.DefaultRequestHeaders
    .Accept
    .Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", jwtToken);

var uri = "https://graph.windows.net/xxxxxxxx-18fe-xxxx-bb90-d62195600495/users/xxxxxxxx-aa58-4329-xxxx-b39af07325ee?" + queryString;

//var content = new StringContent("{\"passwordProfile\": {\"password\": \"Test123456\", \"forceChangePasswordNextLogin\": true }}");
var response = client.PatchAsync(new Uri(uri), content, jwtToken);

The PatchAsync method is an extension method as below:

public static class HttpClientExtensions
{
    public static async Task<HttpResponseMessage> PatchAsync(this HttpClient client,
        Uri requestUri, HttpContent iContent, string jwtToken)
    {
        var method = new HttpMethod("PATCH");
        var request = new HttpRequestMessage(method, requestUri)
        {
            Content = iContent,
        };
        request.Content.Headers.ContentType =
            new MediaTypeHeaderValue("application/json");

        request.Headers.Authorization =
            new AuthenticationHeaderValue("Bearer", jwtToken);

        HttpResponseMessage response = new HttpResponseMessage();
        try
        {
            response = await client.SendAsync(request);
        }
        catch (TaskCanceledException e)
        {
            Console.WriteLine("ERROR: " + e.ToString());
        }

        return response;
    }
}

Could someone please clarify if this is possible using the credentials grant flow with a username and password for authentication. If so how do I achieve this?

1
Have you checked if the JWT contains the necessary scope? You can use a tool like jwt.ms for decoding the token.juunas

1 Answers

2
votes

You're mixing up Microsoft Graph and Azure AD Graph API. These are two different APIs and calls to one are not interchangeable with the other.

You are correct in that you need to use the Directory.AccessAsUser.All scope for this activity. This scope allows the API to do anything to the AAD that the signed in user would be able to do themselves (i.e. change their own password).

Once you have a valid access_token for the user with Directory.AccessAsUser.All permission, you can update the user's passwordProfile:

PATCH https://graph.microsoft.com/v1.0/me
Content-type: application/json

{
  "passwordProfile" : {
    "forceChangePasswordNextSignIn": true,
    "password": "password-value"
  }
}