1

I want to do Apex REST callouts to a JSON web service. I've got a "Client-Id" and "Client-Secret", and they need to be sent in the HTTP header for authentication throughout, along with some additional headers for version and other information. For some of the callouts I need to do a prior authentication against some "https://example.com/foo/authenticate", where I send

{"email": "[email protected]", "password": "secret"}

in a JSON POST body (in addition to Client-Id and Client-Secret in the header!) and receive an accessToken in the JSON response body. (Email and password unrelated to the current user.)

This works well doing

HttpRequest r = new HttpRequest();
r.setEndpoint(endpoint);
r.setHeader('Client-Id', clientId);
r.setHeader('Client-Secret', clientSecret);
r.setHeader('Some-Other-Header', otherHeader);

plus for some of the requests (after sending an authentication request)

r.setHeader('Authorization', 'Bearer ' + accessToken);

How to avoid holding clientSecret and password anywhere in clear text in Salesforce's Setup? Use "Auth. Provider" and Named Credential (for an example here):

The "Auth. Provider" would look about this:

Provider Type: Open ID Connect
Name: Some name
URL Suffix: Some name
Consumer Key: The clientId from above
Consumer Secret: The clientSecret from above
Authorize Endpoint URL: https://example.com/foo/authenticate
Token Endpoint URL: https://example.com/foo/
Send access token in header: checked
Send client credentials in header: checked

Everything else left empty / unchecked.

Not sure why "URL Suffix" is required under these circumstances. This adds to the redirect_uri, but what importance does it have in my case? The given web service does not allow to enter a Callback URL (apart from one that redirects users after exiting).

The Named Credential would look like:

Label: Some label
Name: Some name
URL: https://example.com/foo/
Identity Type: Named Principal
Authentication Protocol: OAuth 2.0
Authentication Provider: Name of the above Auth. Provider
Generate Authorization Header: checked

From what I can tell, I can't use "Start Authentication Flow on Save" - already because my setup requires a POST request with the username and password in its body. Therefore the "Authentication Status" remains "Pending" and I'm not surprised to receive

"System.UnexpectedException: You don't have permission to view this data. Ask your administrator to set up authentication for the external data source."

when attempting to use this Named Credential. (I'm aware "external data source" is a misnomer here.)

How would you set up this web service securely?

Update

Apparently there is no 100% satisfying solution for the given web service. However, the helpful feedback I got from User6670 and identigral both pointed at the possibility to use Named Credentials of type "Password Authentication" and Merge Fields.

I have made use of this advice and the Client-Secret is hidden this way now. It's annoying the password can be found in a Custom Metadata Type record, but without the Client-Secret it is of no use. I'm looking forward to use the new type of Named Credentials mentioned by identigral.

6
  • when i setup Oath 2.0 flow using named creds in my env, i was asked by the third party service to provide the callback url, which they used to send token back.
    – User6670
    Sep 27, 2022 at 12:12
  • I understand, but this doesn't happen in the above case :-/ Sep 27, 2022 at 12:34
  • Only client secret and password are secrets in information security sense. You can stick client secret in username field and password in password field, then use them as merge fields. Client id and username are not secrets, can reside anywhere and be used by Apex code however you implement their injection.
    – identigral
    Sep 27, 2022 at 16:47
  • @identigral, thank you for your suggestion, but the username field value appears in clear text in a Named Credential of type "Password Authentication". Which leaves me the choice to use the password field for Client-Secret or password. Or maybe I misunderstand?! Sep 27, 2022 at 18:18
  • @FelixvanHove No, you're right, forgot that the username is plaintext. In Winter 23, SF rewrote Named Credentials. Perhaps there's a way for you to accomplish this in the new framework, take a look: help.salesforce.com/s/…
    – identigral
    Sep 27, 2022 at 18:41

1 Answer 1

0

One way to use Named Credential's in your case can be->

Create Named Creds with following config setting and set "Allow Merge Fields in HTTP Header" checkbox as Checked and enter client id and secret in username and pass- enter image description here

Now in you apex use those username and password as merge fields which wont be visible to anyone

req.setEndpoint('callout:Some_label/foo/whateveryouwat');
req.setHeader('Client-Id', '{!$Credential.UserName}');
req.setHeader('Client-Secret', '{!$Credential.Password}');

Similary you can create another named creds for entering your username,password and check "Allow Merge Fields in HTTP Body" checkbox as checked in that one and then in your apex use Merge fields in body to get username-password.

3
  • Thank you! But how does this get me any closer to be able to send the authentication request to https://example.com/foo/authenticate? In which case I'd need the merge fields from two separate named credentials: client-id/secret for the header, username and password for the body. Is your idea to build one HTTP request based on two named credentials, maybe by swapping request bodies? Sep 27, 2022 at 14:05
  • 1
    @FelixvanHove if you need all 4(user-pass,clientid-secret) in one request then this wont work, i thought you needed them in different request.
    – User6670
    Sep 27, 2022 at 14:19
  • Thank you anyway, appreciated! I've added a bracket to my question to make this point clearer. Sep 27, 2022 at 14:27

You must log in to answer this question.

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