question

AnandP-8228 avatar image
0 Votes"
AnandP-8228 asked RamjotSingh commented

How can Azure app subscribe to Team chat messages not part of the same Azure account/ tenant?

Objective: Subscribe a listener application to receive all ‘chat message’ events in the team/ channel for which the subscription has been created (or renewed).
Reference:
https://docs.microsoft.com/en-us/graph/api/subscription-post-subscriptions?view=graph-rest-1.0&tabs=javascript#example

We are using the Graph Subscribe API to read all Team chat messages, our understanding is that we would need to create an Azure app for this (as opposed to a plain-vanilla Teams Bot app using Bot API). Questions:
a) Does the Microsoft authorization hierarchy allow an Azure app to read Team chat messages from Microsoft accounts that are not part of the same Azure account/ tenant? What are the pre-requisites to do so?
b) Can such an app be published to the Teams marketplace? We would prefer this to improve the discoverability of the app across all organizations that use Teams, including those that do not have an Azure account.
c) Graph Subscribe API to read all Team chat messages requires the subscription to be renewed every 60 minutes. This makes the solution complex and prone to breakdown. Is there an alternate solution using which the subscription can be configured to be in non-expiry mode?

Error message we are getting while subscribing with POST - https://graph.microsoft.com/beta/subscriptions:
Operation: Create; Exception: [Status Code: Forbidden; Reason: Required permissions to access tenant-wide channel message subscription ('ChannelMessage.Read.All') is missing.]
This is when using the Bearer token generated from the common endpoint that we used with our Organizational account.
We also tried to give app roles like: https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps
but it doesn't work either.

office-teams-app-devmicrosoft-graph-teamwork
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi @AnandP-8228,

Please kindly understand under Teams tag, we mainly focus on general issues about Teams desktop client. Considering that your issue may be more related to API development, I would remove teams tag from your thread. Thanks for your understanding and hope your issue would be resolved soon.

0 Votes 0 ·

@AnandP-8228 - Hope this issue is resolved with the answer provided by Ramjot.
Could you please accept the answer if it is resolved.

0 Votes 0 ·
RamjotSingh avatar image
2 Votes"
RamjotSingh answered RamjotSingh commented

a) Does the Microsoft authorization hierarchy allow an Azure app to read Team chat messages from Microsoft accounts that are not part of the same Azure account/ tenant? What are the pre-requisites to do so?
You can use multi-tenant app. Refer to https://docs.microsoft.com/en-us/azure/active-directory/develop/single-and-multi-tenant-apps

b) Can such an app be published to the Teams marketplace? We would prefer this to improve the discoverability of the app across all organizations that use Teams, including those that do not have an Azure account.
You can use Graph in any app. However, looking at your scenario of listening to an entire tenant's messages means that Graph app will be working behind the scenes. For example, listening to messages, performing analytics on them and then delivering value through another Teams app which will be published to store.

c) Graph Subscribe API to read all Team chat messages requires the subscription to be renewed every 60 minutes. This makes the solution complex and prone to breakdown. Is there an alternate solution using which the subscription can be configured to be in non-expiry mode?
We intend to extend the expiration but it will never be non-expiry mode due to security reasons.

Adding roles in the app is not enough. You need admin of the tenant you intend to use the app in to consent to your app. Please refer to https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/grant-admin-consent

· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Hi Ramjot,
Thanks for your point-wise responses. We have followed https://docs.microsoft.com/en-us/azure/active-directory/develop/single-and-multi-tenant-apps
However our understanding is that it is related to user authentication across multiple tenants. What we are looking for is to allow our Azure app from our tenant to access MS Teams account of another tenant.
Thanks.

0 Votes 0 ·

Both work in same way. An app spanning across tenant makes it work for users and tenant as a whole.

0 Votes 0 ·
AnandP-8228 avatar image
0 Votes"
AnandP-8228 answered AnandP-8228 commented

Hi Ramjot,
Thanks for the pointers shared so far; we are able to successfully read chat messages from the Graph Subscription API, across tenants.

However, the ID of the user who posted the chat message is not available in the chat message payload. This is required for our app to respond to this user (privately). Refer to the Graph Subscription payload below as opposed to payload of messages with RSC using Teams Bot API.

Payload using Graph Subscription API (does NOT contain user identifier):

     {
       value: [
         {
           subscriptionId: 'ae--------------',
           changeType: 'created',
           clientState: null,
           subscriptionExpirationDateTime: '2021-09-23T04:08:45-07:00',
           resource: "teams('9d----------------')/channels('19:a-----------@thread.tacv2')/messages('16------')/replies('16-------')",
           resourceData: {
             id: '16----------',
             '@odata.type': '#Microsoft.Graph.chatMessage',
             '@odata.id': "teams('9d--------------')/channels('19:a---------@thread.tacv2')/messages('16-----')/replies('16---------')"
           },
           encryptedContent: {
             data: 'qVuJB/PkQaKAbqp+zpxTm0hsQiDGUemanHuW1Y',
             dataSignature: 'JM---------------',
             dataKey: 'mtg--------------------',
             encryptionCertificateId: '------',
             encryptionCertificateThumbprint: 'B---------------------'
           },
           tenantId: '8-------------------'
         }
       ],
       validationTokens: [
         'eyJ0eXAiOiJ----------------'
       ]
     }

