Externally Sharing – SPFX IsExternallyShared Field Customizer


This is my last post of the Externally Sharing series.

This post will show you how I have created a SPFX field customizer, where if it is displaying in the view, it will perform a call to the GetSharingInformation, then iterate through the links -> Invitations and principals results looking for external users. If it finds an external user, it displays a sharing link for the item, or it doesn’t.

 

ExternalShare2

You can find my Git Hub project at the following URL: https://github.com/pmatthews05/spfx-IsExternallyShared

The field customizer is a REACT SPFX project. As the control onInit() it grabs the list id from the pageContext.


@override
  public onInit(): Promise {
    // Add your custom initialization to this method.  The framework will wait
    // for the returned promise to resolve before firing any BaseFieldCustomizer events.
    Log.info(LOG_SOURCE, 'Activated IsExternallySharedFieldCustomizer with properties:');
    Log.info(LOG_SOURCE, `The following string should be equal: "IsExternallySharedFieldCustomizer" and "${strings.Title}"`);
    this.listId = this.context.pageContext.list.id;
    return Promise.resolve();
  }

As the control onRenderCell event fires, I grab the listitem’s ID. Then pass the ListID and the ID of the item to my .tsx control.

@override
  public onRenderCell(event: IFieldCustomizerCellEventParameters): void {
    const id = event.listItem.getValueByName('ID');
    Log.info(LOG_SOURCE, `Loaded onRenderCell: ID: ${id}`);
    const isExternallyShared: React.ReactElement =
      React.createElement(IsExternallyShared,
        {
          id: id,
          listId: this.listId,
          context: this.context
        } as IIsExternallySharedProps);
    ReactDOM.render(isExternallyShared, event.domElement);
  }

The ExternallyShared.tsx file sets its externalShared state to 0. There are 3 values for externalShared.

0 – Loading

1 – Shared

2- Not Shared

The ExternallyShared.tsx file calls the GetSharingInformation/permissionsInformation in a POST REST API call. From the results I get back (assuming successful), I loop through the links, and principals looking for external users. As soon as I find 1 external user I return my results back. The code here isn’t perfect. I should really create an Interface for the returning json results.

export default class IsExternallyShared extends React.Component {
  constructor(props: IIsExternallySharedProps, state: IIsExternallySharedState) {
    super(props, state);
    this.state = {
      externallyShared: 0
    };
  }
  @override
  public componentDidMount(): void {
    Log.info(LOG_SOURCE, 'React Element: IsExternallyShared mounted');
    this._getIfExternallyShared()
      .then(isExternallyShared => {
        this.setState({
          externallyShared: isExternallyShared
        });
      })
      .catch(error => {
        Log.error(LOG_SOURCE, error);
      });
  }
  @override
  public componentWillUnmount(): void {
    Log.info(LOG_SOURCE, 'React Element: IsExternallyShared unmounted');
  }

 @override
 public render():React.ReactElement {
 return (

 

);
}
privateasync_getIfExternallyShared():Promise {
 var response = await this.props.context.spHttpClient.post(`${this.props.context.pageContext.web.absoluteUrl}/_api/web/Lists('${this.props.listId}')/GetItemById('${this.props.id}')/GetSharingInformation/permissionsInformation`, SPHttpClient.configurations.v1, null);
if (response.status!=200) {
 return 0;
}
  var jsonReponse = await response.json();
    let returnValue = 2;
    for (let link of jsonReponse.links) {
      if (link.linkDetails.HasExternalGuestInvitees) {
        returnValue = 1;
        break;
      }
    }

    if(returnValue == 2)
    {
      for(let principal of jsonReponse.principals)
      {
        if(principal.principal.isExternal){
          returnValue =1;
          break;
        }
      }
    }

    return returnValue;
  }

As you can see in the render() method, I have created my own ExternallySharedIcon control. Passing in the externallyShared value.

  

The ExternallySharedIcon.tsx control just displays an 64based image depending on the value.

