Search code examples
outlook-addinoffice-addinsoutlook-restapi

Outlook Add-in REST call error


I'm trying to mark (flag) a message using the Outlook rest API, but I keep getting error messages. I've tried with different rest URLs but it doesn't help - the errors just varies.

Important values in the manifest for allowing this I believe are:

<Requirements>
    <Sets>
      <Set Name="Mailbox" MinVersion="1.1" />
    </Sets>
  </Requirements>
...
<Permissions>ReadWriteItem</Permissions>
  <Rule xsi:type="RuleCollection" Mode="Or">
    <Rule xsi:type="ItemIs" ItemType="Message" FormType="Read" />
  </Rule>
...
<VersionOverrides xmlns="http://schemas.microsoft.com/office/mailappversionoverrides" xsi:type="VersionOverridesV1_0">
    <Requirements>
      <bt:Sets DefaultMinVersion="1.3">
        <bt:Set Name="Mailbox" />
      </bt:Sets>
    </Requirements>

Here is the part I'm trying to do that causes error:

Office.context.mailbox.getCallbackTokenAsync({ isRest: true }, function (result)
{
    if (result.status === "succeeded")
    {
        var accessToken = result.value;
        var itemId = getItemRestId();
        var restUrl = Office.context.mailbox.restUrl + "/api/v2.0/messages/" + itemId;

        var request = {
            url: restUrl,
            type: "PATCH",
            dataType: 'json',
            data: { "Flag": { "FlagStatus": "Flagged" } },
            headers: { 
                "Authorization": "Bearer " + accessToken, 
                "Conntent-Type": "application/json" 
            }
        };

        $.ajax(request)
            .done(function (item)
            {
                // dome something
            })
            .fail(function (error)
            {
                // handle error
            });
    }
    else
    {
        // handle error
    }
});

function getItemRestId()
{
    if (Office.context.mailbox.diagnostics.hostName === 'OutlookIOS')
    {
        return Office.context.mailbox.item.itemId;
    }
    else
    {
        return Office.context.mailbox.convertToRestId(
            Office.context.mailbox.item.itemId,
            Office.MailboxEnums.RestVersion.Beta
        );
    }
}

This code above will result in the error:

{"readyState":4,"responseText":"","status":404,"statusText":"Not Found"}

If I try to JSON.stringify() the data attribute of the request I get:

{"readyState":4,"responseText":"","status":404,"statusText":"Not Found"}

If I change the rest URL to (seen in older samples):

'https://outlook.office.com/api/beta/me/messages/'+ itemId;

And the headers attribute of the request to (seen in older samples):

headers: {
    'Authorization': 'Bearer ' + accessToken,
    'Content-Type': 'application/json'
}

Then I get the following error instead:

{
  "readyState": 4,
  "responseText": "{\"error\":{\"code\":\"ErrorAccessDenied\",\"message\":\"The api you are trying to access does not support item scoped OAuth.\"}}",
  "responseJSON": {
    "error": {
      "code": "ErrorAccessDenied",
      "message": "The api you are trying to access does not support item scoped OAuth."
    }
  },
  "status": 403,
  "statusText": "Forbidden"
}

Can anyone see what I'm doing wrong or missing here?

I'm debugging in Outlook 2016 and the account is Office 365.

UPDATE: Fiddler outputs

Here is the request my own sample sends (results in 403 Forbidden) Exact error: {"error":{"code":"ErrorAccessDenied","message":"The api you are trying to access does not support item scoped OAuth."}}

PATCH https://outlook.office.com/api/beta/me/messages/AAMkAGNmMDllMTVhLTI3ZDctNDYxZS05ZWM5LTA3ZWQzMzYyNDBiOABGAAAAAAD6OQOAoKyKT6R02yYFe0bIBwD5fUzv7OgQQYAILztCFSSWAALg591rAAC382lxTQ2HQpUKZsAGTeWVAARPu37CAAA= HTTP/1.1
Content-Type: application/json
Accept: application/json, text/javascript, */*; q=0.01
Authorization: Bearer <long token code removed...>
Referer: https://localhost:44394/MessageRead.html?_host_Info=Outlook$Win32$16.02$da-DK
Accept-Language: da-DK
Origin: https://localhost:44394
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: outlook.office.com
Content-Length: 33
Connection: Keep-Alive
Cache-Control: no-cache

{"Flag":{"FlagStatus":"Flagged"}}

Here is the request the demo project sends (results in 200 OK)

PATCH https://outlook.office.com/api/beta/me/messages/AAMkAGNmMDllMTVhLTI3ZDctNDYxZS05ZWM5LTA3ZWQzMzYyNDBiOABGAAAAAAD6OQOAoKyKT6R02yYFe0bIBwD5fUzv7OgQQYAILztCFSSWAALg591rAAC382lxTQ2HQpUKZsAGTeWVAARPu37CAAA= HTTP/1.1
Content-Type: application/json
Accept: application/json, text/javascript, */*; q=0.01
Authorization: Bearer <long token code removed...>
Referer: https://<company.domain.com>:1443/outlookaddindemo/RestCaller/RestCaller.html?_host_Info=Outlook$Win32$16.02$da-DK
Accept-Language: da-DK
Origin: https://<company.domain.com>:1443
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: outlook.office.com
Content-Length: 47
Connection: Keep-Alive
Cache-Control: no-cache

{
  "Flag": {
    "FlagStatus": "Flagged"
  }
}

The only difference I can see is that the 2nd request payload seems formatted for reading while data wise being identical to the previous one.

I can't seem to find the problem here - I even made sure that both projects use the same version of JQuery.


Solution

  • If you need write access to the item via REST, you need to specify ReadWriteMailbox in the Permissions element in your manifest. Despite it's name, ReadWriteItem doesn't give you a token with the proper scope. Any permission level other than ReadWriteMailbox gives an item-scoped token, and as the error says, the operation you're trying to do doesn't support item-scoped OAuth.

    See https://learn.microsoft.com/en-us/outlook/add-ins/use-rest-api for details, but here's the relevant bit:

    Add-in permissions and token scope

    It is important to consider what level of access your add-in will need via the REST APIs. In most cases, the token returned by getCallbackTokenAsync will provide read-only access to the current item only. This is true even if your add-in specifies the ReadWriteItem permission level in its manifest.

    If your add-in will require write access to the current item or other items in the user's mailbox, your add-in must specify the ReadWriteMailbox permission level in its manifest. In this case, the token returned will contain read/write access to the user's messages, events, and contacts.