Resource 'GUID value here' does not exist or one of its queried reference-property objects are not present
C

1

1

I'm trying to change an Azure AD user password.

The user is already authenticated in a SPA application using the implicit flow and the adal library.

While calling:

return await graphClient.Me.Request().UpdateAsync(new User
                {
                    PasswordProfile = new PasswordProfile
                    {
                        Password = userPasswordModel.NewPassword,
                        ForceChangePasswordNextSignIn = false
                    },
                });

I'm getting this exception:

{"Code: Request_ResourceNotFound\r\nMessage: Resource '35239a3d-67e3-4560-920a-2e9ce027aeab' does not exist or one of its queried reference-property objects are not present.\r\n\r\nInner error\r\n"}

Debugging the access token I got with Microsoft Client SDK I see that this GUID is referring to the oid and sub properties. See below:

enter image description here

This is the code I use to acquire the token:

 IConfidentialClientApplication clientApp =
     ConfidentialClientApplicationBuilder.Create(Startup.clientId)
            .WithAuthority(string.Format(AuthorityFormat, Startup.tenant))
            .WithRedirectUri(Startup.redirectUri)
            .WithClientSecret(Startup.clientSecret)
            .Build();

            var authResult = await clientApp.AcquireTokenForClient(new[] { MSGraphScope }).ExecuteAsync();

            return authResult.AccessToken;

I'm using the implicit flow in a SPA application. While doing ClaimsPrincipal.Current I see that my user is authenticated and all claims are present.

I've read a lot of docs @ GitHub and Microsoft Docs but it's still not clear in my mind how to implement this. By the way, I'm using these Microsoft Graph packages:

  <package id="Microsoft.Graph" version="1.15.0" targetFramework="net461" />
  <package id="Microsoft.Graph.Auth" version="0.1.0-preview.2" targetFramework="net461" />
  <package id="Microsoft.Graph.Core" version="1.15.0" targetFramework="net461" />

I guess I'm not approaching this correclty because instead of acquiring a token for the application I should acquire a token for the user. Should I use clientApp.AcquireTokenOnBehalfOf instead?

If so what's the recommended way of acquiring a token for the currently logged in user using Microsoft Graph API SDK?

Can you shed some light?

####### EDIT #######

I was able to make some progress using this:

var bearerToken = ClaimsPrincipal.Current.Identities.First().BootstrapContext as string;

JwtSecurityToken jwtToken = new JwtSecurityToken(bearerToken);

var userAssertion = new UserAssertion(jwtToken.RawData, "urn:ietf:params:oauth:grant-type:jwt-bearer");

IEnumerable<string> requestedScopes = jwtToken.Audiences.Select(a => $"{a}/.default");

var authResult = clientApp.AcquireTokenOnBehalfOf(new[] { MSGraphScope }, userAssertion).ExecuteAsync().GetAwaiter().GetResult();

return authResult.AccessToken;

but now I'm getting the error:

insufficient privileges to complete the operation. authorization_requestdenied

Commence answered 11/6, 2019 at 15:21 Comment(4)
Just after posting this question, I found this github.com/AzureAD/microsoft-authentication-library-for-dotnet/…Commence
I'm a little confused. You mention using the Implicit OAuth grant but the token you're getting contains Roles rather than scp. Roles are Applications copes (Client Credentials) which implies you're not using Implicit here. What you want is Implicit using the Directory.AccessAsUser.All scope.Kelton
Just to clarify, Application scopes (roles) cannot be used to reset user passwords. Only Delegated scopes support resetting passwords. This ensures that any password reset can be audited back to a specific user ("a throat to choke" as they say).Kelton
@MarcLaFleur Thanks for stopping by and for the clarification. I resorted to using the original clientApp.AcquireTokenForClient instead of clientApp.AcquireTokenOnBehalfOf. Posted an answer below.Commence
C
5

After a long debugging session (8 hours or so) I was finally able to get what I wanted after I saw this answer by @Michael Mainer.

This is the "right" code I put together:

public async Task<User> ChangeUserPassword(UserPasswordModel userPasswordModel)
{
    try
    {
        var graphUser = ClaimsPrincipal.Current.ToGraphUserAccount();

        var newUserInfo = new User()
        {
            PasswordProfile = new PasswordProfile
            {
                Password = userPasswordModel.NewPassword,
                ForceChangePasswordNextSignIn = false
            },
        };

        // Update the user...
        return await graphClient.Users[graphUser.ObjectId].Request().UpdateAsync(newUserInfo);
    }
    catch(Exception e)
    {
        throw e;
    }
}

Note 1: graphClient.Users[graphUser.ObjectId] is being used instead of graphClient.Me

Note 2: .ToGraphUserAccount() is from Microsoft.Graph.Auth.

I had a sample PATCH request in Postman that correctly set a new password for the user.

enter image description here

The Access Token used in Postman's Authorization request-header had the same format\properties from the one I was acquiring with Microsoft Graph API. I just compared them using jwt.io. So I must've been calling something wrongly...

I used clientApp.AcquireTokenForClient instead:

var authResult = await clientApp.AcquireTokenForClient(new[] { MSGraphScope }).ExecuteAsync();

return authResult.AccessToken;

where:

MSGraphScope = "https://graph.microsoft.com/.default"
Commence answered 12/6, 2019 at 19:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.