We’re trying to build an integration that connects with Microsoft Business Central for our customers.
This integration needs access to read and write data to Microsoft Business Central.
Our setup
- We’ve created a multi-tenant application with Web authentication.
- We’re authenticating the
common
endpoint as we don’t mind if personal or organisation companies use our app.
- We’ve set up a client secret and use this when dealing with the
/token
endpoint.
- Configured the application API permissions to request Dynamics
API.ReadWrite.All
and Microsoft Graph offline_access
- We request the scopes
https://api.businesscentral.dynamics.com/.default offline_access
- These permissions have had admin consent, and we have done this both interactively using https://login.microsoftonline.com/common/adminconsent?client_id={application_client_id} and through the portal using
Grant admin consent
- We are able to successfully gain an
access_token
and refresh_token
using the authorization code flow.
- This
access_token
has the correct access that we expect and we can, for one day until it expires, access the business central company like we expect.
Where things go wrong is when we try to refresh the access_token using a valid, non-expired refresh token.
POST /common/oauth2/v2.0/token
Host: <https://login.microsoftonline.com>
Content-Type: application/x-www-form-urlencoded
Authorization: Basic xxx (client_id & client_secret)
refresh_token=xxx...
&grant_type=refresh_token
No matter what we do, we get
{ "error":"invalid_grant","error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID 'xxxxxx-00ca-4a05-9d8c-9dd2da526591' named 'xxxxxx-Development'. Send an interactive authorization request for this user and resource. Trace ID: 1aa7c10f-f6f6-44a3-8e46-52f547d60c00 Correlation ID: e4df8738-4d07-4627-9676-929f076f5a7d Timestamp: 2025-05-01 03:56:40Z","error_codes":[65001],"timestamp":"2025-05-01 03:56:40Z","trace_id":"1aa7c10f-f6f6-44a3-8e46-52f547d60c00","correlation_id":"e4df8738-4d07-4627-9676-929f076f5a7d","suberror":"consent_required"}
What we’ve tried when refreshing the token
- Calling with and without scopes with and without
offline_access
- Calling
organizations
endpoint and the {tenantId}
endpoint instead of common
- Revoking and regranting admin consent via the portal and the admin consent url.
- Passing
client_id
and client_secret
in the form body instead of as the basic auth header.
- Trying permutations of business central scopes, but only
/.default
seems to work
One thing that stands out as incredibly strange is that if we remove the https://api.businesscentral.dynamics.com/.default scope, and only request openid offline_access, we are able to refresh the token, but we have no access to business central - which makes it useless to us. So this seems to be specific to our requesting scopes to business central.
Appreciate any help here!