Not receiving a request on our MS Graph Webhook for deleting a User in AAD - azure

We created a Webhook for receiving "Delete" notifications when a user is deleted from Azure AD. But we're not receiving any notifications when we delete a user. In AAD the user is first placed in the recycle bin, but also if we remove the user from the recycle bin, we don't receive any notifications.
We've tried our code with receiving emails -> that worked. And with changing a user in AAD -> that also worked. So we changed "updated" to "deleted" and no calls are triggered.
We started with de documentation (https://learn.microsoft.com/en-us/graph/webhooks) and the sample code provided by Microsoft (https://github.com/microsoftgraph/aspnet-webhooks-rest-sample)
We use Permission Scopes:
User.Read.All & Directory.Read.All
Graph Webhook subscription:
Resource: "users"
ChangeType: "deleted"
When we specify "updated" as ChangeType, we received notifications, as expected. But ChangeType "deleted" was not giving any notifications.
Is this not supported, or are we missing a permission...
I hope someone can help.

When you've subscribed to deleted events, you will only get notifications for hard-deleted users. User are almost always "soft-deleted" at first, and then get permanently deleted automatically after 30 days.
For both cases, the permissions User.Read.All is sufficient.
When a user is "soft-deleted" an event is sent to apps subscribed to updated changes. Here's an example (you'll have to trust me that this was due to a soft-delete, since it's the same event for a regular attribute change):
{
"value": [
{
"changeType": "updated",
"clientState": null,
"resource": "Users/514ffc40-afef-4ad9-bc1f-4ad3e425fcec",
"resourceData": {
"#odata.type": "#Microsoft.Graph.User",
"#odata.id": "Users/514ffc40-afef-4ad9-bc1f-4ad3e425fcec",
"id": "514ffc40-afef-4ad9-bc1f-4ad3e425fcec",
"organizationId": "1c411c5e-78cc-4e89-af5e-169408a540b7",
"sequenceNumber": 636921552671905776
},
"subscriptionExpirationDateTime": "2019-05-01T17:13:30.289+00:00",
"subscriptionId": "cfbfa7fc-0771-4394-b563-cff3f8140d02",
"tenantId": "1c411c5e-78cc-4e89-af5e-169408a540b7"
}
]
}
When a user is permanently deleted (either naturally after 30 days, or manually by an admin), apps subscribed to deleted will get a notification. Here's an example:
{
"value": [
{
"changeType": "deleted",
"clientState": null,
"resource": "Users/514ffc40-afef-4ad9-bc1f-4ad3e425fcec",
"resourceData": {
"#odata.type": "#Microsoft.Graph.User",
"#odata.id": "Users/514ffc40-afef-4ad9-bc1f-4ad3e425fcec",
"id": "514ffc40-afef-4ad9-bc1f-4ad3e425fcec",
"organizationId": "1c411c5e-78cc-4e89-af5e-169408a540b7",
"sequenceNumber": 636921556468034066
},
"subscriptionExpirationDateTime": "2019-05-01T17:13:30.289+00:00",
"subscriptionId": "ce04c176-370d-4b67-9da6-05c441186756",
"tenantId": "1c411c5e-78cc-4e89-af5e-169408a540b7"
}
]
}

Related

API Integration across multiple accounts within same organization

I have had my API Integration promoted to my Production environment for a few weeks now and all is well but I ran into a new issue that I need help understanding. The process is setting up impersonation. The hierarchy of the organization is relatively simple:
My Integration was built under Company A and so far 100% of Company A accounts are able to be impersonated as expected. The issue came up when Company B was added to the Organization and one of the existing accounts was included in the list to be impersonated. The following message is what I am getting back from my API call.
I have Organization Admin permissions as well as Admin permissions on all of the Company Accounts too and this message appears even for me. My feeling is this is a simple administrative function to grant the User in Company A the permissions to access either a User in Company B or all of Company B. I am just not seeing where this gets setup. I hope anyone can point me in the right direction on this one.
=== 07/06/2022 - Adding additional details ===
/oauth/userinfo respose...
{
"sub": "xxxxx-xx-xx-xx-xxxxx",
"name": "Greg Miller",
"given_name": "Greg",
"family_name": "Miller",
"created": "2017-11-10T18:26:23.583",
"email": "greg.miller#companyA.com",
"accounts": [
{
"account_id": "xxxxx-xx-xx-xx-xxxxx",
"is_default": true,
"account_name": "CompanyA",
"base_uri": "https://###.docusign.net",
"organization": {
"organization_id": "xxxxx-xx-xx-xx-xxxxx",
"links": [
{
"rel": "self",
"href": "https://account.docusign.com/organizations/xxxxx-xx-xx-xx-xxxxx"
}
]
}
},
{
"account_id": "zzzzz-zz-zz-zz-zzzzz",
"is_default": false,
"account_name": "CompanyB",
"base_uri": "https://###.docusign.net",
"organization": {
"organization_id": "zzzzz-zz-zz-zz-zzzzz",
"links": [
{
"rel": "self",
"href": "https://account.docusign.com/organizations/zzzzz-zz-zz-zz-zzzzz"
}
]
}
}
]
}
Additional Info Added 07/07/22
Both Company A and Company B base_uri designation is the same "https://na2.docusign.net"
This is the /oauth/userinfo data returned using the JWT created for the Company B user account I am trying to impersonate.
{
"sub": "xxxxx-xx-xx-xx-xxxxx",
"name": "Company B",
"given_name": "CompanyB",
"family_name": "XYZ TEAM",
"created": "2021-03-31T18:20:05.23",
"email": "xyzteam#companyb.com",
"accounts": [
{
"account_id": "xxxxx-xx-xx-xx-xxxxx",
"is_default": true,
"account_name": "Compan B",
"base_uri": "https://na2.docusign.net",
"organization": {
"organization_id": "xxxxx-xx-xx-xx-xxxxx",
"links": [
{
"rel": "self",
"href": "https://account.docusign.com/organizations/xxxxx-xx-xx-xx-xxxxx"
}
]
}
}
]
}
The steps I take are basically the same as you outline:
Generate JWT Access Token
I am manually storing the required userinfo data userID(sub) and base_uri in a local db table.
I am using CURL to make my API calls " $base_uri.'/restapi/v2.1/accounts/'.$AccountID.'/views/console'"
You have two choices for accessing data in Company B (Account B):
Add the user in Company A (Account A) to also be a user in Account B. (Users can have memberships in more than one account.)
To access the data in Account B (Company B), impersonate a (different) user who is in account B. This is done via the eSign Admin app or via the Org Admin app.
By design, a user who is not in Account B cannot access any data in Account B. (This is the error message you're receiving.)
Note: you do not need to make any changes to your app's integration key (client ID)--all client IDs in production can be used with any user, with any account the user has access to.
To see which accounts the current user has access to, use the /oauth/userinfo API method.
Added
When you get the message User does not have a valid membership in this account check:
What account is the request using? (What is the URL of the request?)
Was the request sent to the right base url for the account?
What result does the current access token provide when calling the /oauth/userinfo API method.
Your test API calls should be:
Get an access token
Call /oauth/userinfo
Call the eSign API (eg list envelopes or somesuch) for each of the accounts listed in /oauth/userinfo

Notifications for changes in user data in Azure AD

I created a subscription in Azure AD for receiving notification when a user is deleted. But I'm not receiving any notifications when I delete or update a user.
I got 201 code, so every thing is ok.
I've been waiting for more than 24 hours !
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity",
"id": "5bfc6bc2-a4bd-4e03-b420-7f5f72158dab",
"resource": "users",
"applicationId": "4547857b-a6ce-424f-8f71-d1f0c8050c59",
"changeType": "updated,deleted",
"clientState": null,
"notificationUrl": "https://8ffa2519154d.ngrok.io/azure/notifications",
"expirationDateTime": "2020-09-16T11:05:00Z",
"creatorId": "78f2eabd-f0a5-4350-b541-ef9edb47ae80",
"latestSupportedTlsVersion": "v1_2"
}

"webUrl" coming as null when sending an file invitation - Graph API (Send a sharing invitation)

I'm Using an invite API from MS GRAPH - Invite LINK
Send a sharing invitation – External users
POST /me/drive/items/{item-id}/invite
POST /sites/{siteId}/drive/items/{itemId}/invite
The responses from the above request returns 200 OK response code and a permission object is returned , but the Sharing link(webUrl) under the link object returns “null” most of the times due to which the shareable link cannot be shared to External user for editing the document.
REQUEST BODY:
{
"recipients": [
{
"email": "abc#abc.com"
}
],
"message": "Here's the file that we're collaborating on.",
"requireSignIn": true,
"sendInvitation": false,
"roles": [ "write" ]
}
Here i don't want to share the item throgh mail hence making sendInvitation false and using the webURL recevied from response for collaboration.
Observation :
It works with gmail and outlook account.
For business accounts it's not working receiving url as null.
Samples:
If i invite for the first time , i'm getting below response ,
Sample Excepted Response:
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(permission)",
"value": [
{
"#odata.type": "#microsoft.graph.permission",
"roles": [
"write"
],
"grantedToIdentities": [
{
"user": {
"email": "##########.com"
}
}
],
"invitation": {
"signInRequired": true
},
"link": {
"type": "edit",
"webUrl": "https://**********encryptedURL*****/"
}
}
]
}
From the second time link object not coming in the response,
Sample Response Received Without webURL:
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(permission)",
"value": [
{
"#odata.type": "#microsoft.graph.permission",
"id": "###############",
"roles": [
"write"
],
"grantedTo": {
"user": {
"email": "#############.com",
"id": "#############",
"displayName": "#######"
}
}
}
]
}
i resolved my problem by using the upload API LINK
, once upload is successful the API returns response which will have "webUrl" link that can be used to share from second time.
(After exploring more with MS GRAPH API's observed that if we are
sharing the same file multiple times it will not give any new link as
the old link only can be used )
Actual Flow : Upload File -> Share File (this was happening everytime)
Changed Logic : First time : Upload File -> Share file
Second Time onwards : as file is already shared , using the url from upload API Response
As file is already shared with user for the first time hence the url which we are getting as response from UPLOAD API can be used , this url can be accessible by already shared user.
So i changed my logic if the user performing the operation for first time then i'm uploading file and sharing with him , if the user wants to perform same operation again instead of sharing the file again using the upload response url it works for me.
Posting this solution as it might help someone