  1. – Shows the spinning loading icon
  2. – Shows the sharing icon
export default class ExternallySharedIcon extends React.Component{
    private SHARED_ICON: string = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAG00AABtNAWneuvsAAAAZdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuMTZEaa/1AAACkUlEQVRoQ9XaO2gVQRTG8ev7VSiKASGi2PgANYJaxE5IYqdiZavYWAk2IgoWgp1YaaxEG0HUSiy1sooGQnyAoDYqgiGg+ADf/08YGMLJvTP7mNn94FeES3ZPcmdnz8xupyEZwCVM4At+4j3u4QiWoJFZhZv428M7HECjsgGvYRVs+YMzaESW4yWsQns5huy5Cqu4EF+xDtmik/+CVVyoy8iWU7CKijGNecgSTY1WUbF2IEuewiooVrZp9RmsgmIdQtLMxWFo/FoFxRpEsuzFY1iFFPEdSdqLbbgPq4gybqPWrMU1/IZVQBk65k5EZQH6sOL/T7NHn1/AN1gnr8JFBEUFn8U41N66A3zELezDHCiLcAJT8E9WtTuYj65RUcfxGdZBfA+h5iqmq3T0OzpPyLT6A+fR8+6r4tVnWAepiobXObhZRMPzKB5hZo/0AVewEUE5Df8AVbuL9Zgty7AFahP64YZoUDZDX5V14rKeYwi15gask5fxCSehYVJrFkMLBauIIjRXX8caJMluWIUU8QRJ+xTlIKxiYr2CmrbkqfIPyLJS2gWroCJ0596DpKn6ItYejma1ZBexolnDKqYMN40uRO2p80b2AsOoPa1uJZSUzdxSKBpevZq5TYiK2lyNX/9glgdQO63p0/q8mzfQedQnWZ/7tB4Jaqf9rIa1oNGiRQuaEbivWP/FxixorMQuKauckmcKXlKWiS5ALerLbuJa1CjqxpskW1HHtoqGUtJoY2sMVjFFJNvY8qMLvrVbi36q2p1OvrnrMgmroFjZttdb/4Cj9Y+Y9JDPv7MXkfUhnzIKq7AQ2R+zKq1/0K3oVYOYLrZRrxq4rIRe9lBxVtHOW+xHY7Md3V630aaDkU7nH6/PvLG2FyRSAAAAAElFTkSuQmCC";
    private LOADING_ICON: string = "data:image/gif;base64,R0lGODlhIAAgAJEDAMbvwFTOQ37acf///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJCgADACwAAAAAIAAgAAACUpyPqcvtD6Oc1AAhALq5HhEEAgKKnlGOX6h6nHa850y7GCzJS0rxO9sDLnS5W+2ITCopxEiTJJT4FNNIdWOcPJdcWyeW9VSvk3G00tx21+y2owAAIfkECQoAAwAsAAAAACAAIAAAAk6cj6nL7Q+jnNQAIUB9IgSxOd0XNldWpup6alPLjOAkx94s1Qv8Yu4KDAqHJh+Fp9DlbjYSjbkz9lDEagk5wFKUA+4WavBOf1mp9YxOOwoAIfkECQoAAwAsAAAAACAAIAAAAkecj6nL7Q+jnNQAIUB9IgSxOd0XNldWpup6aisygu8Rz0dr5/rOU/j0U9QmQ6FHJikmghFm7/lgOolHWDWkVPowLgsXCg6LCwAh+QQJCgADACwAAAAAIAAgAAACQZyPqcvtD6Oc1AAhQH0iBLE53Rc2V1am6npqKzKC7xHPR2vn+s5TeF4DemS2X++INA6UlKDBWYFCfRiXpYrMarcFACH5BAkKAAMALAAAAAAgACAAAAI7nI+py+0Po5zUACFAfSIEsTndFzZXVqbqemorMoLvEc9Ha+f6zlN4XgN6ZLZf74g0zoI25kyJjEqniAIAIfkECQoAAwAsAAAAACAAIAAAAjecj6nL7Q+jnNQAIUB9IgSxOd0XNldWpup6aisygu8Rz0dr5/rOU3heA3pktl/viEwql8ymM1cAACH5BAkKAAMALAAAAAAgACAAAAIynI+py+0Po5zUACFAfSIEsTndFzZXVqbqemorMoLvEc9Ha+f6zvf+DwwKh8Si8YhUFQAAIfkECQoAAwAsAAAAACAAIAAAAiqcj6nL7Q+jnNQAIUB9IgSxOd0XNldWpurKtu4Lx/JM1/aN5/rO9/4vKwAAIfkEBQoAAwAsAAAAACAAIAAAAiqcj6nL7Q+jnNQBIUB9IgSxOd0XNldWpurKtu4Lx/JM1/aN5/rO9/6vKgAAIfkEBQoAAwAsGAAKAAQABAAAAgeEZBGneFAAACH5BAUKAAMALBgAEgAEAAQAAAIHhGQRp3hQAAAh+QQFCgADACwSABgABAAEAAACB4RkEad4UAAAIfkEBQoAAwAsCgAYAAQABAAAAgeEZBGneFAAACH5BAUKAAMALAQAEgAEAAQAAAIHhGQRp3hQAAAh+QQFCgADACwEAAoABAAEAAACB4RkEad4UAAAOw==";
    @override
    public render(): React.ReactElement {
        const image = this._getSharedStatus(this.props.isExternallyShared);
        if (image != "") {
            return (
                <img src="{image}" width="17px" />
            );
        }
        else {
            return ();
        }
    }
    private _getSharedStatus(value: number): string {
        if (value == 0)
            return this.LOADING_ICON;
        if (value == 1)
            return this.SHARED_ICON;
        if (value == 2)
            return "";
    }
}

The github project shows you how to create the column for the list. It is a standard text column, that doesn’t show up in any New/Edit forms. It only shows up in the view field. I’m using a pnp template to add it to the site collection.

Taking it further

The spfx-isExternallayShared field customizer is a very simple version of the one I created for my client. I was concerned with the number of times I was calling the GetSharingInformation, especially on very large libraries with many users. The call is only made for the number of items that are currently displayed on the screen to the user, however, when you start multiplying that by a possible 2000+ users that is a much larger load on SharePoint. Therefore, I moved the calling of the REST API to an Azure Function. Using the uniqueID of the item I was able to also use Redis Cache.

The first person who visited the library for each row would perform the actual GetSharingInformation call within the Azure Function, as there is nothing in the Redis Cache, then store either a 0 (not externally shared) or 1 (externally shared) with the UniqueID of the item as the key in Redis Cache. The second call to the library, the Azure Function would first check the cache, and just return that value.

The downside was the additional cost of running Azure Functions and Redis Cache, and that the externally shared value was no longer up to date and could be an hour out of date.

UPDATE: It turns out moving it to an Azure Function, where the calls are being made by one App account causes throttling issues. The client had over 79k calls made in an hour (many document libraries and users), and the App token that I was using got blocked by Microsoft. After further conversations with technical people at Microsoft, the calls need to be made in the context of the user. Since I’ve implemented the simple solution above, we haven’t noticed any throttling issues. Therefore if you want to move to an Azure Function, any calls back into SharePoint should really be done in the context of the user.

Advertisements

Externally Sharing – GetSharingInformation REST API


Externally Sharing Series:

My last two blogs I have been explaining how to set up your tenant for External Sharing, and as a user how to share a document to an external user. At the end of my last blog post I explained how you can view which documents have been externally shared, by navigating to Site Usage and viewing the Externally Shared tab.

However, as stated previously, this section is flawed in a couple of ways.

