I have a daemon service which acquires access_token using username/password of a user (user1#domain.com) [ Using Microsoft.Identity.Client.PublicClientApplication & then using AcquireTokenByUsernamePasswordAsync to get the access token).
Azure app has scope of "Mail.Read" and Delegated Permission has been granted (which doesn't require admin consent)
I can successfully get the subscription for mailbox of user1#domain.com thus receiving notification for mails.
I am using Graph client for Subscription.
var subscription = new Subscription
{
Resource = "users/user1#domain/mailFolders('Inbox')/messages",
ChangeType = "created",
NotificationUrl = "NotificationUrl",
ClientState = Guid.NewGuid().ToString(),
ExpirationDateTime = DateTime.UtcNow + new TimeSpan(0, 0, 15, 0)
};
user2#domain.com has shared its mailbox to user1#domain.com. On azure app I added the scope of "Mail.Read.Shared" and also made sure to click on Grant Permission ( again not admin consent required).
However even after this I only receive notifications for user1#domain.com and not for user2#domain.com
I then stumbled on https://github.com/microsoftgraph/microsoft-graph-docs/blob/master/concepts/outlook-share-messages-folders.md
However even if I try to get the subscription by changing Resource property in above mentioned Subscription to "users/user2#domain/mailFolders('Inbox')/messages" - I receive following error
Code: ExtensionError Message: Operation: Create; Exception: [Status Code: Forbidden; Reason: Forbidden]
Interestingly if I use access_token received using above specified manner to initiate a get request to below url on Postman I can read the mails of shared mailbox!
(https://graph.microsoft.com/v1.0/users/user2#domain.com/mailFolders('Inbox')/messages)
Also when I tried same thing on graph explorer after modifying permissions and granting consent for Mail.Read.Shared I can read the mails.
However I get same error message when I try to acquire subscription using Postman.
Help needed on following:
What changes are required to get the subscription for user2?
Is it correct to assume that I can get access_token for user1 and then I have to setup up different subscription's for user1 & user2 in order to get notifications using a common access_token?
Related
I am working on creating application which uses Microsoft Graph API to access to calendar events for the users that belongs to an organization.
There is no issue getting event using below endpoint
https://graph.microsoft.com/v1.0/me/calendar/events
However, when accessing to below end point cause 403 error.
https://graph.microsoft.com/v1.0/users/xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/calendar/events
403 - Forbidden
{"error":{"code":"ErrorAccessDenied","message":"Access is denied. Check credentials and try again."}}
So far I have below:
Application is registed to Aure with granting
Application.ReadWrite.All
Calendars.ReadWrite
offline_access
User.ReadWrite.All
Have logic to retrieve the access / refresh token.
When the access token is decoded, below scoes are availalbe
"scp": "Application.ReadWrite.All Calendars.ReadWrite User.ReadWrite.All profile openid email"
Others:
Below endpoints work
https://graph.microsoft.com/v1.0/users
https://graph.microsoft.com/v1.0/users/xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Below endpoints errors with access denied
https://graph.microsoft.com/v1.0/users/xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/calendars
I also tried using User Principal Name instead of ID but it didn't make any difference.
Can someone please help why I am not able to access to the user calendar / events when specifying the user?
Error message showed Access is denied, we can understand that your account doesn't have enough permission to call that api(querying others' calendar events). Since the request calling only related with the access token, no matter whether you have an admin role or not. So let's assume whether you want other users to sign in your app and then they are able to query your calendar events.
According to your description, your token contained scp claim, so I'm sure you are using the delegate permission, which means you signed in and calling api on behalf yourself. And this may be the reason why the access is denied.
We can see the permissions in the screenshot above, I think the application permission type can solve your issue. Using permission type means the api calling is executed by your application itself, but not on behalf of some user. So the application can query any users' calendar events in your tenant.
Using application permission required you to assign application api permission like screenshot below.
Then if you are just testing via tools like postman, using request below to generate access token:
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id=535fb089-9ff3-47b6-9bfb-4f1264799865
&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&client_secret=sampleCredentia1s
&grant_type=client_credentials
If are composing an asp.net core application and trying to call graph api via graph sdk, follow code snippet below:
using Microsoft.Graph;
using Azure.Identity;
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "tenant_name.onmicrosoft.com";
var clientId = "aad_app_id";
var clientSecret = "client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var events = await graphClient.Users["{user_id}"].Events
.Request()
.Header("Prefer","outlook.timezone=\"Pacific Standard Time\"")
.GetAsync();
Whenever you are trying to access another user calendar events, make sure you have Calendars.Read delegated permission , you can check what type of permission you have in azure portal -
https://learn.microsoft.com/en-us/graph/migrate-azure-ad-graph-configure-permissions?tabs=http%2Cupdatepermissions-azureadgraph-powershell
I am trying to rech the endpoint
https://graph.microsoft.com/v1.0/users/{emailaddress}/mailFolders('InBox') but am receiving
Error Access Denied response.
I have granted both Mail.Read.Shared and Mail.ReadWrite.Shared on delegated permission.
The scenario I have is that in Azure AD there are a number of users , Manager#acme.com and Tests#acme.com , so they exist under the same tennant /organization.
I have an app whereby I login as Manager#acme.com as the current user. I the create a connection to the App using client id , secret etc and receive an Auth toke n to use in my api calls.
but when i try to call
https://graph.microsoft.com/v1.0/users/Tests#acme.com/mailFolders('InBox') with that token I
get the following error:
{
"error": {
"code": "ErrorAccessDenied",
"message": "Access is denied. Check credentials and try again."
}
}
Do i ned to grant both Mail.Read.Shared and Mail.ReadWrite.Shared on Application level ?
Or do i need to create a shared folder in Outlook ?
I don't believe Mail.Read.Shared or Mail.ReadWrite.Shared exist as assignable application permissions.
The only permission your App Registration should need is Mail.Read, unless you're intending on the using Graph to delete / send emails etc.
You will likely also need an ApplicationAccessPolicy. You can either create one in the Exchange Online Admin Center, or through PowerShell. I recommend you create a mail enabled security group for all addresses which you need to access and grant restricted access to your app through that policy.
I prefer PowerShell, so in that case you would need the ExchangePowerShell module, and connect to Exchange Online. You'll need some Exchange admin role to be able to do this.
So, let's assume you've created a mail enabled security group called GraphAccessibleUsers#acme.com. You can set the property to hide this from the GAL so users can't see it.
You would then create a policy as follows:
New-ApplicationAccessPolicy -AccessRight RestrictAccess -AppId "<Your-App-Registration-Id" -PolicyScopeGroupId GraphAccessibleUsers#acme.com -Description "Allow App access to users in GraphAccessibleUsers#acme.com"
The -PolicyScopeId parameter will accept:
Name
Distinguished name (DN)
Display name
Email address
GUID
If you only have a few addresses, you may opt to create an individual ApplicationAccessPolicy for each email address.
Finally, I don't think your Graph API URI is correct.
If you want to access the Inbox of Tests#acme.com, then try this instead:
https://graph.microsoft.com/v1.0/users/Tests#acme.com/mailFolders/Inbox
Please refer to mailFolder Resource Type here.
I have a web application which uses App only tokens to override the end user's permission to retrieve all Site Collections in the tenant. When attempting to use the boiler plate code provided in the example with one minor change, the Graph API is returning accessDenied when attempting to issue the call https://graph.microsoft.com/v1.0/sites?search=*. If I remove WithAppOnly(), the call succeeds [if Delegated rights for Sites.Read.All is assigned]. The Azure AD registered app has admin approved Application-scoped Sites.Read.All assigned to it.
var queryOptions = new List<QueryOption>()
{
new QueryOption("search","*")
};
var sites = await graphServiceClient.Sites.Request(queryOptions)
.WithAppOnly()
.WithScopes("Sites.Read.All")
.GetAsync();
ServiceException: Code: accessDenied
Message: Access denied
Inner error:
AdditionalData:
date: 2021-03-20T21:45:27
request-id: 16933bd6-5e7f-4820-9563-fec75575c9b2
client-request-id: 16933bd6-5e7f-4820-9563-fec75575c9b2
ClientRequestId: 16933bd6-5e7f-4820-9563-fec75575c9b2
You need to add Sites.Read.All of applicaiton permission in the Azure portal.
Note: Click the , because this permission is admin consent required.
Try to test with client credentials flow.
// Get access token
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={client_id}
&scope=https://graph.microsoft.com/.default
&client_secret={client_secret}
&grant_type=client_credentials
// Call MS Graph API
GET https://graph.microsoft.com/v1.0/sites?search={query}
I have the following permissions granted in my Azure AD App:
App:
Read calendars in all mailboxes
Read and write calendars in all mailboxes
Read calendars in all mailboxes
Delegated:
Read user and shared calendars
Read and write user and shared calendars
Read and write user calendars
Read user calendars
Read and write user and shared calendars
Read user and shared calendars
Registration Screen Shot
I am successfully generating an Access Token like the following:
const string clientId = "my-client-id";
const string clientSecret = "my-secret"; // C
var tenant = "mytenant.onmicrosoft.com";
var authContext = new AuthenticationContext($"https://login.windows.net/{tenant}/oauth2/token");
var credentials = new ClientCredential(clientId, clientSecret);
AuthenticationResult authResult = await authContext.AcquireTokenAsync("https://graph.microsoft.com/", credentials);
With that Access Token, I am trying to make a basic request to /CalendarView passing the Bearer token header:
https://outlook.office.com/api/v2.0/users/my#email.com/calendarview?startDateTime=2017-11-12&endDateTime=2017-11-13
However, I keep receiving Access Denied. Are there additional permissions I need to set? Am I calling into the correct endpoint?
You don't include the body of the error response, but my guess is that you're hitting this because Exchange won't accept a token generated with a shared secret. Instead, you need to use a certificate-based assertion to request the token. Azure documents this a "Second case: Access token request with a certificate" here.
I actually was able to figure this out. Instead of using the Exchange API, I just applied the permissions in the Graph API.
Hit the following endpoint:
https://graph.microsoft.com/v1.0/users/{email}/calendarview?startDateTime={startDate}&endDateTime={endDate}
It's not very clear the difference between which API to use... but I'm moving forward now.
My end goal is to be able to retrieve place details from Google's API.
I need to do this as a Service Account, since this is kicked off as a background task on my server. Service Accounts require you to exchange a JWT (JSON Web Token) for an access_token. I finally got that working and am receiving an access_token. Phew.
Now however, I don't know what to do with this access_token.
The Place Details API says that the key parameter is required, but I don't have a key. Just an access_token. Using that value for key or changing the name of the paramater to access_token is not working.
Ultimately I need to be able to hit a URL like so:
https://maps.googleapis.com/maps/api/place/details/json?reference={MY_REFERENCE}&sensor=false&key={MY_ACCESS_TOKEN}
How do I use my Access Token to make a request to the Google Place Detail APIs?
Update 1
Still no success, but I thought I'd post the details of my request in case there's something wrong with what I'm submitting to Google.
I'm using the JWT Ruby library, and here are the values of my claim set:
{
:iss => "54821520045-c8k5dhrjmiotbi9ni0salgf0f4iq5669#developer.gserviceaccount.com",
:scope => "https://www.googleapis.com/auth/places",
:aud => "https://accounts.google.com/o/oauth2/token",
:exp => (Time.now + 3600),
:iat => Time.now.to_i
}
Looks sane to me.
Create the service account and its credentials
You need to create a service account and its credentials. During this procedure you need to gather three items that will be used later for the Google Apps domain-wide delegation of authority and in your code to authorize with your service account. These three items are your service account:
• Client ID.
• Private key file.
• Email address.
In order to do this, you first need a working Google APIs Console project with the Google Calendar API enabled. Follow these steps:
Go to the Google APIs Console.
Open your existing project or create a new project.
Go to the Service section.
Enable the Calendar API (and potentially other APIs you need access to).
You can now create the service account and its credentials. Follow these steps:
Go to the API Access section.
Create a client ID by clicking Create an OAuth 2.0 client ID...
Enter a product name, specify an optional logo and click Next.
Select Service account when asked for your Application type and click Create client ID.
At this point you will be presented with a dialog allowing you to download the Private Key as a file (see image below). Make sure to download and keep that file securely, as there will be no way to download it again from the APIs Console.
After downloading the file and closing the dialog, you will be able to get the service account's email address and client ID.
You should now have gathered your service account's Private Key file, Client ID and email address. You are ready to delegate domain-wide authority to your service account.
Delegate domain-wide authority to your service account
The service account that you created now needs to be granted access to the Google Apps domain’s user data that you want to access. The following tasks have to be performed by an administrator of the Google Apps domain:
Go to your Google Apps domain’s control panel. The URL should look like: www.google.com/a/cpanel/mydomain.com
Go to Advanced tools... > Manage third party OAuth Client access.
In the Client name field enter the service account's Client ID.
In the One or More API Scopes field enter the list of scopes that your application should be granted access to (see image below). For example if you need domain-wide access to the Google Calendar API enter: www.googleapis.com/auth/calendar.readonly
Click the Authorize button.
Your service account now has domain-wide access to the Google Calendar API for all the users of your domain, and potentially the other APIs you’ve listed in the example above.
Below is a description that uses a service account to access calendar data in PHP
The general process for service account access to user calendars is a follows:
• Create the Google client
• Set the client application name
• If you already have an Access token then check to see if it is expired
• If the Access token is expired then set the JWT assertion credentials and get a new token
• Set the client id
• Create a new calendar service object based on the Google client
• Retrieve the calendar events
Note: You must save the Access token and only refresh it when it is about to expire otherwise you will receive an error that you have exceeded the limit for the number of access tokens in a time period for a user.
Explanation of Google PHP Client library functions used:
The client object has access to many parameters and methods all of the following are accessed through the client object:
Create a new client object:
$client = new Google_Client();
Set the client application name:
$client->setApplicationName(“My Calendar App”);
Set the client access token if you already have one saved:
$client->setAccessToken($myAccessToken);
Check to see if the Access token has expired, there is a 30 second buffer, so this will return true if the token is set to expire in 30 seconds or less. The lifetime of an Access token is one hour. The Access token is actually a JSON object which contains the time of creation, it’s lifetime in seconds, and the token itself. Therefore no call is made to Google as the token has all of the information locally to determine when it will expire.
$client->isAccessTokenExpired();
If the token has expired or you have never retrieved a token then you will need to set the assertion credentials in order to get an Access token:
$client->setAssertionCredentials(new Google_AssertionCredentials(SERVICE_ACCOUNT_NAME,array(CALENDAR_SCOPE), $key,'notasecret','http://oauth.net/grant_type/jwt/1.0/bearer',$email_add));
Where:
SERVICE_ACCOUNT_NAME is the the service account email address setup earlier.
For example:’abcd1234567890#developer.gserviceaccount.com’
CALENDAR_SCOPE is the scope setup in the Google admin interface.
For example: ‘https://www.googleapis.com/auth/calendar.readonly’
$key is the content of the key file downloaded when you created the project in Google apps console.
$email_add is the Google email address of the user for whom you want to retrieve calendar data.
Set the client id:
$client-setClientId(SERVICE_CLIENT_ID);
Where:
SERVICE_CLIENT_ID is the service account client ID setup earlier.
For example: ‘abcd123456780.apps.googleusercontent.com’
Create a new calendar service object:
$cal = new Google_CalendarService($client);
Several options can be set for calendar retrieval I set a few of them in the code below, they are defined in the api document.
$optEvents = array('timeMax' => $TimeMax, 'timeMin' => $TimeMin, 'orderBy' => 'startTime', 'singleEvents' => 'True');
Get the list of calendar events and pass the above options to the call:
$calEvents = $cal->events->listEvents('primary', $optEvents);
Loop through the returned event list, the list is paged so we need to fetch pages until the list is exhausted:
foreach ($calEvents->getItems() as $event) {
// get event data
$Summary = $event->getSummary();
$description = $event->getDescription();
$pageToken = $calEvents->getNextPageToken();
if ($pageToken) { // if we got a token the fetch the next page of events.
$optParams = array('pageToken' => $pageToken);
$calEvents = $cal->events->listEvents('primary', $optParams);
} else {
break;
}
}
Get the Access token:
$myAccessToken=$client->getAccessToken();
Save the access token to your permanent store for the next time.
The language isn't important php, ruby, .net, java the process is the same. The api's console shows the Places API as supporting service accounts so it should be possible to access it.
As far as using the token please have a look at https://code.google.com/p/google-api-ruby-client/ code as the usage is clearly defined in the code repository. Doesn't make any difference if the access token is for a service account or a single user the process for using the token is the same. See the section titled "Calling a Google API" in the following link: https://developers.google.com/accounts/docs/OAuth2InstalledApp
The access token is sent in the http authorization header along with the request.For a calendar request it would look something like the following:
GET /calendar/v3/calendars/primary HTTP/1.1
Host: www.googleapis.com
Content-length: 0
Authorization: OAuth ya29.AHES6ZTY56eJ0LLHz3U7wc-AgoKz0CXg6OSU7wQA