REFRACT Dispatcher API

Authentication

How the Dispatcher's App Registration supports both delegated and client-credentials authentication, and the Microsoft Entra ID gotchas specific to the self-referencing delegated pattern.

The Dispatcher accepts two distinct OAuth 2.0 flows against the same "REFRACT Dispatcher API" App Registration. Both are validated identically once a token arrives - the difference is entirely in how the caller obtains that token.


Client Credentials (service-to-service)

Used by the Power Platform connector, ERP integrations, and any unattended script. A separate App Registration acts as the client, requests a token for api://<dispatcher-app-client-id>/.default, and authenticates with a client secret. No user is ever involved.

Full setup steps live in Power Platform Connector Setup - the same pattern applies to any other client-credentials caller, using a differently-named App Registration for the client.


Delegated (Authorization Code + PKCE)

Used for interactive testing (Postman) and by the SPFx dashboard. Unlike client credentials, this pattern reuses "REFRACT Dispatcher API" as both the client and the resource - there is no separate client app. A human signs in, so no client secret is required; the app authenticates via PKCE instead.

Because client and resource are the same app here, Microsoft Entra ID applies a few rules that don't come up in the client-credentials path. Each one below was hit and resolved while setting up Postman testing against a live Dispatcher.

Required App Registration configuration

Redirect URI under "Mobile and desktop applications"

The redirect URI used for this flow (https://oauth.pstmn.io/v1/callback for Postman; your own scheme for a native app) must be registered under the "Mobile and desktop applications" platform, not "Web". A PKCE-only public client (no secret) registered under "Web" fails token exchange with AADSTS90009 or a generic invalid_request - "Web" platform redirect URIs expect a confidential client.

Scope must be the bare GUID, not the App ID URI

<dispatcher-app-client-id>/.default

not

api://<dispatcher-app-client-id>/.default

When client and resource are the same app, Microsoft Entra ID only supports this "requesting a token for itself" scenario when the resource is identified by its bare GUID application ID. Using the api:// App ID URI form here fails with:

AADSTS90009: Application '<appId>' is requesting a token for itself. This scenario
is supported only if resource is specified using the GUID based App Identifier.

The app must request its own delegated permission

Exposing the user_impersonation scope on the API side is not enough. The app must also list that same scope as a required permission it requests for itself, under API permissions, with admin consent granted. Without this, token issuance fails with:

AADSTS650057: Invalid resource. The client has requested access to a resource which
is not listed in the requested permissions in the client's application registration.

infra/deploy.ps1 automates this: it adds the self-referencing requiredResourceAccess entry and grants admin consent (with retry, since a just-created or just-updated app registration can take up to a minute to propagate to the consent endpoint). If you're working against an app registration created before this automation existed, add it manually:

Azure Portal -> App registrations -> "REFRACT Dispatcher API" -> API permissions -> Add a permission -> APIs my organization uses -> search for "REFRACT Dispatcher API" itself -> Delegated permissions -> user_impersonation -> Add permissions -> Grant admin consent.

Why SPFx never surfaced this

SharePoint's tenant-wide app-catalog deployment consents the SPFx solution's own webApiPermissionRequests entry independently at solution-deploy time. Any other delegated consumer of this same app (Postman, or a future non-SPFx client) needs the self-referencing permission requested and consented explicitly, which is what the deploy.ps1 automation above now handles.

Client-side gotcha: the OAuth relay and the browser mode

Separately from the App Registration configuration above, one client-side (Postman) setting matters: "Authorize using browser" must be checked when using a hosted relay redirect URI such as https://oauth.pstmn.io/v1/callback. That relay is built for the system-browser flow. With the checkbox unchecked, Postman's embedded webview loads the relay page directly and fails with an opaque Error: [object Object] instead of a readable Microsoft Entra ID error - the sign-in itself succeeds, but the redirect handoff back into Postman does not.


JWT Validation

Regardless of which flow produced the token, the Dispatcher validates the same three claims on every request:

ClaimValidatesConfigured via
audToken was issued for this Dispatcher's App RegistrationJWT_AUDIENCE app setting
issToken was issued by this tenant's own Microsoft Entra IDJWT_ISSUER app setting
tidToken belongs to the authorised tenantAZURE_TENANT_ID app setting

JWT_REQUIRED must be true in production

If JWT_REQUIRED is not set to "true", a transient failure reaching the Microsoft Entra ID JWKS endpoint could allow unauthenticated requests to proceed. Always set JWT_REQUIRED=true in production.


Next Steps

On this page