Named Credential with OAuth Authentication Provider does not refresh access token

calloutnamedcredentialsoauth2refresh-token

This is an extremely annoying unsolved issue that I see more people than I have been, and are(?), struggling with without a solution to date. Please help! Workaround or guidance.

  • Auth Provider (AP): Azure Ad via Open ID Connect. Works fine.
  • Named Credential (NC): OAuth2 using the AP, with Scope including offline_access. Upon saving the NC everything looks and works fine, authorization granted and NC saves without warning about missing refresh token (which is the case if omitting the offline_access in Scope).
  • Apex (future) callout (CO): works fine until the original access token expires, upon which a 401 is received.

I.e. no automatic refresh of the access token, despite having a refresh token.

I have tried adding a "manual" retry call in my Apex CO upon receiving a 401, to no avail.

I have not (yet) set up a proxy in front of the Azure AD auth and token endpoints to see what happens behind the scenes by the NC and AP, and wish it doesn't come to that.

I tried using Beeceptor as a proxy in front of MS Azure AD authorize and token urls, but that does not work at all. Stops at a failing sign in dialog (I can't share a screen shot for privacy reasons).

Error logs:

05:19:50.682 (1682989819)|NAMED_CREDENTIAL_REQUEST|NamedCallout[Named Credential Id=0XAIY00000001PW, Named Credential Name=Azure_Entity_SFChangeEvent, Endpoint=https://prod-12.westeurope.logic.azure.com:443/workflows/qwerty/triggers/manual/paths/invoke?api-version=2016-10-01, Method=POST, External Credential Type=EXTERNAL, HTTP Header Authorization=Method: Bearer - Authorization Credential Hash: -2022866655, Content-Type=null, Request Size bytes=776, Retry on 401=True]
05:19:50.684 (1684291301)|NAMED_CREDENTIAL_RESPONSE|NamedCallout[Named Credential Id=0XAIY00000001PW, Named Credential Name=Azure_Entity_SFChangeEvent, Status Code=401, Content-Type=application/json; charset=utf-8, Response Size bytes=166, Overall Callout Time ms=459, Connect Time ms=278
05:19:50.109 (1692101634)|CALLOUT_RESPONSE|[18]|System.HttpResponse[Status=Unauthorized, StatusCode=401]
05:19:50.187 (1701634210)|EXCEPTION_THROWN|[15]|AzureFutureCallout.AzureCalloutException: 401 Unauthorized {"error":{"code":"SecurityTokenExpired","message":"The provided token is expired and valid till: '6/3/2022 8:39:31 AM' UTC. Please try again with refreshed token."}}

Best Answer

In this particular case, the issue was that Azure AD returns an error upon using the refresh_token for a new access_token.

So, on the Salesforce side of things everything worked fine and as should be.

For anyone interested Azure AD returned when Salesforce tried to refresh the access token: "AADSTS90009 Application is requesting a token for itself. This scenario is supported only if resource is specified using the GUID based App Identifier."

This is resolved by creating two "App registrations" in Azure AD.

One App registration for the service (API/endpoint) on Azure side of things. Having nothing more, basically, than scope definition, such as "api:/..

And one App registration for the client, i.e. Salesforce (with my callout and its auth.provider and named credential). Having a client id, secret callback uri, and a permission to use the scope of the App registration above.

I.e. the above becomes separate from he client's client id guid.

My Salesforce Auth Provider (OpendID) authenticates using the client app registration client Id and secret.

And my Salesforce Named Credential uses the scope definition from the service app registration.

Related Topic