  1. It relies on Search. Meaning that documents may not show up for a while.
  2. It only shows documents that have been externally shared, and the external user has viewed the file. This is because there is a flag (ViewableByExternalUser) on the file that gets updated, only after an external user has viewed the file, not when you have shared it.
  3. Following on from the last point, your site could have lots of documents externally shared, but you would never know if no one externally has viewed the file.
  4. Sharing of a folder will show up the folder in the list, (if external user goes directly to the folder). However, any items that are within the folder, which would also be externally shared, do not show up in this list. (Even if the external user has viewed the files)

If you had a content query webpart and want the same results to be brought back as it shows you in the ‘Shared externally’ tab you need to set your Search Query as follows:

Path:"<a href="https://examplesite.sharepoint.com/">https://examplesite.sharepoint.com/</a>" (IsDocument:"True" OR contentclass:"STS_ListItem") ViewableByExternalUsers=TRUE

GetSharingInformation REST API

The above ‘Externally Shared’ tab and search call, although it can bring back documents, it wasn’t what my client really wanted. They wanted to know straight away if the document was externally shared or not.

I emailed Stephen Rice (Product Manager for SharePoint and OneDrive for business) at Microsoft, and he said there wasn’t a current way of tagging the documents if they were externally shared at the time of sharing the document. The ViewableByExternalUsers is only updated when the user enters the document.

What I couldn’t understand was when I click the Info Panel for a document, if it was externally shared, it would state that the item was shared with guests. This was before an external user access the document.

Using Chrome network capture, I investigated what happens when the Info Panel loads up, and one call caught my attention.

https://tenant.sharepoint.com/_api/web/Lists(@a1)/GetItemById(@2)/GetSharingInformation?@a1='listguid'&@a2='itemID'&$Expand=permissionsInformation,pickerSettings

This call is made via a POST call.

Looking at the Preview response and digging further down into the results that returned, the permissionsInformation section contains all the Sharing links, and groups and users that the item is shared with.

permissionsInformation > links > results

Contains an array of link results. This includes internal sharing links, anonymous links and external links.

linkDetails

Important part of the linkDetails:

