I want to obtain a list of my shifts from the Microsoft Teams 'Shifts' App using NodeJS.
I believe the Microsoft Teams Shifts app comes as standard with Microsoft Teams.
The shifts I want to obtain are for a specific 'Schedule' (although there is only one available at the moment).
As an example, lets use the shift name of "Main Shift".
The filter settings I use to view the current shifts in Microsoft Teams are as follows:
The shifts look like this when filtered:
I registered a new Azure AD App in my portal.azure.com.
This app is called microsoft-teams-shifts
and has a secret called microsoft-teams-shifts-secret
.
This application has the relevant 'Microsoft Graph API/ Permissions':
I have followed the documentation on the Microsoft website for authenticating and communicating with the Microsoft Graph API: https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-nodejs-console
But I every time I try to use the access token I am given, I keep receiving a 403 - Unauthorised error, stating:
{
error: {
code: 'Forbidden',
message: "Missing role permissions on the request. API requires one of 'Schedule.Read.All, Schedule.ReadWrite.All'. Roles on the request ''.",
innerError: {
date: '2023-04-14T08:00:04',
'request-id': 'xxxx',
'client-request-id': 'xxxx'
}
}
}
My authentication code looks like this (hardcoded values for now while I test):
// auth.js
const msal = require('@azure/msal-node');
// Taken from:
// portal.azure.com -> AD -> App Registrations -> 'microsoft-teams-shifts' -> Application (client ID)
const CLIENT_ID = 'xxxx';
// Taken from:
// 'microsoft-teams-shifts' -> Certificates & secrets -> 'microsoft-teams-shifts-secret' (the actual secret value, not the ID of it)
const CLIENT_SECRET = 'xxxx';
// Taken from:
// portal.azure.com -> AD -> App Registrations -> 'microsoft-teams-shifts' -> Directory (tenant) ID
const TENANT_ID = 'xxxxx';
const config = {
auth: {
clientId: CLIENT_ID,
clientSecret: CLIENT_SECRET,
authority: `https://login.microsoftonline.com/${TENANT_ID}`,
},
};
const cca = new msal.ConfidentialClientApplication(config);
// Function to acquire a token
async function getToken() {
const tokenRequest = {
scopes: ['https://graph.microsoft.com/.default'], // Include the required scope(s) here
};
try {
const authResult = await cca.acquireTokenByClientCredential(tokenRequest);
return authResult.accessToken;
} catch (error) {
console.log(error);
}
}
module.exports = getToken;
And I use this token to make a request to the graph API:
const axios = require('axios');
const getToken = require('./auth'); // Import the getToken function from auth.js
async function fetchData() {
try {
const accessToken = await getToken(); // Call the getToken function to get the access token
// Make the API call with Axios and pass the access token and required request headers
// The Team ID is my Microsoft Teams 'team' ID
const response = await axios.get('https://graph.microsoft.com/v1.0/teams/myTeamIDHere/schedule', {
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'roles': 'Schedule.Read.All', // Include the required role permission
},
});
// Process the response
const data = response.data;
console.log(data);
} catch (error) {
console.log(error.response.data.error);
}
}
fetchData();
This exact request works perfectly fine via the Graph Explorer, with the same URL endpoint.
When I compare the auth token I get from my NodeJS code with the one I can see in the Graph Explorer, I can see that the:
aid
figures are different (but that kind of makes sense) Theaid
in my access token corresponds with the CLIENT_ID I specified in my auth code. The one given to me by Graph Explorer has a different one presumably because it's an app of it's own on MS's side.tid
is exactly the same in both JWTs- The
wids
array is different though, there is a different value in the one Microsoft Graph generates. Are these supposed to match and that's why my code doesn't work?
Can anyone spot where I am going wrong with this?