14

Given: a REST API that requires authorization. The authorization service attached to the API supports oAuth, one of the available oAuth flows is Client Credentials (grant_type=client_credentials)

How can we use External and Named Credentials to implement access to the REST API that meets above criteria?

1 Answer 1

17

At the protocol level, Client Credentials grant is designed to not require a user identity. It involves an application exchanging its application credentials, such as client ID and client secret, for an access token. This flow is best suited for Service-to-Service integrations where a backend component (your Service) talks to another backend component (the REST API => the other Service).

Starting with the Winter '24 release, oAuth Client Credentials flow (grant_type=client_credentials) is supported out of the box by a combination of External and Named Credential. The authorization step of obtaining the access token is done by the External Credential. The callout to the REST API and injection of the access token is handled by Named Credential.

For our implementation example we'll use Marketing Cloud (MC) as the target. MC meets our criteria: it has REST APIs which require authorization via oAuth, Client Credentials is a supported flow. One of the REST APIs available in Marketing Cloud is Email Validation: given an email address, the API tells you if the email is valid. (As an aside, this API alone is not sufficient for real-world email validation, see more on this below).

  • REST API: https://<api_host:port>/address/v1/validateEmail
  • oAuth Authorization Service - Token: https://<token_host:port>/v2/token

Implementation

1. External Credential (EC)

a. Credential

  • Protocol: oAuth 2.0
  • Flow Type: Client Credentials with Client Secret Flow
  • Identity Provider URL: URL of oAuth Authorization Service - Token endpoint
  • Pass client credentials in request body: depends

enter image description here

The Pass client credentials.. checkbox is a nuance of oAuth client credentials grant implementation. Regardless of whether this box is checked, the body of the request will be encoded via application/x-www-form-urlencoded scheme. The body contains the grant type (set by EC/NC to client_credentials) and optionally the scope (the Scope field).

The oAuth spec allows the credentials to be stuffed into the request body or by encoding them into the Authorization header according to HTTP Basic authentication scheme. The spec recommends the Authorization header as an allegedly safer choice. When the spec was published in 2012, there may have been relevant considerations for this preference. Since then lots of e-ink has been spilled on various threats to oAuth protocol. There's no universal "safe(r)" option nor does this one choice significantly harm or improve your overall security posture.

This checkbox is important because some older oAuth authorization services will accept only one out of these two credential transmission formats: body or Authorization header. Modern authorization services accept both. Based on reading the docs or by trial and error you'll need to find out what mode(s) your authorization service supports and make an appropriate selection.

As it so happens, Marketing Cloud's oAuth authorization service is an older implementation. It only allows credentials to be sent in the body of the request, requiring the Pass credentials... option to be checked.

If you leave the option unchecked and your target authorization service doesn't support this mode, you'll get a CalloutException along the lines of:

> System.CalloutException: Unable to fetch the OAuth token. Error:
> invalid_request. Error description: Make sure that the client ID and
> client secret are valid and that the following parameters are not
> empty or null: client ID, client secret, and grant type..

b. Principal

The principal represents your application acting as oAuth client. Within the EC definition page, scroll down to the Principal panel and create a new Principal:

  • Parameter Name: name of your application (e.g. Salesforce - Production org)
  • Client Id: oAuth client id
  • Client Secret: oAuth client secret

If you have configured the Principal and later on decide to edit the EC (say, to change the Pass client credentials... option), you will need to reapply the credentials to the Principal. As a safety precaution, EC removes the credentials from the Principal on edit of EC. If you are not aware of this and you attempt to make the callout, you'll receive a CalloutException: The external credential isn't fully configured. Contact your Salesforce admin

2. Named Credential (NC)

  • URL: https://<api_host:port>
  • Enabled for Callouts: select
  • External Credential: select the EC from step 1
  • Callout options > Generate Authorization Header: select

enter image description here

3. Permissions

The user who will be making the callout via Named Cred needs to be granted permission to the underlying External Cred. (Analogy: Named Cred = pipe, External Cred = identity). The user in question is the user attached to the runtime context of your callout.