  • Created & CreatedBy – When the link was created and by who.
  • Expiration – Only used for anonymous links
  • HasExternalGuestInvitees
    Indicates if any users that have been invited are external users
  • IsEditLink – Set to true for contribute, false for read
  • Invitations See below
  • LinkKind – Enumeration (Microsoft.SharePoint.Client.SharingLinkKind)
    • 0 – Uninitialized
    • 1 – Direct
    • 2 – OrganizationView – People within ‘tenant’ Read
    • 3 – OrganizationEdit – People within ‘tenant’ Contribute
    • 4 – AnonymousView
    • 5 – AnonymousEdit
    • 6 – Flexible – Used in External Sharing with email address.
Invitations > results

The invitation sections gathers the information about the person who doing the inviting ‘InvitedBy‘ and who has been invited “invitee”. You also have the exact time and date when the invite was sent out.

linkMembers

I have discovered that the linkMembers only has values after the external user has visited the link.

permissionsInformation > links > results

The principals results are the groups and members that have access to the documents. If the external user has been previous added to your tenant via a site share, or document share before December 2017, then they will have an actual account in your tenant. Anything shared with them will show up here, instead of through the Shared Links. The Paul Matthews shown in the screenshot below is actually my personal email address and therefore an external user.


The role are:

  • 0 – None
  • 1 – Can View
  • 2 – Can Edit
  • 3 – Owner

principal

The principal contains the user or SP Group information. For users (principalType 1) this includes if they are external or not.

How to use this information?

The last section showed you all about the GetSharingInformation REST API call. It has to be done with a POST call, but apart from that, it does obtain all the Sharing Information in regard to that item. The only one thing I would say it is lacking is that if a SP Group that is assigned to that document had an external user in it. (For example, the members group, because you have externally shared the site) the isExternal flag on the principal of that group isn’t set to True.

Now knowing about GetSharingInformation REST API call I was able to create a SPFX field customizer. My next blog post will show you how I built the SPFX field customizer.

Externally Sharing – User experience with SharePoint Online


Externally Sharing Series:

Continuing from my previous blog “Externally Sharing – Settings within SharePoint Online” which shows you the different configuration settings within your tenant to enable External Sharing, this blog post is showing from a user perspective how to share, and from the external user’s perspective on the email they receive and how they sign in.

As an internal user sharing

From a document library/list, a user can easily share a document/folder with an external user.

  • Click on the document that you wish to share.
  • From the ribbon click on Share
  • From the dialog, select the drop down.
  • Select Specific people, optionally untick Allow editing if you only wish to give read access.
  • Put in the email address.
  • Click Send

An email will be sent to the user.

To find out who the document has been shared with, click the tick next to the document, then open the information panel. Under Has Access, click Mange Access.

If the user you have shared with, is found within your tenants directory, they will be added as a person to the manage access section. (Cliff K) in screenshot above. If the external user is not found in your directory, then a link will be created. (The top link in the screenshot above). By hovering your mouse over the image below the link, will show you who that person is.

An external user experience

An external user will receive an email from no-reply@sharepointonline.com. Please Note: The email might be in the users Junk Email.

The external user will need to click the link. They will be taken to a page, where they need to verify that it is them.

The external user will need to click the Send Code button. An email will be sent to their email address and be given a code. Please note: This email could also end up in the Junk folder.



The external user will then gain access to the file.

What documents are externally shared?

Unless you are the only user on the site, you will not know what documents have been externally shared. There is a way to see what documents have been externally shared for a given site.