Payload with RSC using Teams Bot API (contains user identifier):

 TurnContext {
   _respondedRef: { responded: false },
   _turnState: TurnContextStateCollection [Map] {
     Symbol(BotIdentity) => ClaimsIdentity { claims: [Array], authenticationType: true },
     Symbol(ConnectorClient) => ConnectorClient {
       _withCredentials: false,
       _httpClient: [AxiosHttpClient],
       _requestPolicyOptions: [RequestPolicyOptions],
       _requestPolicyFactories: [Array],
       baseUri: 'https://smba.trafficmanager.net/in/',
       requestContentType: 'application/json; charset=utf-8',
       credentials: [MicrosoftAppCredentials],
       attachments: [Attachments],
       conversations: [Conversations]
     },
     Symbol(OAuthScope) => 'https://api.botframework.com',
     'botCallbackHandler' => [AsyncFunction],
     turn: { locale: 'en-GB' }
   },
   _onSendActivities: [],
   _onUpdateActivity: [],
   _onDeleteActivity: [],
   _turn: 'turn',
   _locale: 'locale',
   bufferedReplyActivities: [],
   _adapter: BotFrameworkAdapter {
     middleware: MiddlewareSet { middleware: [Array] },
     BotIdentityKey: Symbol(BotIdentity),
     ConnectorClientKey: Symbol(ConnectorClient),
     OAuthScopeKey: Symbol(OAuthScope),
     TokenApiClientCredentialsKey: Symbol(TokenApiClientCredentials),
     settings: {
       appId: '42--------------------',
       appPassword: '0------------------',
       channelService: undefined,
       openIdMetadata: undefined
     },
     credentials: MicrosoftAppCredentials {
       refreshingToken: null,
       appId: '42-------------------------',
       _tenant: 'botframework.com',
       _oAuthEndpoint: 'https://login.microsoftonline.com/botframework.com',
       authenticationContext: [AuthenticationContext],
       _oAuthScope: 'https://api.botframework.com',
       tokenCacheKey: '42-------------------https://api.botframework.com-cache',
       appPassword: '0-------------------------'
     },
     credentialsProvider: SimpleCredentialProvider {
       appId: '42-------------------',
       appPassword: '0----------------4'
     },
     isEmulatingOAuthCards: false,
     authConfiguration: AuthenticationConfiguration {
       requiredEndorsements: [],
       validateClaims: undefined
     },
     turnError: [AsyncFunction]
   },
   _activity: {
     text: '<at>converseBot2-local-debug</at> intro \n',
     textFormat: 'plain',
     attachments: [ [Object] ],
     type: 'message',
     timestamp: 2021-09-23T10:52:07.986Z,
     localTimestamp: 2021-09-23T10:52:07.986Z,
     id: '16**********',
     channelId: 'msteams',
     serviceUrl: 'https://smba.trafficmanager.net/in/',
     from: {
       id: '29:1mOwl*************************',
       name: 'FNU LNU',
       aadObjectId: 'd4*******************'
     },
     conversation: {
       isGroup: true,
       conversationType: 'channel',
       tenantId: '8f*****************',
       id: '19:a********************@thread.tacv2;messageid=16********'
     },
     recipient: {
       id: '28:42******************2',
       name: 'converseBot2-local-debug'
     },
     entities: [ [Object], [Object] ],
     channelData: {
       teamsChannelId: '19:a*******************@thread.tacv2',
       teamsTeamId: '19:0*******************@thread.tacv2',
       channel: [Object],
       team: [Object],
       tenant: [Object]
     },
     locale: 'en-GB',
     localTimezone: 'Asia/Calcutta',
     rawTimestamp: '2021-09-23T10:52:07.9862382Z',
     rawLocalTimestamp: '2021-09-23T16:22:07.9862382+05:30',
     callerId: 'urn:botframework:azure'
   }
 }


· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

You need to decrypt the encryptedContent to get the full content. Please refer to https://docs.microsoft.com/en-us/graph/webhooks-with-resource-data?context=graph%2Fapi%2F1.0&view=graph-rest-1.0 on how to decrypt.

0 Votes 0 ·

Thanks Ramjot. We are able to decrypt the chat message and read the content. The issue is that there is no identifier indicating which Teams user posted the message on the channel, hence we are not able to respond back to that user (privately). Our use case does not allow us to respond back publicly on the same channel.

0 Votes 0 ·
RamjotSingh avatar image
0 Votes"
RamjotSingh answered RamjotSingh commented

You need to decrypt the encryptedContent to get the full content. Please refer to https://docs.microsoft.com/en-us/graph/webhooks-with-resource-data?context=graph%2Fapi%2F1.0&view=graph-rest-1.0 on how to decrypt.

· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Thanks Ramjot. We are able to decrypt the chat message and read the content. The issue is that there is no identifier indicating which Teams user posted the message on the channel, hence we are not able to respond back to that user (privately). Our use case does not allow us to respond back publicly on the same channel.

0 Votes 0 ·

Once you decrypt you should message in this schema https://docs.microsoft.com/en-us/graph/api/resources/chatmessage?view=graph-rest-1.0 which has a from field in it. Can you share the payload you got after decrypting (please remove all sensitive fields).

0 Votes 0 ·