Using an Auth. Provider and Named Credentials in Salesforce with Azure OAuth

Please note: When I refer to “Azure” below I’m referring to Microsoft Azure, the cloud product from Microsoft.

Please note: If you know all about why Auth. Providers and Named Credentials are great and simply wanna know about how to use them with Microsoft Azure feel free to skip down to “How does it apply to Azure”.

All this started some time back when I was at a customer showing how to integrate with Salesforce and call other API’s from Salesforce. In Salesforce we have a great concept called Authentication Providers (“Auth. Provider”) that handles the underlying authentication protocol such as OAuth 2.0. Auth. Providers may be used to provide Single-Sign-On in Communities (our portals) or with Named Credentials. The latter is a way to externalize authentication from a piece of code or functionality. So if I need to call an external API from Apex (our Java-like programming language) I can simply do something like the code below:

HttpRequest req = new HttpRequest();
req.setEndpoint('callout:My_NamedCredential/accounts/list');
req.setMethod('GET');
Http http = new Http();
HTTPResponse res = http.send(req);
System.debug(res.getBody());

As you can see there is nothing here about authentication. Nor is there anything around the actual URL being called. That information is externalized into a Named Credential called My_NamedCredential in this case. The only part I specify is the specific path of the API I’m referring to (here “/accounts/list”). This is great for development as it makes it easier for me the developer but it’s also easier to admins moving changes between environments as the endpoint and credential management is externalized as setup for the org. It means it’s easy to change credentials and endpoints between development, test, QA and production. Awesome!

Setting this up is pretty easy as well and is done in 3 steps:

  1. Start by setting up the Auth. Provider by specifying your client_id (we call it the “Consumer Key”), the client_secret (we call it the “Consumer Secret”), the Authorization endpoint and the token endpoint. The last two you get from your provider – in this case Azure. For now with version 2 of their identity platform they will be https://login.microsoftonline.com/<tenant>/oauth2/v2.0/authorize and https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token respectively (replace <tenant> with your tenant id).
  2. Now create a Named Credential specifying the root URL you would like to call against in the “URL” field. For “Identity Type” select “Named Principal” to use the same credentials across the org or “Per User” to use user specific credentials and set “Authentication Protocol” to “OAuth 2.0”. In “Authentication Provider” select the provider we created above set the scope to use.
  3. Now use the Named Credential as discussed above.

Now let’s discuss what’s special about Azure.

How does it apply to Azure?

Above I was intentionally pretty loose when discussing the scope to set in the Named Credentials. The reason is that this is quite specific when dealing with Azure.

