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:
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.