The moment your web part needs more than SharePoint data — a user's manager, their calendar, Teams membership, profile photos — you're talking to Microsoft Graph. The beautiful part of SPFx is that you never handle a token yourself. The framework brokers authentication through a shared Azure AD application, and you just ask for a client. But that convenience hides an approval flow every developer must understand.

The two HTTP clients

SPFx gives you two purpose-built, pre-authenticated clients via this.context:

Calling Graph with MSGraphClient

Acquire the client from the factory, then build the request. Here we read the signed-in user's profile and manager:

import { MSGraphClientV3 } from '@microsoft/sp-http';

private async loadProfile(): Promise<void> {
  const client: MSGraphClientV3 =
    await this.context.msGraphClientFactory.getClient('3');

  const me = await client.api('/me')
    .select('displayName,jobTitle,mail')
    .get();

  const manager = await client.api('/me/manager')
    .select('displayName,mail')
    .get();

  console.log(me.displayName, '→ reports to →', manager.displayName);
}
The Graph client uses the same OData query options you already know — $select, $filter, $expand, $top — exposed as .select(), .filter(), etc. Always .select() the exact fields you need; Graph endpoints often return large default property sets otherwise.

Declaring permission requests

SPFx web parts get their tokens from a single shared Azure AD application in your tenant (the "SharePoint Online Client Extensibility" service principal). By default it can do very little. To call /me/manager you must declare the permission your solution needs in config/package-solution.json:

{
  "solution": {
    "name": "contoso-graph-webpart",
    "id": "…",
    "webApiPermissionRequests": [
      { "resource": "Microsoft Graph", "scope": "User.Read.All" }
    ]
  }
}

This does not grant the permission — it requests it. When you deploy the .sppkg, a tenant administrator must approve the request in the SharePoint admin center under Advanced → API access (or via PnP PowerShell Approve-PnPTenantServicePrincipalPermissionRequest). Until then, your Graph call fails with a token error.

Permissions are tenant-wide and shared. The grant applies to the shared extensibility principal, so any SPFx solution in the tenant can use it. Request the least-privileged scope you need — prefer User.Read.All over Directory.Read.All — because you're widening the blast radius for the whole tenant, not just your web part.

Calling a custom API with AadHttpClient

For your own Azure-hosted API secured by Azure AD, use AadHttpClient. You pass your API's application ID URI as the resource, and SPFx fetches a token scoped to it:

import { AadHttpClient, HttpClientResponse } from '@microsoft/sp-http';

private async callCustomApi(): Promise<void> {
  const client: AadHttpClient =
    await this.context.aadHttpClientFactory
      .getClient('api://contoso-orders-api');  // your AAD app's URI

  const res: HttpClientResponse = await client.get(
    'https://contoso-orders.azurewebsites.net/api/orders',
    AadHttpClient.configurations.v1);

  const orders = await res.json();
}

The matching permission request names your API instead of Graph:

"webApiPermissionRequests": [
  { "resource": "contoso-orders-api", "scope": "user_impersonation" }
]

Application vs delegated permissions

One crucial limitation: SPFx clients always run as the signed-in user (delegated permissions). There is no app-only token from SPFx. If an operation needs application permissions — acting without a user, or accessing data the user shouldn't see directly — you must front it with your own API (an Azure Function) that holds the app credential, and call that from SPFx with AadHttpClient. I cover building exactly that pattern in a later article on securing custom APIs.

Practical checklist

Once you're comfortable acquiring tokens through these clients, the entire Microsoft 365 surface opens up to your web parts — users, groups, Teams, mail, calendars, files, and your own line-of-business APIs — all without ever touching a client secret in browser code.

DS

Dhaval Shah

Cloud Solution Architect — 18+ years across SharePoint, Microsoft 365, and Azure AI.