In Azure an access token is actually a Json Web Token (JWT, https://jwt.io) which is a standardized token format containing signed claims that may be verified by the recipient. The payload in a JWT access token from Azure could look like this:

{
  "aud": "2dd1b05d-8b45-4aba-9181-c24a6f568955",
  "iss": "https://sts.windows.net/e84c365f-2f35-8c7d-01b4-c3412514f42b/",
  "iat": 1573811923,
  "nbf": 1573811923,
  "exp": 1573815823,
  "aio": "42VgYJj0Zt3RXQIblViYGkuiHs9dAQA=",
  "appid": "32c0ba71-04f4-4b3a-a317-1f1febd5fc22",
  "appidacr": "1",
  "idp": "https://sts.windows.net/e84c365f-2f35-8c7d-01b4-c3412514f42b/",
  "oid": "394e0c1a-0992-42e7-9875-3b04786147ca",
  "sub": "394e0c1a-0992-42e7-9875-3b04786147ca",
  "tid": "e84c365f-2f35-8c7d-01b4-c3412514f42b",
  "uti": "ESAOZDPFeEydSYxohgsRAA",
  "ver": "1.0"
}

Here the important piece is the “aud” claim as it contains the ID of the application or API on Azure the token is valid for. In this case it’s for an App Registration in Azure.

When we deal with OAuth providers we might be used to deal with standard OpenID Connect scopes like openid, email, profile and offline_access. For Azure which is much more of a generic platform the scope we specify is used to indicate what application we are requesting access to. In Azure the issued access token is specific to the application we request access to and we can only request access for a single application at a time which have some implications:

  1. If you request an access token for an application (App Registration in Azure) the access token is valid for that application only
  2. The access token is not valid for other API’s like the Microsoft Graph
  3. If you do not request an access token for a specific application Azure issues you an access token for the Microsoft Graph API

This may be obvious but it caused me quite some troubleshooting.

OAuth scopes in Azure

When you request an access token from Azure you must specify what API you intend to use it for. There are two versions of the OAuth endpoints (v1 and v2) – in version 1 you use a resource-parameter to indicate the target application to Azure. In version 2 this has been standardized and is now using the standard scope-parameter. It also means that scope is now not simply the OpenID Connect standard scopes (such as openid, offline_access) or the application specific scopes i.e. from Microsoft Graph but is also used to indicate the API you are requesting access to i.e. an App Registration.

Now to the fun, the stuff you just need to know and the stuff which is easy enough to find if you know what to Google for…

Specifying the URI of an App Registration is the scope is not enough. You have to add a scope to the Application URI and unless you’ve defined a custom scope for your app you should use “.default”. So if your App Registration has an URI of “api://2dd1b05d-8b45-4aba-9181-c24a6f568955” use “2dd1b05d-8b45-4aba-9181-c24a6f568955/.default” as the scope. If you’ve registed custom scopes for an application those may be used in place of .default but you cannot combine .default with more specific scopes. Also using specific scopes are also restricted in use for certain OAuth flows due to the way delegated permissions work in Azure. But that’s for another time.

Using the client_credentials grant type (works somewhat like the Salesforce username/password OAuth flow) this becomes a POST like the one below. Please note that the Azure tenant_id is both in the URL and a parameter in the POST body:

POST /f84c375f-3f35-4d9d-93b3-c4216511f87a/oauth2/v2.0/token HTTP/1.1
 Host: login.microsoftonline.com
 Content-Type: application/x-www-form-urlencoded
 Content-Length: XYZ
 Connection: close
client_id=32c0ba71-04a4-4b3a-a317-1b1febd5fc22
&client_secret=shhhhh...
&grant_type=client_credentials
&tenant=f84c375f-3f35-4d9d-93b3-c4216511f87a
&scope=2dd1b95d-8b45-4aba-9181-c24f6f5e8955/.default

The response is a JSON document with an access_token (a JWT which may then be used as a Bearer token):

{
   "token_type":"Bearer",
   "expires_in":3599,
   "ext_expires_in":3599,
   "access_token":"eyJ0eXAiOiJKV1QiLCJhbGciO..."
}

When using Azure with Salesforce I would recommend using version 2 of the OAuth endpoints as Salesforce Auth. Providers and Named Credentials do not have a way to send custom parameters without resorting to writing a custom Auth. Provider implementation. This means there is no standard way to send the “resource” parameter to the version 1 OAuth endpoint.

What scopes should I specify in Salesforce?

When you create your Auth. Provider or Named Credentials specify the scopes you need from Azure. My personal preference is to specify the scopes on the Auth. Provider when using it for Single-Sign-On and specifying the scopes on the Named Credentials when using it for API access. One thing to note again is that an access token is for one API only and that an access token for a custom application will not work for the Microsoft Graph as well.

Lesson learned: You should not specify the UserInfo endpoint on the Auth. Provider in Salesforce unless it’s used for Single-Sign-On AND you are not specifying an App Registration in the Scopes-field.

If you specify an “App Registration scope” in the Scopes-field and specify the UserInfo endpoint Salesforce will attempt to read from the UserInfo endpoint following successful authentication using the obtained access token which will fail because the access token is only valid for the intended API and not for the Microsoft Graph.

Feel free to add other standard OpenID Connect scopes for Auth. Providers for Single-Sign-On. For most uses you would want to specify the offline_access scope as it ensures your Auth. Provider or Named Credential receives a refresh token.

Calling API’s protected by Azure API Management (APIM)

So far so good. Now you can obtain access tokens and use them with Azure Function Apps or read from the Microsoft Graph. But what if you need to access API’s hosed in Azure API Management (APIM)? Well read on…

In Azure API Management API’s are governed by subscriptions and you need to specify a subscription ID when calling into the API. The subscriptions map into users which are different from the ones in Azure AD. I’m not really sure why this is the case but I’m sure there are reasons.

The subscription ID you should use would be supplied by the people managing the API and is a GUID like string. When you call the API be sure to supply it using the “Ocp-Apim-Subscription-Key”-header. Failing to supply the subscription ID will result in an error like the one below:

{"statusCode": 401,   
"message": "Access denied due to missing subscription key. Make sure to include subscription key when making requests to an API."}

Putting all the above together to POST to an API behind Azure API Management using Apex would be something like the below using a Named Credentials called “My_NamedCredential”:

HttpRequest req = new HttpRequest();
req.setEndpoint('callout:My_NamedCredential/echo
req.setMethod('POST');
req.setHeader('Ocp-Apim-Subscription-Key', '7f9ed...1d6e8');
req.setBody('Hello, Salesforce, World!');
Http http = new Http();
HTTPResponse res = http.send(req);
System.debug(res.getBody());

As always… YMMV!

10 thoughts on “Using an Auth. Provider and Named Credentials in Salesforce with Azure OAuth”

  1. Hi! Great blog post, helper me a lot! However, I am trying to access an Azure API with a consumer key and secret set up in Auth. Provider and Named Credential (named principle) setup but once the Authentication flow starts from the Named credential it re-directs me to Azure to login and I don’t have access with my user. Isn’t that what the Auth. Provider should handle? Is there a setting in Azure that needs to be fixed, or is it a one-time thing which will result in zero connection between the user I am doing that Auth with and the API?

    Like

    1. It redirects you because you need to authenticate so that Salesforce now holds an access_token so the auth can be transparent when using the API from Apex or similar.

      Like

  2. Hi Lekkim, how did you manage the oauth with azur from named cred with client credentials (grant type) , did you created a custom auth provider ?

    but nice article, thanks for sharing!!

    Like

      1. Lekkim, I would also love to see this custom auth provider. I find it amazing how I can easily call to get a token with client_credentials in Postman, but not in this Auth. Providers form in Salesforce.

        Like

      2. Just published a post about a custom Auth. Provider for this. Source is on my Github page (linked from the post).

        Like

  3. awosme i would really be interested to see that, i have a custom auth provider wriiten where i all i am struggling is to get acces token from Azure, tried wiring post in initiate / onhandlecallback method , but didnt work, I will share the code with you 🙂 but once again thanks for being super kind to take a minute and respond.

    Like

  4. yes please it would be great to get some support from you on this.
    i have sent you a note in the email adding detaills triyng both openid & custom adapter

    Also i just tried doing open id connect with SF and azure AD to access the graph api resource . It follows 3 legged approach as you explained where in SF I see the status authenticated once i save my named credenetial. BUT when i try to call the the https://graph.microsoft.com/v1.0/users, it returns me 500, internal server error. it makes me confused from last 2 days if i am missing something imp.

    One thing is clear SF auth privider + named cred. follows “authz code flow” type oauth declaratively, and i tried to moke the same from postman, it works but somehow SF beacuse of the abstraction i am unable to figure whether it really works or expecting somehting mor.

    Like

  5. Hi Lekkim,
    This is very interesting. I am trying to integrate Salesforce with Azure API manager. We have Azure AD configured as Auth provider in Salesforce and the single sign on works fine (scope set in auth provider is openid). I have configured the named credentials ({ url : https://{Apim instance}.management.azure-api.net ,Identity Type : Named Principal , Authentication Protocol : OAuth 2.0 , Authentication Provider : {Auth provider configured in Salesforce}, scope : openid}) . I have been assuming the named credential would do all the trick of sending the necessary token to get the response from APIM, but a simple callout like callout:Apim/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/xxxxx/providers/Microsoft.ApiManagement/service/{Apim instance}/apis?api-version=2019-12-01 returns a response with no values. It doesn’t return a 401 due to absence of APIM subscription key.
    I am not sure where am I going wrong or is there an additional token to be sent from Salesforce to Apim to get the call working. I have considered using ‘Ocp-Apim-Subscription-Key’ but wanted to know how do we see to it that a seamless integration works between Salesforce and APIM. I am also considering building a custom auth provider if need be. Your inputs are highly valued.

    Like

Leave a comment