![Building Proxy Provider for SharePoint Framework and Microsoft Graph Toolkit](https://spblog.net/image.axd?picture=//www.sharepointeurope.com/2021/spfx-mgt-proxy/title.jpg)
The Microsoft Graph Toolkit (MGT) is a collection of reusable, framework-agnostic components and authentication providers for accessing and working with Microsoft Graph. The components are fully functional right out of the box, with built in providers that authenticate with and fetch data from Microsoft Graph.
MGT has a nice and simple integration with SharePoint Framework with just one line of code:
Providers.globalProvider = new SharePointProvider(this.context);
However, MGT supports other provider types as well. Like Proxy provider. In some cases, it makes sense to use the Proxy provider inside your SharePoint Framework solution instead of the SharePointProvider. Here is why:
- you have a backend API that talks to MS Graph with on-behalf-of (OBO) flow. You don’t use JS code to talk to MS Graph and rely solely on the backend
- you don’t want to maintain many “webApiPermissionRequests” entries inside package-solution.json, because every new permission requires re-deployment
If the above is true for you, then you can implement the Proxy provider for MGT instead of SharePointProvider.
The source code for this sample is available at GitHub here.
Solution overview
This is the approximate schema of how it works:
Explanation:
MGT is configured to use a Proxy provider with the URL to our backend. The backend uses Azure AD for authentication and OBO flow to request access tokens for MS Graph in the proxy controller. As an example, the web part uses the <Person> MGT component. In the beginning, we need an access token for our backend, thus we use aadTokenProvider SPFx API to get an access token and init Proxy provider.
Proxy provider, which is running on SPFx, uses aadTokenProvider and generates an access token for our backend API. Then Proxy provider sends authenticated requests to the backend with the access token. Proxy backend controller exchanges tokens using OBO flow and then passes requests further to MS Graph API.
MS Graph returns data back, proxy backend controller, in turn, returns data to <Person> MGT control on the SPFx web part. <Person> updates itself based on the received data.
App registration
We’ll start with the app registration.
- In your Azure Add under App Registrations click “New Registration”.
- Provide a name, click “Register”.
- Under Authentication enable “Access tokens” and “ID tokens” checkboxes.
- Under “Certificates and secrets” generate a new Client Secret and save it (we need it later).
- Under “API Permissions” add all permissions needed for your app. In my sample, I use Person MGT control, which needs only User.Read. If you need more, like Calendars, Mails, etc, then simply add all required.
- Grant admin consent for all permissions on the same page.
- Under “Expose an API” generate an App ID URI, i.e. api://<api name>/<client id>, for example api://spfx-mgt-proxy/14bfb200-fe7b-44ac-b19f-08d9fc2f833e
- Click on “Add scope”, give it a name “user_impersonation”, enabled for admins and users, display name “Access the API”
- Save all changes.
SharePoint Framework web part
Scaffold a new SPFx webpart, add MGT dependencies:
npm install mgt-element mgt-proxy-provider mgt-react --save
In the default web part code add init method:
public onInit(): Promise<void> {
Providers.globalProvider = new ProxyProvider("https://localhost:44320/api/GraphProxy", async () => {
const provider = await this.context.aadTokenProviderFactory.getTokenProvider();
const token = await provider.getToken(CLIENT_ID);
return {
Authorization: `Bearer ${token}`,
};
});
return Promise.resolve();
}
Where Client_Id is your app registration’s ClientId (Application Id), https://localhost:44320/api/GraphProxy is our backend proxy URL (will be configured later). In the code above we use the built-in SPFx mechanism for obtaining an access token for our backend API, which serves as a proxy for MGT controls.
The component itself is quite simple:
import * as React from 'react';
import { FC } from 'react';
import { Person, PersonViewType, PersonCardInteraction } from '@microsoft/mgt-react';
const MgtProxy: FC = () => {
return (
<div>
<Person personQuery="me" view={PersonViewType.twolines} personCardInteraction={PersonCardInteraction.hover} />
</div>
)
}
export default MgtProxy;
We should also update package-solution.json to include information about our backend API. Add new webApiPermissionRequests entry:
"webApiPermissionRequests": [
{
"resource": "SPFx-mgt-proxy",
"scope": "user_impersonation"
}
]
Package solution:
gulp bundle --ship
gulp package-solution --ship
Upload .sppkg file to the App Catalog and approve permission request from Central Admin (from API Management page).
ASP.NET 5 backend proxy
Our backend is just a regular ASP.NET 5 project configured with Azure AD authentication and a proxy Web API controller inside.
You can easily convert it to the ASP.NET 6 because for this project it’s almost identical setup.
Here is how you should configure services:
public void ConfigureServices(IServiceCollection services)
{
services.AddMicrosoftIdentityWebApiAuthentication(Configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
services.AddCors(o => o.AddPolicy("EnableAll", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
services.AddScoped<MSGraphClientFactory>();
services.AddScoped((provider) =>
{
var factory = provider.GetRequiredService<MSGraphClientFactory>();
return factory.CreateGraphClient();
});
services.AddControllers();
}
In my setup I allow CORS from any hosts, but for your solution you may implement more strict conditions to improve security.
AddMicrosoftIdentityWebApiAuthentication protects our API with Azure AD authentication. By default, it reads the config section with the name “AzureAd“, which we configured before.
EnableTokenAcquisitionToCallDownstreamApi adds support for on-behalf-of (OBO) flow for MS Graph and other services. Under the hood, it uses the MSAL.NET library to perform necessary Azure AD calls.
AddInMemoryTokenCaches uses memory to store tokens so that we don’t call Azure AD every time we need a token.
What is MSGraphClientFactory then? That’s a class, which creates an instance of GraphServiceClient:
public class MSGraphClientFactory
{
private readonly ITokenAcquisition _tokenAcquisition;
// put all needed for your solution MS Graph scopes here
private readonly string[] _scopes = new[] { "User.Read" };
public MSGraphClientFactory(ITokenAcquisition tokenAcquisition)
{
_tokenAcquisition = tokenAcquisition;
}
public GraphServiceClient CreateGraphClient()
{
return new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
var result = await _tokenAcquisition.GetAccessTokenForUserAsync(_scopes);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result);
}));
}
}
IMPORTANT. I use only User.Read scope, because I use <Person> component only. If you need more, then add all needed scopes in this class. Don’t forget to update app registration and grant permissions for the tenant afterwards.
_tokenAcquisition.GetAccessTokenForUserAsync is an OBO call (as said backed with MSAL.NET).
This is the proxy Controller:spoiler (click to show)
Most of the code was taken from the MGT sample here.
Run it
To run, hit F5 in Visual Studio and “npm run serve” inside the SPFx solution. Use hosted workbench, it should display currently logged in user info:
Find more great blogs here.
About the Author:
Hello!
My name is Sergei and I’m Microsoft MVP (2017-current), SharePoint & Microsoft 365 developer and consultant working with SharePoint and related technologies for more than 10 years.
My full professional profile you can find under LinkedIn.
Reference:
S, Sergeev (2022).Building Proxy Provider for SharePoint Framework and Microsoft Graph Toolkit. Available at: https://spblog.net/post/2022/02/01/building-proxy-provider-for-sharepoint-framework-and-microsoft-graph-toolkit [Accessed: 11th August 2022].