msgraph-sdk-javascript icon indicating copy to clipboard operation
msgraph-sdk-javascript copied to clipboard

Subscription creation to `/communications/onlineMeetings...` fails.

Open mhashmi opened this issue 2 years ago • 4 comments

Bug Report

Prerequisites

  • [x] Can you reproduce the problem?
  • [x] Are you running the latest version?
  • [x] Are you reporting to the correct repository?
  • [x] Did you perform a cursory search?

For more information, see the CONTRIBUTING guide.

Description

We are developing a multi-tenant application to monitor online meetings and call records for MS Teams. To this end, we have an Office 365 developer account (E5 license) to play the role of a client, and our own Azure account with a configured client application.

The client application requests the following application scopes (not delegated scopes):

onlineMeetings.Read.All
callRecords.Read.All

The client app is additionally set as a Multi-Tenant app, with implicit and hybrid access token flows checked.

When we attempt to create a subscription to the resource /communicatons/callRecords, the subscription works correctly for our client-app, and we receive callbacks on our webhook.

However, when we attempt to create a subscription to the the resource /communications/onlineMeetings/?$filter=JoinWebUrl eq '{JoinWebUrl}', we get an error message stating: The meeting token does not match the tenant token.

Console Errors: The following error is shown in the terminal:

GraphError: Operation: Create; Exception: [Status Code: Forbidden; 
Reason: The meeting tenant does not match the token tenant.]

Steps to Reproduce

  1. Sign up for a Microsoft 365 Developer account, login to Azure Portal and note the tenant Id down. Let's call this Tenant-A-ID
  2. Sign up for an Azure account for the client app (TENANT-B) a. Add an app registration for the client app b. Add application scopes for OnlineMeetings.Read.All and CallRecords.Read.all c. Generate a secret key and note it down d. Ensure Implicit and Hybrid OAuth flows are enabled e. Add a redirect URL for OAuth under SPA
  3. Open the following URL as an admin user in a browser (or GET via Postman):
https://login.microsoftonline.com/{TENANT-A-ID}/adminconsent
?client_id=YOUR_APP'S_CLIENT_ID
&state=some-state-string
&redirect_uri=http://your-registered-redirect-url-from-step-2e
  1. Ensure a TENANT-A admin account logs in and consents to the requested permissions
  2. Login to the Azure Portal for TENANT-A with your admin account
  3. Navigate to Azure AD, click on Enterprise Applications, and your TENANT-B client app should be listed
  4. Click on Permissions and ensure Admin consent has been granted for the requested scopes

MSAL initialization and MS Graph calls:

  1. Initialize the msal-node client as follows (also reference supporting documentation):
const authApp = new ConfidentialClientApplication({
  auth: {
    clientId: 'app-client-id',
    clientSecret: 'app-client-secret',
    authority: `https://login.microsoftonline.com/${TENANT-A-ID}`,
  },
});
  1. Request an access token as follows:
const authContext = await authApp.acquireTokenByClientCredential({
  authority: `https://login.microsoftonline.com/${TENANT-A-ID}`,
  scopes: ['https://graph.microsoft.com/.default'],
});

const accessToken = authContext.accessToken;
  1. Initial the MS Graph client as follows:
const client = MSClient.init({
  debugLogging: true,
  authProvider: (done) => {
    done(null, accessToken);
  },
});
  1. Attempt to create a subscription to /communication/callrecords as follows:
const callRecordsSubscription = await client
  .api('/subscriptions')
  .version('beta')
  .create({
    changeType: 'created,updated',
    notificationUrl: `https://your-ngrok-url`,
    resource: '/communications/callrecords',
    clientState: 'some-state',
    expirationDateTime: 'date-time',
});

The subscription should get created successfully, and your registered webhook will fire notifications correctly (this is the case with my testing).

  1. Attempt to create a subscription to /communications/onlineMeetings/?$filter=JoinWebUrl eq '{JoinWebUrl}' as follows:
const subscription = await client
  .api('/subscriptions')
  .version('beta')
  .create({
    resource: `/communications/onlineMeetings/?$filter=JoinWebUrl eq '{JoinWebUrl}'`,
    changeType: 'created,updated',
    notificationUrl: `https://your-ngrok-url`,
    clientState: 'some-state',
    expirationDateTime: 'date-time',
    includeResourceData: true,
    encryptionCertificate: 'serialized-cert',
    encryptionCertificateId: 'cert-id',
});