  • Go to Site Contents.
  • Along the menu bar click the Site Usage button.
  • Then click the Shared externally tab.

This will display the externally shared items.

Issues with the Externally Shared tab

Although the above shows you which documents are externally shared, it is flawed in a couple of ways.

  1. It relies on Search. Meaning that documents may not show up for a while.
  2. It only shows documents that have been externally shared, and the external user has viewed the file. This is because there is a flag on the file that get’s updated, only after an external user has viewed the file, not when you have shared it.
  3. Following on from the last point, your site could have lots of documents externally shared, but you would never know if no one externally has viewed the file.
  4. Sharing of a folder will show up the folder in the list, (if external user goes directly to the folder). However, any items that are within the folder, which would also be externally shared, do not show up in this list. (Even if the external user has viewed the files)

This blog post has described how you would share a document with an external user, and how that external user would then gain access to the document. Lastly, I have shown where you can view which documents have been externally shared, although it isn’t great.

My next post will be more development focused, and explain how I’m able to work out if an item has been externally shared or not.


Externally Sharing – Settings within SharePoint Online


Externally Sharing Series:

It is possible to share content from your SharePoint environment to external users. These users are not part of your tenant. They might have their own tenant within their company, or they might just be a person with a personal email address, such as a live.com or gmail.com.

There are few setup steps within your tenant to allow/disallow external Sharing.

SharePoint Admin center

By navigating to your SharePoint Admin Center, https://<tenant>-admin.sharepoint.com on the left hand side of the screen, there is a sharing navigation link. This takes you to the ExternalSharing.aspx page.

Sharing outside your organization

This part of the page you are able to disable externally sharing completely, or open it up granular.

  • Don’t allow sharing outside your organization
    • This disables externally sharing
  • Allow sharing only with the external users that already exist in your organization’s directory
    • If you have previous shared in the past (especially before Microsoft changed how the External Sharing worked – Dec 2017), or shared a entire site with an external person, their account get’s put into your Organisation as a guest account. Having this option on, will continue to allow sharing with those people, but prevent new external users to be shared with.
  • Allow users to invite and share with authenticated external users
    • This option, allows users to externally share, but prevent them sending anonymous links. New external users can be invited. (Personally, I feel this option is also missing an expired option, where a link only exists for x number of days for external users, hopefully this is something Microsoft will include eventually)
  • Allow sharing to authenticated external users and using anonymous access links.
    • This options completely opens up the external sharing. Your users can externally share with anyone, without without requiring the external user to authenticate. With anonymous sharing, you also get the option to expire links after x number of days, and you can set the access link types that get sent out to users. By setting Files or Folders to ‘View’, this will mean every anonymous external link sent out will always be read only. If set to ‘View and Edit’ for files or ‘View, Edit, and Upload’ for folders, that allows the person who is sending out the anonymous external link to decide if they want to apply Read only or Contribute permission for the link.

Who can share outside your organization

If your organisation wanted to only allow a group of people to externally share, here they can do that. This can be done by using individual people names, or even with an AD group. What is nice here, is if you don’t trust a group of people to allow anonymous sharing, you can also assign a different group of people to allow anonymous sharing.

Default link type

When someone selects a document/folder and clicks on the Share option you can default the link type, so the user doesn’t need to switch each time.

Direct – specific people: (Screenshot below – Yellow option) Specific people

Internal – only people in your organization: (Screenshot below – Blue option) People in <tenantName>

Anonymous Access – anyone with the link: (Screenshot below – Green option) Anyone

The tick option of use shorter links when sharing files and folders

Default link type

Here you can set the default permission of either view or edit when someone creates a sharing link. This isn’t just for external links, this applies to anonymous, internal and direct links. If your company is quite security conscientious, setting this to View is a good idea.

Additional settings

As stated on the page, these settings do not apply to anonymous access links.