How to get Organization name from Azure openid?

I want to build a SAAS Service for Azure marketplace using single-sign-on.
I have read this document Microsoft identity platform access tokens, but can not find anything relate to User's Organization.
Is there any way to get user's Organization name?
For now I only can parser from email.
You can call MS Graph API to get the user's organization details: https://learn.microsoft.com/en-us/graph/api/organization-get?view=graph-rest-1.0&tabs=http.
The endpoint is at https://graph.microsoft.com/v1.0/organization
Sample response:
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#organization",
"value": [
{
"assignedPlans": [
{
"assignedDateTime": "datetime-value",
"capabilityStatus": "capabilityStatus-value",
"service": "service-value",
"servicePlanId": "servicePlanId-value"
}
],
"businessPhones": [
"businessPhones-value"
],
"city": "city-value",
"country": "country-value",
"countryLetterCode": "countryLetterCode-value",
"displayName": "displayName-value"
}
]
}
You can call this endpoint even with the basic User.Read permission.

The graph token has no scopes, while trying to get Azure functions working with Excel output

I'm trying to get Azure Functions working with IoT Hub messages, and on message adding a row to an Excel file.
I tried with setting Excel integration as output, which wasn't working, so I changed and tried with Excel input.
When I try this, I get the following failure in the console.
The graph token has no scopes. Ensure your application is properly configured to access the Microsoft Graph.
What is the correct way to get the credentials working with my Microsoft account? Note that I'm trying to connect with my account and not end-user's account, which is mentioned in all the tutorials/documentation.
I'm using a personal Microsoft account and not Org account, in case that is important.
Here is my function.json.
{
"bindings": [
{
"type": "eventHubTrigger",
"name": "IoTHubMessages",
"direction": "in",
"eventHubName": "samples-workitems",
"connection": "home-auto_events_IOTHUB",
"cardinality": "many",
"consumerGroup": "$Default"
},
{
"type": "excel",
"name": "inputTable",
"path": "Documents/Test.xlsx",
"worksheetName": "Sheet1",
"tableName": "Table1",
"identity": "ClientCredentials",
"direction": "in"
}
]
}
Here is the default index.js from the template.
module.exports = function (context, IoTHubMessages) {
context.log(`JavaScript eventhub trigger function called for message array: ${IoTHubMessages}`);
IoTHubMessages.forEach(message => {
context.log(`Processed message: ${message}`);
});
context.done();
};
Questions:
What else can I try to get the authentication working? Any ideas on what is going wrong? I'm not even sure if I'm using this correctly.
How to debug this? I can't seem to figure out how to get any more information on the failure then what is shown in the web console.
TL;DR;
The workaround is to generate the tokens on your own and then make the rest subscription call to the graph api from your Azure Function.
Most probably the token you're acquiring does not contain a scp claim but roles instead.
{
"typ": "JWT",
...
"alg": "RS256",
...
}.{
"aud": "https://graph.microsoft.com",
"iss": "https://sts.windows.net/<tenantId>/",
...
"app_displayname": "change notification app",
...
"roles": [
"User.ReadWrite.All"
],
...
"ver": "1.0",
...
}.[Signature]
please take a look at what the Webhook Azure Function binding is expecting from here
There are other users facing the same issue as you can see from here too,
what makes me think this extension could be outdated/under-development.

Resources