The request will fail with the console error noted above.

Expected behavior: The subscription should get created.

Actual behavior: Client throws an error with the message: The meeting tenant does not match the token tenant.

Additional Context

As a test, I created a client application in TENANT-A (no multi-tenancy involved), and received the exact same error message, whereas the subscription to /communications/callRecords again did not have any issues.

Usage Information

  • Request ID: 226feda8-d956-47f7-87ed-43ca16eafb32
  • SDK Version: 3.0.2
  • [x] Node
  • Node Version - 14.8.1

mhashmi avatar Aug 11 '22 10:08 mhashmi

@mhashmi Can you please provide the request id of the failing request and the subscription id?

nikithauc avatar Aug 12 '22 06:08 nikithauc

@nikithauc Please find below additional details from the error console:

statusCode: 403,
code: 'ExtensionError',
requestId: '226feda8-d956-47f7-87ed-43ca16eafb32',
date: 2022-08-11T09:50:47.000Z,
body: {
    code: "ExtensionError",
    message: "Operation: Create; Exception: [Status Code: Forbidden; Reason: The meeting tenant does not match the token tenant.]",
    innerError: {
      date: "2022-08-11T09:50:47",
      request-id: "226feda8-d956-47f7-87ed-43ca16eafb32",
      client-request-id: "9d772272-b2d6-6d68-8c13-7d2b1ea7ef58"
    }
  }

Please note that I don't have a subscription id here as subscription creation fails. If there is anything else I can provide please do let me know, and many thanks for looking into this, greatly appreciated!

mhashmi avatar Aug 12 '22 10:08 mhashmi

Can you try the following sugggestion by the service team ?

Can the customer add application permissions 'OnlineMeetings.ReadWrite.All' and try again?

The client application requests the following application scopes (not delegated scopes):

onlineMeetings.Read.All

callRecords.Read.All

nikithauc avatar Aug 12 '22 21:08 nikithauc

Can the customer add application permissions 'OnlineMeetings.ReadWrite.All' and try again?

Just added the additional scope: OnlineMeetings.ReadWrite.All. The screenshot below shows granted application permissions in Tenant-A (as described in the issue overview):

Screen Shot 2022-08-13 at 7 12 39 PM

Unfortunately, the console error still remains the same. Please find request details below for reference:

"statusCode": "403",
"code": "'ExtensionError",
"requestId": "'45dc7cf7-59bd-4bae-ba34-79d1f69a2ed5",
"date": "2022-08-13T14:11:52.000Z",
"body": {
  "code": "ExtensionError",
  "message": "Operation: Create; Exception: [Status Code: Forbidden; Reason: The meeting tenant does not match the token tenant.]",
  "innerError": {
    "date": "2022-08-13T14:11:52",
    "request-id": "45dc7cf7-59bd-4bae-ba34-79d1f69a2ed5",
    "client-request-id": "c610e787-0d35-3802-d292-64aeba3dbfb2"
  }
}

If I can help further please do let me know. Thanks.

mhashmi avatar Aug 13 '22 14:08 mhashmi

Root Cause:

Response from the service team: After the POST Request to https://plat.teams.microsoft.com/communications/subscriptions, the code will check for consistency between the tenant ID extracted from the joinWebUrl and the tenant ID from the token principal. In the customer's request, a placeholder value was used for joinWebUrl and as the result there is no tenant ID from the joinWebUrl. That's why the error message says " The meeting tenant does not match the token tenant."

Action:

Please replace the placeholder value for joinWebUrl with an actual meeting url. The join URL for the meeting is included in the joinWebUrl property of the onlineMeeting resource, or in the Teams client for a meeting.

nikithauc avatar Aug 16 '22 23:08 nikithauc

Thanks for the clarification -- my understanding of the purpose of the joinWebUrl was wrong.

Please consider the issue resolved given I am sure the subscription will work if I specify a joinWebUrl.

If possible, can you outline how I would get callbacks to a webhook for any meeting a particular user joins? Is this even possible? I see delegated scopes available for onlineMeetings.Read, but I am unsure what the correct subscription resource URL should be.

mhashmi avatar Aug 17 '22 05:08 mhashmi