  • Limit external sharing using domains (applies to all future sharing invitations).
    • If your company only works with a couple of 3rd party companies, and your documents shouldn’t be shared with anyone else, this is a good option to use. Here you can ‘whitelist’ domain names to send to.
  • Prevent external users from sharing files, folders, and sites that they don’t own.
    • Once you have externally shared your document to someone, do you want them to then be able to share onto someone else that you haven’t authorised? If not, then ensure this is ticked. If it their own document that they have uploaded/created that will be able to share that.
  • External users must accept sharing invitations using the same account that the invitations were sent to.
    • When you send an link to someone, by having this option selected, they will only be able to sign in using the email address you sent the invite to. This option selected can also cause problems if you send the link to someone personal email, and they also have their own 365 account, as their 365 cookie token can get picked up, and they receive the error message that the account they are using to sign in with is not the same account the invite was sent to.
  • Require recipients to continually prove account ownership when they access shared items.
    • Once the external user has logged in once without this ticked a cookie is put on their machine to allow them access to that document again without require to re-authenticate with code.

Notifications

These notifications are for when sharing using One-Drive for business. You can optionally select if an email will be sent or not for the following 3 options.

  • Other users invite additional external users to shared files.
  • External users accept invitations to access files.
  • An anonymous access link is created or changed.

OneDrive External Sharing

There is also a configuration page for OneDrive external sharing.

Go to the OneDrive admin centre. Click the Waffle -> Admin. Under Admin centers click OneDrive. Then on the left-hand navigation, click Sharing.

Note that these setting when changed here, will affect the settings you set in SharePoint Admin centre for Sharing. So, change something here, then head back to the SharePoint Admin Centre sharing page, and the changes will reflect there. However, there is one setting here, that isn’t in the SharePoint Admin Centre sharing page, and that is OneDrive permissive sharing.

The above sections allows you to set the Sharing settings for SharePoint different to OneDrive. In the above screenshot, I’ve allowed Sharing (not anonymous) in SharePoint and prevented any external sharing in OneDrive.

Please note: You cannot set the OneDrive to be more permissive than SharePoint, As stated underneath the sliders.

Site Collection sharing

In SharePoint Admin Center in the Site Collections, you can change some of the tenant sharing settings just for that give site collection. Select the site collection you wish to change and click Sharing in the ribbon.

Sharing outside your company

At the site collection level, you are able to choose a option for Sharing outside your company, as long as it’s less permissive than your tenant settings.

Site collection additional settings

  • Limit external sharing by domain
    • Here you can create either a blacklist to block sending external sharing links to certain domains, or ‘whitelist’ a bunch of domains so that external sharing can only go to them.

Allowing non-owners to invite new users

By clicking the “Turn off sharing for non-owners on all sites in this site collection” will only allow people in the Owners group to share anything with anyone externally. Once turned off, it looks like there is no way of turning it on again. They way you turn it back on is going into the Site Collection -> Site Settings -> Site Permissions -> Access request settings. Here you can tick the top check box “Allow members to share the site and individual files and folders” (More explaining on the Access Request Settings in next section)

Site Collection (in the site) settings

By going to the actual root site, there is one more place where you can affect sharing settings. This is the Access Request Settings.

Site Settings -> Site Permissions. Then Access Request Settings in the ribbon.

There are 3 settings here.

Allow members to share the site and individual files and folders

  • With this ticked, members can share.

    • However, with this just ticked they cannot share the site. When a user tries to share the site the Share button clicks but nothing happens.
  • With this unticked you are turning off sharing for everyone except for Owners.

Allow members to invite others to the site members groups.

  • With this ticked. Any user that is part of the Members group will be able to Share the site. It doesn’t matter if a user has permissions in the site, say in a different group, unless they are part of the members groups, they will not be able to share the site.

Allow access requests

