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.