Permissions can be granted via Profile or Permission Set:

  • Navigate to External Credential Principal Access section, click Edit and select the EC/Principal combination that will be listed. Save.

Warning: as of this writing, External Credential Principal Access has a bug: if more than one set of EC/Principal is available, you'll only see one EC/Principal combo in External Credential Principal Access view/panel until you hit Edit. The other EC/Principal combos will then show up on the Edit page.

4. Callout (Apex)

String body = '{"email": "[email protected]","validators": [ "SyntaxValidator", "MXValidator", "ListDetectiveValidator"]}';
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:mc/address/v1/validateEmail'); // base URL (NC) + relative API path
req.setMethod('POST');
req.setBody(body);
Http http = new Http();
HttpResponse res = http.send(req);
System.debug(res.getBody());

Result:

00:66:66:224 USER_DEBUG [8]|DEBUG|{"email":"[email protected]","valid":true}

5. Callout (Flow)

As an alternative to calling the API in Apex, you can perform the same operation via a Flow.

a. HTTP Callout action

Create the HTTP Callout action and link it to the Named Credential created in step 2:

enter image description here

Provide the relative API path (/address/v1/validateEmail) and sample JSON request/response, thus completing the build of the action:

enter image description here

If you squint at this screen, you'll see that a ValidateEmail External Service was auto-generated and linked to the action. This is so because underneath the hood the HTTP Callout action is a Flow wrapper (Adapter design pattern) around External Service.

b. Wire request/response

For this example, we're using the Screen Flow to prompt the user for the email address, compose the API request, call the API and display the response

enter image description here

Result:

enter image description here

Debug log:

VALIDATEEMAIL.VALIDATE EMAIL (EXTERNAL SERVICES): Validate Email (API)

Inputs:
body = {!ValidateEmailPayload} (ValidateEmail_Validatex20Email_IN_body : { "validators_set" : true, "validators" : [ "ListDetectiveValidator", "MXValidator", "SyntaxValidator", "ListDetectiveValidator", "MXValidator", "SyntaxValidator" ], "email_set" : true, "email" : "[email protected]" })

Outputs:
2XX (ValidateEmail_Validatex20Email_OUT_2XX : 
{ "valid_set" : true, "valid" : false, "email_set" : true, "email" : "[email protected]" })
responseCode (200)

Bonus: you might have noticed that both request and response generated by the External Service have xxx_set keys (email_set,validators_set, valid_set) not present in the payload built in Apex. These keys are not part of MC API schema, they're junk artifacts of External Service. If the target API used strict JSON parsing that rejected unknown keys, this request would have blown up. Thankfully Marketing Cloud's JSON parser is as relaxed as its email validation approach.

Double Bonus if you've read this far: out of three validators available when calling MC's Email Validation API, the syntax validator is the only one that's mildly useful. As you can see, [email protected] is syntactically good according to the RFC but it's certainly not a valid real-world email address.

16
  • 3
    doesn't the running user also need R/View All on UserExternalCredential as well?
    – cropredy
    Commented Aug 28, 2023 at 22:35
  • 1
    @cropredy Life-saving comment Eric! Yes the permset needs UserExternalCredential in order for Platform User licenses to Authenticate the Principal. Otherwise the OAuth fails AuthorizationError?ErrorCode=Existing_link Commented Nov 2, 2023 at 23:08
  • 1
    @aidonsnous Inbound calls are an entirely different scenario - take a look at trailhead.salesforce.com/content/learn/projects/… and do a search here, lots of Q&As on this
    – identigral
    Commented Dec 12, 2023 at 5:24
  • 1
    @AndreasEpping Unfortunately that requires custom code via Apex, most of this Q&A does not apply. The inclusion of account id (MID/BU ID) in the oAuth call the way MC wants it is a proprietary option and not in oAuth standard. You can still use Named/External Cred to hold the secrets and reference them in Apex via a formula
    – identigral
    Commented Jan 16 at 17:31
  • 1
    @SwethaMaddali scope is a field in External Credential - you fill it in based on whatever you target application wants/needs and if your app is oAuth-compliant it should work from there.
    – identigral
    Commented Jan 29 at 17:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .