5
votes

Using the Microsoft Graph API I was able to get a list of all users in our Azure Active Directory tenant and determine if they have a profile picture. I wanted to then take the list of users without a photo and upload one for them, but the API returns a 403 error even though the account I'm using has full access to all of the user accounts and the application is setup with full permissions to the Graph API.

using (HttpClient client = new HttpClient())
{
    client.BaseAddress = new Uri("https://graph.microsoft.com/");
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("image/jpeg"));
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", oauthToken);

    // HTTP GET                
    HttpResponseMessage response = await client.PatchAsync($"v1.0/users/{emailAddress}/photo/$value", byteContent);
    if (!response.IsSuccessStatusCode)
    {
        throw new Exception("Error!");
    }
}

403 FORBIDDEN

Is it not possible to do this using the Graph API or am I missing a permission somewhere?

The SCP value is: Calendars.Read Calendars.ReadWrite Contacts.Read Contacts.ReadWrite Directory.AccessAsUser.All Directory.Read.All Directory.ReadWrite.All email Exchange.Manage Files.Read Files.Read.Selected Files.ReadWrite Files.ReadWrite.AppFolder Files.ReadWrite.Selected full_access_as_user Group.Read.All Group.ReadWrite.All Mail.Read Mail.ReadWrite Mail.Send MailboxSettings.ReadWrite Notes.Create Notes.Read Notes.Read.All Notes.ReadWrite Notes.ReadWrite.All Notes.ReadWrite.CreatedByApp offline_access openid People.Read People.ReadWrite profile Sites.Read.All Tasks.Read Tasks.ReadWrite User.Read User.Read.All User.ReadBasic.All User.ReadWrite User.ReadWrite.All

2
Can you please enter your token at jwt.calebb.net and give us the value for "scp" field. This is the list of permissions in your token. Thanks. - Venkat Ayyadevara - MSFT
Hi Venkat, I added the SCP attribute value to the original post since it was too long to enter as a comment. - Adam Curtis
Not sure that the operation works with PATCH. It should be a PUT. However I also repro the same issue (running as an admin user) and here is the trace. The scopes contain user.readwrite.all which should be enough to update another user's photo. { "error": { "code": "ErrorAccessDenied", "message": "Access is denied. Check credentials and try again.", "innerError": { "request-id": "23559b24-d5e9-4dae-8311-94f706320b4b", "date": "2016-04-09T06:21:56" } } } - Dan Kershaw - MSFT
how do you get the token ? from a clientid + clientsecret ? - Thomas
Hi Dan, I was using PATCH because the Graph documentation mentions using a PATCH method, but I also tried it using PUT and it didn't work either. I'm beginning to think the API just doesn't allow updating another user's photo. - Adam Curtis

2 Answers

10
votes

First of all, you should take a look at the new Microsoft Graph SDK, released during //Build 2016 Here is the Github of Microsoft Graph SDK : https://github.com/microsoftgraph
Here is a full sample I ve created, using it : https://github.com/Mimetis/NextMeetingsForGraphSample

For your question, here are two methods I've written, which worked for me : I assume, you have a method to get a valid access token.

using (HttpClient client = new HttpClient())
{
    var authResult = await AuthenticationHelper.Current.GetAccessTokenAsync();
    if (authResult.Status != AuthenticationStatus.Success)
        return;

    client.DefaultRequestHeaders.Add("Authorization", "Bearer " + authResult.AccessToken);

    Uri userPhotoEndpoint = new Uri(AuthenticationHelper.GraphEndpointId + "users/" + userIdentifier + "/Photo/$value");
    StreamContent content = new StreamContent(image);
    content.Headers.Add("Content-Type", "application/octet-stream");

    using (HttpResponseMessage response = await client.PutAsync(userPhotoEndpoint, content))
    {
        response.EnsureSuccessStatusCode();
    }
}

If you use the Microsoft Graph SDK, it will be really straightforward :)

GraphServiceClient graphService = new GraphServiceClient(AuthenticationHelper.Current);
var photoStream = await graphService.Users[userIdentifier].Photo.Content.Request().PutAsync(image); //users/{1}/photo/$value

Seb

1
votes

The documentation is pretty clear about updating a photo of an other user.

To update the photo of any user in the organization, your app must have the User.ReadWrite.All application permission and call this API under its own identity, not on behalf of a user. To learn more, see get access without a signed-in user.

This means you'll need an access token for the application, and not the token you get if the user accesses your application. This page shows how to get an application access token. This also means you have to grant your application the application permissions, which is often forgotten.

Once you get the token you can check it on https://jwt.ms to see the claims in the token explained.

You're talking about the scp claim in the token. That is only there if you have a user token, not an application token.