I am building a Teams chat-bot that looks at the history of messages in the current chat/channel whilst in conversation with the user.
My bot has been granted all the RSC (Resource-Specific Content) Permissions it needs (see image below)
Here is the relevant parts of the manifest:
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.11/MicrosoftTeams.schema.json",
"version": "1.0.0",
"manifestVersion": "1.11",
"id": "bd33f8b1-b593-433c-926e-44a27c1bd94a",
...
"permissions": [
"identity",
"messageTeamMembers"
],
...
"bots": [
{
"botId": "e6d93739-a8ab-412d-a4f6-b6f514a3451a",
"scopes": [
"team",
"personal",
"groupchat"
],
"isNotificationOnly": false,
"supportsFiles": true
}
],
"validDomains": [],
"webApplicationInfo": {
"id": "e6d93739-a8ab-412d-a4f6-b6f514a3451a",
"resource": "https://RscBasedStoreApp",
"applicationPermissions": [
"TeamSettings.Read.Group",
"ChannelMessage.Read.Group",
"TeamSettings.Edit.Group",
"ChannelSettings.ReadWrite.Group",
"Channel.Create.Group",
"Channel.Delete.Group",
"TeamsApp.Read.Group",
"TeamsTab.Read.Group",
"TeamsTab.Create.Group",
"TeamsTab.ReadWrite.Group",
"TeamsTab.Delete.Group",
"Member.Read.Group",
"Owner.Read.Group",
"ChatSettings.Read.Chat",
"ChatSettings.ReadWrite.Chat",
"ChatMessage.Read.Chat",
"ChatMember.Read.Chat",
"Chat.Manage.Chat",
"TeamsTab.Read.Chat",
"TeamsTab.Create.Chat",
"TeamsTab.Delete.Chat",
"TeamsTab.ReadWrite.Chat",
"TeamsAppInstallation.Read.Chat",
"OnlineMeeting.ReadBasic.Chat",
"Calls.AccessMedia.Chat",
"Calls.JoinGroupCalls.Chat",
"TeamsActivity.Send.Chat"
]
}
}
Note: the bot has permission to read messages in chats and channels. Specifically, my problem affects chats and not channels (which I can get messages from fine).
In order to do this, I get a JWT token for the bot account, accessing the Graph API like so:
GraphServiceClient<?> gsc = GraphServiceClient.builder()
.authenticationProvider(u -> mac.getToken())
.buildClient();
Next, I am using the Graph API to pull back these messages. For messages in channels I can do:
gsc.teams("some group id")
.channels("team id")
.messages()
.buildRequest(Collections.emptyList()).get()));
This works fine.
For chats, I am doing something like:
gsc.chats("29:13qY8hmfkJinH9-v7rYKjCNFHYFJXKbjqR-NyzyKzL694npelHJoq5HrVtqJLRYo79OYeHGQq-bhtJM5N-yKXyQ")
.messages()
.buildRequest().get()));
However, this time I get an error from the Graph API:
[Some information was truncated for brevity, enable debug logging for
more details] com.microsoft.graph.http.GraphServiceException: Error
code: Forbidden Error message: Invoked API requires Protected API
access in application-only context when not using Resource Specific
Consent. Visit
https://learn.microsoft.com/en-us/graph/teams-protected-apis for more
details.
GET
https://graph.microsoft.com/v1.0/chats/29:13qY8hmfkJinH9-v7rYKjCNFHYFJXKbjqR-NyzyKzL694npelHJoq5HrVtqJLRYo79OYeHGQq-bhtJM5N-yKXyQ/messages
SdkVersion : graph-java/v5.6.0
I am at a loss to explain why querying channels works fine but querying chats fails.
Any help gratefully appreciated!
This is a protected API and in order to use it you will first need to make a formal request to Microsoft Graph, asking for permissions to use the API without any user interaction
Here is the list of protected APIs. You need to fill this form to get the required permissions.
To request access to these protected APIs, complete the following
request form. We review access requests every Wednesday and deploy
approvals every Friday, except during major holiday weeks in the U.S.
Submissions during those weeks will be processed the following
non-holiday week.
The other option would be to use delegated flow.
Related
I'm trying to Share Files on a SharePoint Document Library that I have as a part of an Office 365 Developer Program instance.
My AD has a variety of users, some "native" users created in the AD manually and the rest are "guests" from different domains that my team and I work for.
I'm executing the following API request on the graph via code using NestJs (as per snippet). I've all the required Delegated Permissions in the Application Registration to do everything too.
REST View:
POST /drives/{drive-id}/items/{item-id}/invite
{
"requireSignIn": true,
"sendInvitation": false,
"roles": [
"sp.full control"
],
"recipients": [
{
"email": "xxx.xxx#xxx.com"
}
]
}
Code View:
//build list of all to add: PL, PLB, Main, Current User and whatever is added in DTO
const participantsToAdd = [project.projectLead]
.concat(project.projectLeadBackup)
.concat(project.participants.filter(p => newRoles.includes(p.participantRole.name)).map(p => p.user))
.map(u => ({
oid: u.microsoftId,
mail: u.mail,
}));
const permission = {
recipients: participantsToAdd.map(p => ({ email: p.mail })),
requireSignIn: true,
sendInvitation: false,
roles: ['sp.full control'],
};
// add the right permissions to the file
const result = await client.api(`/drives/${this.libraryId}/items/${fileId}/invite`).post(permission);
The above code is building up a list of "User" objects which contain an "oid" which I use later, and a "mail" object. I give these users "sp.full control" role on a file. Some are granted direct access and others are given links (grantedToIndentities) with write permissions.
This only seems to be happening when Guests on the active directory make the request; though it's only occurring for some guests. Two guest users in particular that I grant access to are fine, they get "Direct Access". Others go into the "link sharing" category. I don't see any differences in the users in AD anywhere.
I've tried looking through all admin sites (SharePoint, M365) and tweaked External Sharing permissions but the problem still persists.
When I invoke the action from a "native" user on AD to the Graph using the same request, it all works fine. All users (native and guests) are added with "direct access".
Can anyone share any thoughts? Hope I've given enough info.
Snippet from Graph response:
Image
In the manifest of my application registration I've configured to retrieve the given_name and family_name claims (through the UI, the resulting manifest looks like this):
"idToken": [
{
"name": "family_name",
"source": "user",
"essential": false,
"additionalProperties": []
},
{
"name": "given_name",
"source": "user",
"essential": false,
"additionalProperties": []
}
],
During the redirect I add the profile scope along with the given_name and family_name scopes, which results in the following error.
Message contains error: 'invalid_client', error_description: 'AADSTS650053: The application 'REDACTED' asked for scope 'given_name' that doesn't exist on the resource '00000003-0000-0000-c000-000000000000'. Contact the app vendor.
Any ideas? As I understand that is what is required to configure these optional claims on the v2.0 endpoint as described here: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-optional-claims#v20-specific-optional-claims-set
You should only use the profile 'scope', which should result in you receiving the given_name and family_name 'claims'. That's standard behaviour for an Authorization Server, which will then either:
Return the name details directly in the id token
Or allow you to send an access token to the user info endpoint to get the name details
However, Azure v2 is very Microsoft specific, and user info lookup can be painful and involve sending a separate type of token to the Graph user info endpoint. Hopefully you won't have to deal with that and you will get the name details directly in the id token.
I had a scenario where my API (which only received an access token) needed to get user info, and I solved it via steps 14 - 18 of this write up, but it's a convoluted solution.
Once you configure optional claims for your application through the UI or application manifest. you need to provide profile Delegated permissions for the application.
Next to retrieving calendar views of a user's calendar (on behalf of the user), we are trying hard to also get the calendar view of rooms via the Graph API using
https://graph.microsoft.com/beta/users/room1#ourdomain.com/calendarView. It's a painful process since we've been running into many problems and are currently stuck with the following 404 response:
https://graph.microsoft.com:443/v1.0/users/room1#ourdomain.com/calendarView?startDateTime=2018-12-04T23:00:00.000Z&endDateTime=2019-02-10T22:59:59.999Z
{
"error": {
"code": "ErrorItemNotFound",
"message": "The specified object was not found in the store.",
"innerError": {
"request-id": "358a003a-57a4-4f0e-91da-edc17c1fa2d8",
"date": "2018-12-12T07:38:33"
}
}
}
The email address of the room has been double checked and the resource exists, since we can create appointments with it and it is even being returned in the response when we retrieve the calendar of the user who has an appointment in that location.
App permissions and OAuth2 scopes are set to: openid email profile offline_access https://graph.microsoft.com/Calendars.Read https://graph.microsoft.com/Calendars.Read.Shared https://graph.microsoft.com/User.Read
https://graph.microsoft.com/User.ReadBasic.All https://graph.microsoft.com/User.Read.All, so that should not be an issue, judging by the documentation.
Does anyone know how to solve this?
I've tried all possible ways, but there is no way to get access.
This is what I've tried out the following in the Graph explorer:
https://graph.microsoft.com/v1.0/users/meetingroom1#domain.com/events -> DelegatedCalendarAccessDenied
https://graph.microsoft.com/v1.0/users/meetingroom1#domain.com/calendarView?startDateTime=2019-01-14&endDateTime=2019-01-18 -> ErrorItemNotFound
https://graph.microsoft.com/v1.0/users/meetingroom1#domain.com/calendar/calendarView?startDateTime=2019-01-14&endDateTime=2019-01-18 -> ErrorItemNotFound
All three on both the v1.0 and the beta.
It isn't an issue with rights, because for my testing I granted the Graph Explorer the Directory.ReadWrite.All scope. Resulting in the following scp claim.
The first requests seems the most promising (because of the different error), I also made myself a delegate with full control of the rooms-mailbox. That still didn't help.
A request to https://graph.microsoft.com/v1.0/users/meetingroom1#domain.com gives a result, as in a result describing the meetingroom.
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"businessPhones": [],
"displayName": "Meeting room 1",
"givenName": null,
"jobTitle": null,
"mail": "meetingroom1#domain.com",
"mobilePhone": null,
"officeLocation": null,
"preferredLanguage": null,
"surname": null,
"userPrincipalName": "meetingroom1#domain.com",
"id": "3e0a7b7e-xxxx-xxxx-xxxx-xxxxcxxxx120"
}
After doing all these tests, I can only conclude that you cannot access the events in a rooms mailbox. This is either intended (as in only use the scheduling assistant) or a bug.
Maybe some of the Microsoft guys around here could clarify this?
FINALLY! After going through this with countless Microsoft support people, each of whom said this was not their territory and did not know where to forward the question, I got in touch with somebody from the Exchange team. He suggested the one thing that worked for us: the user on behalf of which you are retrieving the room resource calendar needs to be a delegate of that room resource!
In addition, to retrieve the list of room resources which the user can select from, we needed to use the findRooms endpoint but this only works on the beta API. The only drawback of this is that you cannot seem to filter for rooms of which the user is a delegate. So the user will get a list of rooms for which he might or might not be able to retrieve the calendar.
A final drawback of the room resource calendarView response is that the response does not contain the names of the meetings planned in the rooms. The description of each event only contains the name of the meeting organizer.
Loading shared calendar from a user works so far.
However, as soon as you want to load the events, we get the following error message:
ErrorAccessDenied
Access is denied. Check credentials and try again.
The URL looks like this:
https://outlook.office.com/api/v2.0/users/{userName}/calendars/{sharedCalendarId}/calendarView
Query:
{
"query": {
"$select": "Subject,Location,Start,End,IsAllDay,BodyPreview,Extensions",
"$expand": "Extensions($filter=Id eq \"Microsoft.OutlookServices.OpenTypeExtension.custom.string.here\")",
"startDateTime": "2018-07-09T22:00:00.000Z",
"endDateTime": "2018-11-09T23:00:00.000Z"
},
"headers": {
"Prefer": [
"odata.track-changes",
"odata.maxpagesize=200"
]
}
}
The following scopes were set:
"openid",
"profiles",
"offline_access", // for refresh token
"https://outlook.office.com/calendars.readwrite",
"https://outlook.office.com/calendars.read.shared",
"https://outlook.office.com/calendars.readwrite.shared"
The Outlook REST API requests are always performed on behalf of the current user (authenticated user). That's why the endpoint /me/calendars works but users/{userId}/calendars does not. You can not get access to another user's calendar using this API. More information is provided here.
To access the other user's calendar you should switch to Microsoft Graph API. Then you could use the following endpoints:
Using https://graph.microsoft.com/v1.0/
GET /me/calendar/calendarView
GET /users/{id | userPrincipalName}/calendar/calendarView
GET /groups/{id}/calendar/calendarView
Remember to specify permissions for accessing user's calendars.
I have a public calendar on the site. I need to display upcoming events on the front page.
After spending the whole day on this I am about to give up. Google is disabling older API there is no documentation or anything. I am parsing the RSS feed:
http://www.google.com/calendar/feeds/.../public/basic?futureevents=true
And grabbing feed/entry/update and feed/entry/title from XML. I read somewhere that futureevents will display upcoming events. However I get the list of older events.
Okay, then I tried to use API V3 https://developers.google.com/google-apps/calendar/v3/reference/events/list
Here is the responce I get:
{
"error": {
"errors": [
{
"domain": "usageLimits",
"reason": "dailyLimitExceededUnreg",
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.",
"extendedHelp": "https://code.google.com/apis/console"
}
],
"code": 403,
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
}
}
I jumped through more hoops and installed their ruby code sample as I need this for Rails site.
It sort of worked but it requires user to confirm access to their own calendar. I don't need that. Plus it seems that authentication only valid for a day.
The said calendar is public. It is being displayed in iframe on the site with xml and ical feed. All I need is to parse upcoming events to show them on front page.
What is the way to do it without authorization and other ridiculous stuff?
//This works for me! js:
var request = gapi.client.calendar.events.list({
'calendarId': 'xxx',
'timeMin': (new Date()).toISOString(),
'showDeleted': false,
'singleEvents': true,
'maxResults': 1,
'orderBy': 'startTime'
});
request.execute(function(resp) {}
Use this wizard to create a new API project: https://console.developers.google.com/start/api?id=calendar Then click on "create new key" (choose server key). Then do a request to https://www.googleapis.com/calendar/v3/calendars//events?key={YOUR_API_KEY} replacing the email and api key with your values.