1
votes

I have a NodeJS app that has been successfully using the Microsoft Graph API beta for the past year to interact with Teams and Drive (mostly uploading files and posting messages to channels). Within the past two weeks, the Graph API has started returning 403 (Forbidden) responses about half of the time when requesting write operations to Drive or Teams endpoints. The app uses delegated permissions and the resource-owner password credentials grant flow to acquire a token to make requests (Postman code example below):

var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
myHeaders.append("Host", "login.microsoftonline.com");

var urlencoded = new URLSearchParams();
urlencoded.append("client_id", "xxxxx");
urlencoded.append("scope", "user.read mail.read openid profile offline_access");
urlencoded.append("grant_type", "password");
urlencoded.append("username", "xxxx");
urlencoded.append("password", "xxxx");
urlencoded.append("client_secret", "xxxxx");

var requestOptions = {
  method: 'POST',
  headers: myHeaders,
  body: urlencoded,
  redirect: 'follow'
};

fetch("https://login.microsoftonline.com/organizations/oauth2/v2.0/token", requestOptions)
  .then(response => response.text())
  .then(result => console.log(result.access_token))
  .catch(error => console.log('error', error));

This will successfully return a token and list the granted permissions for the app:

{
    "token_type": "Bearer",
    "scope": "ChannelMessage.Send Chat.Read Chat.ReadWrite Files.Read.All Files.ReadWrite.All Group.Read.All Group.ReadWrite.All Mail.Read Member.Read.Hidden People.Read People.Read.All profile Sites.Read.All Sites.ReadWrite.All User.Read User.Read.All User.ReadBasic.All User.ReadWrite openid email",
    "expires_in": 3599,
    "ext_expires_in": 3599,
    "access_token": "xxx",
    "refresh_token": "xxx",
    "id_token": "xxx"
}

I can then take this token to make requests, such as the following, which will post a message to a Teams channel:

var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", "Bearer xxxxx");

var raw = "{\n  body: { \n      content: '<h1>This is a test</h1><h3>More stuff here</h3>',\n   contentType: 'html'\n  }\n}";

var requestOptions = {
  method: 'POST',
  headers: myHeaders,
  body: raw,
  redirect: 'follow'
};

fetch("https://graph.microsoft.com/v1.0/teams/xxxxx/channels/xxxxx/messages", requestOptions)
  .then(response => response.text())
  .then(result => console.log(result))
  .catch(error => console.log('error', error));

Half the time this will work and half the time it fails with a 403 response:

{
    "error": {
        "code": "Forbidden",
        "message": "Forbidden",
        "innerError": {
            "date": "2020-09-20T17:15:54",
            "request-id": "60e2a57a-6b44-4039-be4f-636debd10f87",
            "client-request-id": "60e2a57a-6b44-4039-be4f-636debd10f87"
        }
    }
}

In Graph Explorer, you can see that the needed permissions are granted, but the request still fails:

Graph explorer error

I have tried performing the operations in JS, the Java SDK, and in Postman, and I always run into the same problems. I have also tried both the beta and v1.0 APIs, with no noticeable difference. Is the issue related to my user authentication flow or my token session? Do I have conflicting or missing permissions in Azure AD? I'm stumped.

2
What is the user's role in this Teams? Is it a member or owner of this Teams? For a user who has no access to this Teams channel, he is not allowed to send messages in channel.Allen Wu
@AllenWu: The user account used is a member of every team in our organization. As I mentioned, this user has been able to successfully post to teams without issues until about 2 weeks ago.woemler
It seems that this is not a universal problem. It may be related to your environment or the back-end O365 server. You can contact Azure support to provide the request-id for further investigation.Allen Wu
@stealththeninja If so, I guess it should be 429 error.Allen Wu
If you cannot contact the support team, it is difficult to troubleshoot the problem from the source. I can think of two points: 1. Change a user to call the request, and check if it is the user's problem. 2. Register a new Azure AD app, add only the minimum permissions required, and then try to call this request.Allen Wu

2 Answers

1
votes

I would diagnose your problem with the following steps:

1. Check license

You probably already checked this, but are you sure your user (for the resource owner password flow) has a teams license assigned?

2. Check the token

I always use https://jwt.ms to inspect the token, go ahead and paste the token there (no worries, it's a Microsoft owned website that only parses the token locally)

Your access token should at least have the ChannelMessage.Send scope and possibly the Group.ReadWrite.All scope (see docs).

The audience should be https://graph.microsoft.com

3. Fix token

If your token isn't correct, you should try to change the "give me an access token" request.

My suggestions:

  • You aren't requesting a token with the correct scope(s), so I guess you should add the ChannelMessage.Send to the original request. Not sure, but I think it only gives you a token with the scopes requested even though you granted more access.
  • You're requesting a token at the organizations endpoint, but for getting access tokens it might be that you need the tenant specific endpoint.

4. Try in the explorer

  1. List teams /v1.0/me/joinedteams
  2. Pick a team and list channels /v1.0/teams/xxxx-xxxx-xxxx-xxxx/channels
  3. Pick a channel and send a message POST /v1.0/teams/xxxx-xxxx-xxxx-xxxx/channels/{channelId}/messages
{
    "body":{
        "content":"Hello channel, from graph explorer"
    }
}

Results in: enter image description here

5. Open support request

On a public forum we cannot look into the specific problem, but if you followed the steps above, and you're sure the token is correct (you're getting the correct scopes and audience), but still get the forbidden error. You should create a support question at the azure portal. And provide them the body of the 403 error. They should be able to diagnose the problem by searching for the request-id.

0
votes

You may have changed your API permissions after giving your application admin consent. When you give admin consent, Azure AD will take a "snapshot" of the permissions at the time of consent. Then if you change the permissions later, you will need to re-do the admin consent process again.

Try typing

https://login.microsoftonline.com/common/adminconsent?client_id={YOUR-APP-ID} into your browser window to prompt the admin consent for your application again. For more details refer to https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#request-the-permissions-from-a-directory-admin