  • With this ticked and with an email address put in place. When a member attempts to share a site, an owner must allow the access. Requests can be found at: Site Settings -> Access requests and invitations.

In this blog post I have explained the different areas where you can set up Externally Sharing in your tenant/site. In my next blog post I’m going to go through the steps as a user, sharing documents/folders with external users and the steps the external user goes through to sign in.

Azure function failing with “Object reference not set to an instance of an object”


I have an Azure application that uses an Office 365 App only token. Followed similar instructions to the one here: https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread

I’m using OfficeDevPnP to connect to SharePoint with my Office 365 App only token. Demo code shown below.


string clientId = GetApplicationSetting("clientId");
string tenant = GetApplicationSetting("tenant");
string certificateFilename = GetApplicationSetting("certificateFilename");
string certificatePath = GetCertificatePath(exCtx.FunctionAppDirectory, certificateFilename);

SecureString certificatePassword = ConvertToSecureString(GetApplicationSetting("certificatePassword"));

string tenantAdminUrl = GetApplicationSetting("tenantAdminUrl");
log.Info("Got details");
OfficeDevPnP.Core.AuthenticationManager authManager = new OfficeDevPnP.Core.AuthenticationManager();

try{
   using (var clientContext = authManager.GetAzureADAppOnlyAuthenticatedContext(siteProperties.Url, clientId, tenant, certificatePath, certificatePassword))
   {
     Web web = clientContext.Web;
     clientContext.Load(web, w =&gt; w.Webs, w =&gt; w.Title, w =&gt; w.Url);
     clientContext.ExecuteQueryRetry();
   }
}catch(Exception ex){
log.Info("Failed! " + ex.Message);
}

The Azure function that has been deployed and running daily for a while now, when all of a sudden it stopped working last Wednesday. Just as it was authenticating with SharePoint it displayed the error message “Object reference not set to an instance of an object”

The stack trace is below:

2018-02-06T09:45:28.347 Failed! Object reference not set to an instance of an object.
2018-02-06T09:45:28.364 StackTrace:    at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.RunAsyncTask[T](Task`1 task)
   at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.AcquireToken(String resource, ClientAssertionCertificate clientCertificate)
   at OfficeDevPnP.Core.AuthenticationManager.<>c__DisplayClass36_0.<GetAzureADAppOnlyAuthenticatedContext>b__0(Object sender, WebRequestEventArgs args)
   at Microsoft.SharePoint.Client.ClientRuntimeContext.OnExecutingWebRequest(WebRequestEventArgs args)
   at Microsoft.SharePoint.Client.ClientContext.GetFormDigestInfoPrivate()
   at Microsoft.SharePoint.Client.ClientContext.EnsureFormDigest()
   at Microsoft.SharePoint.Client.ClientContext.ExecuteQuery()
   at AppFunctionDemo.Function2.GetTenantSiteProperties(AuthenticationManager authManager, String tenantAdminUrl, String clientId, String tenant, String certificatePath, SecureString certificatePassword, TraceWriter log)
   at AppFunctionDemo.Function2.Run(TimerInfo myTimer, ExecutionContext exCtx, TraceWriter log)

After a call with Microsoft, it turns out that the Azure Functions are slowly being updated throughout the tenants.

Last one was 11 days ago (at time of writing this) https://github.com/Azure/azure-functions-host/releases

If you go to your Azure functions and look in the Application Settings, you will see an application setting saying:


FUNCTIONS_EXTENSION_VERSION: ~1

This means it will keep itself up to the latest date. By changing the version “~1” to a previous version “1.0.11490” after saving the Application Settings, and re-running my Azure function, I no longer get an error message saying “Object Reference not set to an instance of an object” and the function runs to completion.

There is obviously a bigger bug issue here, but at least my Azure functions can continue running until then.

SPFX obtaining the URL Query Parameters


This is a very quick blog post, but I needed to obtain the URL parameters of the page for an SPFX webpart.

There is a built in method for obtaining URL parameters.

import { UrlQueryParameterCollection } from '@microsoft/sp-core-library';

...

var queryParms = new UrlQueryParameterCollection(window.location.href);
var myParm = queryParms.getValue("myParam");

I have found this very useful with Console Logs. I have a method called “logMessageToConsole” passing in the message. If I find a parameter called debug in my query string, then all the console logs are written out.

private logMessageToConsole(message:string)
{
var queryParms=new UrlQueryParameterCollection(window.location.href);
  if(queryParms.getValue("debug")){
    console.log(message);
 }
}

That way I only need to add ?debug=1 to the end of my URL or &debug=1 if there are already querystring parameters, and then I can see all my console messages. This is useful to quickly debug on production environments.

View Trace Logs with PnP Provisioning Templates using PowerShell


When running the Get-PnPProvisioningTemplate and the Apply-PnPProvisioningTemplate it is sometime necessary to see what is going on while the call is processing.

Just run the following command first before calling your Get/Apply -PNPProvisioningTemplate command.

Set-PnPTraceLog -On -Level:Debug

If you need to turn it off again

Set-PnPTraceLog -Off