Online collaboration and sharing of documents are key in today’s business. It comes with it, however, the question of confidentiality and security of the documents. In many cases, business needs to instill fine-grained control over such sharing. There are various controls that may need to be enforced with such sharing e.g. share only internally, only with specified users or groups, limited sharing with external users, or even no sharing at all. Add to that an ability to monitor and control each such sharing in detail.
Microsoft provides an excellent framework for online collaboration as well as a deep stack of Azure Information Protection and Management tools to achieve such goals. But it may need a lot of sewing in between before one can play with the canvas.
Talking about sharing, one needs to get into the technicalities of how exactly a sharing event happens and how to trap that to be able to integrate it with document-level security requirements. A detailed use case and implementation could be the subject matter of a future discussion, but today, I wish to discuss the inside working of a sharing event and how to intercept the event allowing you to write your own event handler.
Background
Let me wear the practitioner’s hat now. In one of our recent assignments, we had some requirement and we needed to tap into the sharing event of a document so that we can perform a few additional actions upon sharing. Our natural choice was the out of the box events like “RoleAssignmentAdded/ RoleAssignmentDeleted/ResetInheritance” etc, but unfortunately, these events alone could not do the job. We needed some logic to be built around “Sharing Links” hidden list.
Permissions
Let’s look at the technical intricacies when a document or folder is shared from SharePoint online or One drive and why we had to look into that detail level of implementation from Microsoft.
Just a recap first of what you can do with the permissions while sharing a document. There can be different types of permission levels that can be attached to the user/group:
Read
With Read permission, User gets read access and he would be able to download the document.
User is entitled with this type of permission when we Select a document>Share>Provide email id and comment(optional):
Restricted View
With “Restricted View” permission, User gets read access and he would NOT be able to download the document.
User is entitled with this type of permission when we Select a document>Share>Provide email id and comment(optional)>click on people you specify can view>Enable block download
This type of access is only available for files, not for folders.
Contribute
With “Contribute” permission, User gets the edit access to the document, in the case disabling download option is not viable option.
User is entitled with this type of permission when we Select a document>Share>Provide email id and comment(optional)> click on people you specify can view>check the “Allow Editing” option
So far, this is still what the user gets to see how it is organized. Dig a little deeper now. The item that I am going to discuss here is how the portal maintains this sharing information and how to capture the event of sharing effectively.
If we try to capture the event like “RoleAssignmentAdded” or “RoleAssignmentDeleted”, we might get into issues when we try to share the same document with other users with the similar type of access. The above event handler will not be triggered. To understand why we need to understand the underlying design behind the sharing.
Naming convention of Sharing Group
When we share a document with any user, it creates a special type of SharePoint group with the following naming convention:
“SharingLinks.<document unique id>.Flexible.<access type id>””
When the first user gets added to the group, “RoleAssignmentAdded” event is triggered and we can capture the same. But if the same type of access is provided to the second user for the same document, this time, it is the same group where user gets added and “RoleAssignmentAdded” event will not be triggered.
Similar is the issue when we have multiple users with same access type. Only when permission is being revoked from the last user, “RoleAssignmentDeleted” event is triggered.
Hence, we ran into issues of not being able to capture event for all instances of sharing of the same document.
To overcome this issue, “Sharing Links” hidden SP list became the savior for us. Let us understand the design and behavior of this list.
You may also like: Learn how to proactively identify and protect your sensitive information
Hidden List – “Sharing Links” – The Solution
Assuming the reader has enough idea about “document unique id”, I’m taking the liberty to explain this “access type id” of the dynamic group name mentioned above. To understand that, we need to know about this hidden list (“Sharing Links”) because sharing information of any document in the site is captured in this very hidden list. Initially when we create a site, this list is not created by default. As soon as any document is shared from the site, the list gets created and first entry is made to the list.
Here are the important fields in the hidden list:
Field Title | Internal Name | Details |
Document ID | SharingDocId | Unique id of the document which is shared |
Available sharing links | AvailableLinks | Sharing details with invitee and inviter details along with the permission details |
Available Links Payload
AvailableLinks field stores the Share Id, Expiration Details, Role definition, inviter and invitee details and other required information in the form of json payload
Here is a sample payload:
[{"LinkKind":6,"ExpirationDateTime":null,"ExpirationModifiedDate":"\/Date(1589866980166)\/","IsActive":true,"ShareId":"6756a647-d0c0-44fd-8322-be5e87dcadd2","AuthKey":"ARiB4jWeIjWnoN5OqlXSF0k","CreatedById":14,"LastModifiedById":14,"CreatedDate":"\/Date(1589817470116)\/","LastModifiedDate":"\/Date(1589866980151)\/","ObjectType":1,"Flags":1048,"GroupId":81,"RoleDefinitionId":1073741826,"IsDeleted":false,"HasLinkClaim":false,"IsDefault":true,"Invitees":[{"Type":3,"Email":"ABC@GMAIL.COM","InvitedBy":14,"InvitedOn":"\/Date(1589817469991)\/"},{"Type":1,"PId":61,"ShareByEmailGuest":true,"InvitedBy":14,"InvitedOn":"\/Date(1589866980088)\/"}]},
{"LinkKind":6,"ExpirationDateTime":null,"ExpirationModifiedDate":"\/Date(1589822683075)\/","IsActive":true,"ShareId":"569a7240-3017-4b3e-8580-212242c4bb0a","AuthKey":"AXsC0cf0X8-YTN8w3OvN9fk","CreatedById":14,"LastModifiedById":14,"CreatedDate":"\/Date(1589822683075)\/","LastModifiedDate":"\/Date(1589822683075)\/","ObjectType":1,"Flags":1048,"GroupId":82,"RoleDefinitionId":1073741827,"IsDeleted":false,"HasLinkClaim":false,"IsDefault":true,"Invitees":[{"Type":2,"PId":16,"InvitedBy":14,"InvitedOn":"\/Date(1589822682981)\/"}]},
{"LinkKind":6,"ExpirationDateTime":null,"ExpirationModifiedDate":"\/Date(1589826794964)\/","IsActive":true,"ShareId":"6623c477-e00e-48e2-8f3c-1750578dc59a","AuthKey":"AfmW421MMH6cQXyWKQhY_S4","CreatedById":14,"LastModifiedById":14,"CreatedDate":"\/Date(1589826794948)\/","LastModifiedDate":"\/Date(1589826794948)\/","ObjectType":1,"Flags":1048,"GroupId":84,"RoleDefinitionId":1073741832,"IsDeleted":false,"HasLinkClaim":false,"IsDefault":true,"Invitees":[{"Type":1,"PId":83,"InvitedBy":14,"InvitedOn":"\/Date(1589826794776)\/"}]}]
All information about sharing of any document is segregated based on the above 3 types of shares (Read/Contribute/Restricted View) and gets stored in the “Sharing Links” hidden list.
ShareId | This is the unique identifier for the type of access which gets appended as part of the dynamic group explained above |
AuthKey | This is the unique string appended as part of the shared link sent to the user |
RoleDefinitionId | Defines the type of access (Read/Contribute/Restricted View) |
Invitees | Invitees This is again a nested json object representing the people to whom the document is shared providing the given type of access. These are properties of the object to be specific which would be to our interest: Type: Defined the type of user: Internal User (1)/ Internal Group (2)/ External User (3) PId: User id or group id in case it is shared with internal user or group Email: Email id in case the user is external user SharedEmailGuest: is set to true when the user is B2B external user and already added to the tenant as a guest user. Invited By: User id of the user who has shared the document Invited On: Date Time when the document is shared to the invitee |
Let me explain the important properties which are highlighted above.
Sharing with Internal and External User
Sharing with an internal user and the external user has got some different behaviors in terms of actual access modification of any document which makes it a challenge to work with events like RoleAssignementAdded/RoleAssignmentDeleted etc. For example, when a link is shared with an internal user, the user gets added to the document permission set immediately and if we try to query RoleAssignment of the document programmatically, we would be able to fetch the user details. But the same is not true for external users. Unless and until the external user is clicking on the shared link received in his mailbox, the user is not added to the permission set of the document. These kinds of differences make it difficult to build a generic solution around document sharing. Whereas this “Sharing Links” hidden list itemadded/itemupdated event behavior is quite consistent for both internal and external users. We would be able to fetch the external user information even though the user is yet to access the link.
Takeaways
So, really this is all about understanding the “Sharing Links” hidden list. Once we know how the system maintains the list, we can capture the events on this list. Voilà! you could write the desired piece of code in the event handler and be done with it. The applicability is enormous, you can control any attribute of the document and implement your own security policy.
Download the Datasheet to learn more about Netwoven’s Information Protection and Compliance service.
Download the Solution Brief to learn how Netwoven’s solution proactively identifies and protects your sensitive data.
Hi Devjani
Thanks for a very usefull article about externallinks. I need to query the external links for a certain document of which I have the Guid. I also can locate the “Sharing Links” list by getting it by name from the website.
Do you know a way to get to the ListItem of the document without doing a foreach on all the ListItems on the List ?
I have tried to retrieve the Guid of the Flexible. part and use that Guid to locate the ListItem of the document, but that did not work out. So right now I need to do a foreach on all ListItems on the List which could give a bad performance.
Hi Jesper,
Yes, it is possible to get the external links for a certain document/ folder by querying the list keeping a filter on “SharingDocId” column. Your CAML query should include:
where uniqueId is the UniqueId property of the document/folder.
Please let me know if you need further details.
Hi Devjani
I’ve been looking for more information about shared links and your article is technically very deep.
I’m trying to redirect shared links from old user account to a new user in OneDrive for Business.
The old user login is already fixed at document’s URL, but would be perfect to keep it working after old user’s account had been shutdown by redirecting to some manager account where files were moved to.
Would be possible to achieve this?
Kindly regards.
Hi Jonas,
To my knowledge best solution for your requirement would be to keep track of the Sharing details of the documents(details can be found in SharingLinks list), move the documents to a different library, and programmatically share them through Graph API or some other SharePoint API.
Thanks & Regards,
Devjani
Hello Devjani, hope this finds you well.
We are doing a migration and our users have used the copy link for all employees in company which give me something like this.
/:x:/s/NoDocID/EeYJ7eAKc-NIg4dbwEoWyLMBlMdm8jh1hQygRhw2QHmajQ?e=49fbNt
given what i have read i know it stores this information in the list “Sharing Links” from which i can grab the sharing id and the unique id and figure out which file i need to replace with the Document ID. What i am having trouble figuring out is how do i take the link created and map it to which item id in the “Sharing Links “list? ANy help appreciated. Thanks
Hi Devjani,
For the past 19 months we use to be able to Externally share folders and we would set a password which was primarily to allow our end-users collaborate with clients. Right at the start of 2022 this broke for us. All prior external folder sharing quit working and nothing new would work. The “copy link” works because it prompts for the password but it rejects with a “Access denied”. Ironically we had just migrated some data from an acquisition during the holiday break prior so I’m not sure if they had anything to do with it but I would doubt that was the cause. Its definitely permission related so I’m trying to understand the guts and behind the scenes process of external sharing such as why are some of the hidden names in permissions called sharinglinks vs limited access system group. Do both work together to allow the external sharing execute properly or do the two different terms have two different ties. We have reached out to 4 different MS support techs in hopes that maybe one could figure it out. All of them have been useless to us. The odd sort of work around is that we created a new communication site that we may switch over soon but the external folder share works perfectly in that section.