1

I want to send a proactive message with a team bot. For that I:

  • created a bot in the bot framework
  • created an app with AppStudio in teams
  • assigned the bot to my app
  • assigned the app to my team

Now I would like to send a request to this Url:

https://smba.trafficmanager.net/de/v3/conversations/{teamId}/activities/

to send a message to the channel. For this I have to get a Bearer Token with this Token Url:

https://login.microsoftonline.com/botframework.com/oauth2/v2.0/token

I get this back without any problems.

Now I want to use the GraphAPI to execute a custom request

First of all I have to create a GraphServiceClient like this:

OAuth2AuthenticationProvider authProvider = new OAuth2AuthenticationProvider(_getBotToken(botClientId, botClientSecret, BOT_SCOPE));
    graphBotClient = GraphServiceClient.builder().authenticationProvider(authProvider).buildClient();

After I created the client I do the request.

graphBotClient.customRequest("https://smba.trafficmanager.net/de/v3/conversations/" + pTeamId + "/activities").buildRequest().post(messageAsJson);

I need to use a custom query because there are no methods for it in Java. messageAsJson is a Json object that looks like an activity object, which is needed to write messages.

{
    "type": "message",
    "from": {
        "id": "{clientBotIdFromAzure}",
        "name": "Botname"
    },
    "conversation": {
        "id": "{teamId}",
        "name": "ChannelName"
   },
    "text": "My bot's reply"
}

When I execute this command I get the following exception:

401 : Unauthorized
Strict transport security : max-age=31536000
Cache control : private
x-ms-ags-diagnostic : {"ServerInfo":{"DataCenter": "West Europe", "Slice": "SliceC", "Ring": "5", "ScaleUnit": "002", "RoleInstance": "AGSFE_IN_102"}}
client-request-id : 9a7197en-dd0f-4c90-8b2b-8dc5bb1200ee
WWW-Authenticate : Bearer realm="", authorization_uri="https://login.microsoftonline.com/common/oauth2/authorize", client_id="00000003-0000-0000-c000-000000000000"
request-id : b07b1399-7175-40d3-9891-266abe1144b9
Content-Length : 262
Date : Wed, 22 Apr 2020 08:24:28 GMT
Content-Type : application/json
{
  "error." {
    "code." "InvalidAuthenticationToken"
    "message": "Access token validation failure. Invalid audience."
    "innerError": {
      "request-id": "b07b1399-7175-40d3-9891-266abe1144b9"
      "date": "2020-04-22T08:24:29"
    }
  }
}

Exception in thread "main" com.microsoft.graph.http.GraphServiceException: Error code: InvalidAuthenticationToken
Error message: Access token validation failure. Invalid audience.

If I execute the same request in Postman, with the same token, url and body, the request works fine and my message appears in MSTeams Message sent out of Postman appears in MSTeams

Why does it not work with the GraphAPI? Where is my mistake? What am I missing?

Many greetings

  • In my experience, `Invalid audience` means the access token is for a incorrect resource. I notice that you generate a token for `botframework.com`. But anyway, you are calling Microsoft Graph API, so you should provide a access token for `https://graph.microsoft.com`. – Allen Wu Apr 23 '20 at 01:30
  • Thanks for your answer @AllenWu. Yeah, I generate the token for botframework, because I have to.. the token I get from botframework allows me to send messages into my channel. Other tokens doesn't. I thought that when I build a custom request and filling in the right Url it works fine, because I don't call the graph endpoint.. thought I can use the API for something else with a little trick. But it seems it doesn't work – Martin Bilda Apr 23 '20 at 07:36
  • I'm not sure if you can make Microsoft Graph custom calls to call other resources than Graph. But it seems to be not supported: https://github.com/microsoftgraph/msgraph-sdk-java/wiki/Custom-Requests. Anyway, you need a token to call Microsoft Graph API, but the resource of the token is botframework rather than Microsoft Graph. This sounds unreasonable. If you don't call Microsoft Graph resource, don't use Microsoft Graph calls. – Allen Wu Apr 23 '20 at 09:38

1 Answers1

1

I think the issue might be the mix of authentication against the bot framework itself versus an authentication against the Microsoft Graph - they're not a complete 1-1, I don't think.

That said, it's much easier to use the bot framework directly - it has a mechanism for proactive messages as well. You can create a ConversationContext instance and interact with that. To do this, you do need to have certain information, in particular a conversation id, serviceurl, and so on, which you need to get and store beforehand. There are multiple ways to get this info, but the easiest is to save this information any time a user sends your bot a message (every message will have these bits of info) and then just save it in a database/blob/whatever.

I've got some more detail on this over here: Programmatically sending a message to a bot in Microsoft Teams

Here's also a Node example: Sending Proactive Messages from Azure functions to botservice - node

Of course, don't forget to check out the docs on this for more info: https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-howto-proactive-message?view=azure-bot-service-4.0&tabs=javascript

Hilton Giesenow
  • 9,809
  • 2
  • 10
  • 24
  • Thank you for your answer @Giesenow. In my scenario I want to send notifications via the bot to a teams channel, so not directly to a user. Therefor I have no serviceurl and conversation id for a specific user. But the conversation Id for a channel, specificly for the "general" channel is the teamId, so I have that one. The serviceurl looks like it is always the same: https://smba.trafficmanager.net/de/v3/ isn't it? Nonetheless you might be right with the first sentence and it just don't work. – Martin Bilda Apr 22 '20 at 10:35
  • 1
    The concept is the same for all cases (user 1-1, group chat, and channel), so just use the conversation ID you have (which in this case would be a channel conversation id). Note that the ServiceUrl is NOT always the same - it depends on the tenant and possibly even on the user location, so it's best to save it and use it later, is the guidance from Microsoft. – Hilton Giesenow Apr 22 '20 at 11:29
  • How can I find out the ServiceUrl without the user having to send a message to the bot first? As I mentioned before, in my scenario the bot is supposed to push notifications into a channel. If you only get the ServiceUrl by a user writing a message to the bot, then my plan would not be very efficient. – Martin Bilda Apr 22 '20 at 12:47
  • 1
    That's no problem - when the bot is added to the channel (or group chat or even 1-1), it will receive a "conversationUpdate" event, indicating that a new user (in this case the bot itself) has been added to the conversation. This event, just like a normal message from a user, contains serviceurl, conversationid, etc. See here for more: https://learn.microsoft.com/en-us/microsoftteams/platform/resources/bot-v3/bots-notifications#team-member-or-bot-addition – Hilton Giesenow Apr 22 '20 at 13:17
  • You said: "use the bot framework directly - it has a mechanism for proactive messages as well. You can create a ConversationContext instance and interact with that" - is this available for Java? Where can I find it? – Martin Bilda Apr 22 '20 at 13:47
  • I imagine it would be, but not totally sure how. How have you written your bot in the meantime? Is there a java sdk of some sort? If Node code would help, you can look here: https://learn.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages?tabs=javascript and https://learn.microsoft.com/en-gb/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages?tabs=dotnet . There's C#, Node, and Python in there as examples if they help? There are also samples apps in C# and Node on Github from MS – Hilton Giesenow Apr 22 '20 at 15:27