Using PnPjs to send requests to MS Graph with SharePoint Framework 1.6

SPFx 1.6 was released recently and a lot of new and interesting features were introduced. AadTokenProvider, AadHttpClient, MSGraphClient  went to GA, which are my favorite features. One of the common thing in SPFx development is accessing other resources, protected with Azure AD. For example you might have your LOB API with Azure AD protection and you want to consume that API from SPFx web part (extension). Before SPFx 1.6 it was a bit challenging, because you have to deal with cookies attached to your asynchronous http request or with custom “patched” adal.js implementation. SPFx 1.6 features mentioned earlier drastically simplify the task to access Azure AD protected resources. Now you can access Azure AD APIs (including Microsoft APIs like MS Graph) from SPFx with ease!

I’m pretty sure you know about PnPjs library. It has a lot of cool features, among them a fluent interface to SharePoint and Graph API. WIth SPFx 1.6 release you can use PnPjs as your Graph client without hassle. Read further to find out how.

First things first. Let’s find out what are these “AadTokenProvider, AadHttpClient, MSGraphClient ” things are about. They are all classes, they simplify work with Azure AD protected resources. Currently you can't use them in local workbench (because of redirect url like spfxsinglesigon.aspx, which is hosted in your tenant). You should use hosted workbench instead. To get rid of possible confusion, here is a brief description for every of them:

  • AadTokenProviderthink of it as the lowest possible level. This class is responsible for access token generation (implicit flow with adal.js under the hood) for any Azure AD protected resource. It has only one method getToken. This class used internally by AadHttpClient and MSGraphClient. Use AadTokenProvider if you need access tokens only. For example you might use of third party library for sending asynchronous http requests like jQuery, fetch API or axios. In that case you should use AadTokenProvider  to get access token and attach it to ongoing http request manually
  • AadHttpClientthat’s the next level in “hierarchy”. This client is capable of sending  asynchronous http requests to Azure AD protected resources. It uses AadTokenProvider internally, however actual access tokens are hidden. You should use this client if want send http request to your protected APIs without worrying about token acquisition
  • MSGraphClient  - it’s like AadHttpClient, but prebuilt to work with MS Graph specifically. It also uses AadTokenProvider  for access tokens, but everything is hidden internally. Yet you can use it send MS Graph queries via client.api request and receive strongly typed response with help of @microsoft/microsoft-graph-types

MSGraphClient doesn’t have fluent interface, but PnPjs does. Our task is to use PnPjs for MS Graph interactions. PnPjs has built in mechanism for access tokens requests (via adal.js), however SPFx setup is much simpler, you will see why in a minute. As usual, the source code for the post is available at GitHub here.

We need only a few simple steps:

  1. Create SPFx 1.6 web part and add PnPjs
  2. Create custom fetch client for PnPjs which talks to MS Graph (sounds complicated, but it’s not so difficult with new SPFx 1.6 features)
  3. Update permissions request for web part
  4. Finally use PnPjs and receive some information from MS Graph

Let’s get started!

Create SPFx 1.6 web part and add PnPjs

Don’t forget to update your generator to the latest 1.6 version. Create a basic web part without JavaScript framework. Install PnPjs libraries:

npm install @pnp/logging @pnp/common @pnp/odata @pnp/sp @pnp/graph --save

Optionally you can include it as externals to minify bundle (config.json):

"externals": {
    "@pnp/sp": "https://cdnjs.cloudflare.com/ajax/libs/pnp-pnpjs/1.2.1/pnpjs.es5.umd.bundle.min.js"
  },

Create custom fetch client for PnPjs which talks to MS Graph

As said earlier, we can use AadTokenProvider  for access token acquisition and use such tokens later in third party libraries. AadTokenProvider  perfectly fits for our needs! PnPjs has BearerTokenFetchClient class, which simply adds Authorization header and access token to ongoing http request. We should inherit our fetch client from BearerTokenFetchClient and insert access token acquisition logic with AadTokenProvider . Very simple:

import { BearerTokenFetchClient, FetchOptions } from "@pnp/common";
import { AadTokenProvider } from "@microsoft/sp-http";

export class GraphTokenFetchClient extends BearerTokenFetchClient {
    constructor(private tokenProvider: AadTokenProvider) {
        super(null);
    }

    public fetch(url: string, options: FetchOptions = {}): Promise<Response> {
        return this.tokenProvider.getToken('https://graph.microsoft.com')
            .then((accessToken: string) => {
                this.token = accessToken;
                return super.fetch(url, options);
            });
    }
}

We use AadTokenProvider to get token for MS Graph resource, then we assign token to a property inherited from base BearerTokenFetchClient and that’s it. Why can’t we use BearerTokenFetchClient directly? Tokens might expire, AadTokenProvider  cares about such things. 

Update permissions request for web part

This is a very important step. If you want AadTokenProvider to work, you should provide permission request for corresponding APIs. In case of custom enterprise API that will be user_impersonation scope with API name. In our case your web part should explicitly say, which MS Graph scopes it needs. Add below code in your package-solution.json, under “solution” node:

"webApiPermissionRequests": [
  {
	"resource": "Microsoft Graph",
	"scope": "Group.Read.All"
  }
]

In my sample I want to read all groups, that’s why I’m requesting Group.Read.All scope from MS Graph.

A few important notes regarding permission requests. Before using this web part you should package it for production and upload to your apps catalog site. You will see a dialog saying that you should approve APIs requested in web part. Please read this article Request permissions to an Azure AD application to learn more about webApiPermissionRequests and  approval process.

Use PnPjs and receive some information from MS Graph

Before calling MS Graph, we should initialize PnPjs with our custom fetch client. This can be done in onInit method in a web part:

import { GraphTokenFetchClient } from "../../GraphTokenFetchClient";
import { graph } from "@pnp/graph";
import { AadTokenProvider } from '@microsoft/sp-http';
....
public onInit(): Promise<void> {
    return new Promise((resolve, reject) => {
      this.context.aadTokenProviderFactory
        .getTokenProvider()
        .then((tokenProvider: AadTokenProvider) => {

          graph.setup({
            graph: {
              fetchClientFactory: () => {
                return new GraphTokenFetchClient(tokenProvider);
              }
            }
          });

          resolve();
        })
        .catch(reject);
    });
  }

That’s almost it! Now in render method put a code which talks to MS Graph:

graph.groups.get()
  .then((data) => {
	console.log(data);
  });

In console you will see an array of groups.

The link to the sources used in this post is available here.

Conclusion

SPFx 1.6 introduced a few very powerful classes, which significantly simplify Azure AD authentication in SPFx. You can use it for many different things, what is even better, that they are developer friendly and work very smoothly.