Why This Is Useful
Most of the time, integrating with an external system means using Named Credentials and letting Salesforce handle authentication for you. That should always be your first choice.
But sometimes you’re up against an API that does something non-standard. A weird token format, a custom header scheme, an auth endpoint that expects things in a shape Named Credentials can’t produce. When that happens you have to do the handshake yourself.
Here’s how, and how to do it without hardcoding credentials in Apex.
Code’s on GitHub.
The Shape Of It
Every custom authentication follows the same three steps:
1. Get your credentials from somewhere safe.
2. Call the auth endpoint and get a token back.
3. Use that token on your actual request.
Here’s the orchestrating method, which reads exactly like that list:
public with sharing class CustomAuthentication
{
public Id createAccountCallout()
{
SF_Custom_Integration__mdt connectionData = getConnectionMetadata();
SF_Auth authBody = getSFAccessToken(connectionData);
Id acctId = createAccount(authBody);
return acctId;
}
}Three lines, three steps, each one a small method that does one thing. That’s Single Responsibility in practice, and it’s why this class is readable despite doing something fiddly.
Step 1: Credentials In Custom Metadata
private SF_Custom_Integration__mdt getConnectionMetadata()
{
List<SF_Custom_Integration__mdt> connectionData =
[SELECT Auth_URL__c, Client_Id__c, Client_Secret__c, Content_Type__c,
grant_type__c, Password__c, Security_Token__c, Username__c
FROM SF_Custom_Integration__mdt LIMIT 1];
return connectionData[0];
}This is the important design decision. Nothing sensitive is in the Apex.
Custom Metadata is a good fit because it deploys between orgs, an admin can change a URL without a release, and each environment holds its own values. Same code in sandbox and production, different endpoints.
One caveat worth being honest about: Custom Metadata is readable by anyone with View Setup and Configuration. It’s not a secrets vault. For genuinely sensitive credentials, look at Named Credentials or Protected Custom Metadata in a managed package.
The one thing you must not do is put credentials in the Apex itself, where they end up in your Git history forever.
Step 2: Get The Token
private SF_Auth getSFAccessToken(SF_Custom_Integration__mdt connectionData)
{
HttpRequest authReq = new HttpRequest();
authReq.setMethod('POST');
authReq.setEndpoint(generateEndpointURL(connectionData));
authReq.setHeader('Content-Type', connectionData.Content_Type__c);
HttpResponse response = new Http().send(authReq);
return SF_Auth.parse(response.getBody());
}SF_Auth is a wrapper class matching the auth response, so you get authBody.access_token and authBody.token_type as proper typed properties.
Step 3: Use The Token
private Id createAccount(SF_Auth authBody)
{
HttpRequest dataReq = new HttpRequest();
dataReq.setMethod('POST');
dataReq.setEndpoint(ACCOUNT_CREATION_URL);
dataReq.setHeader('Authorization', authBody.token_type + ' ' + authBody.access_token);
dataReq.setHeader('Content-Type', 'application/json');
dataReq.setBody(JSON.serialize(wrapAccount(generateAccount())));
HttpResponse response = new Http().send(dataReq);
//...
}That Authorization header is the payoff. token_type is usually “Bearer”, so you end up with Bearer eyJhbGci..., which is what most modern APIs expect.
Two callouts here, incidentally, so remember the 100 callout limit and never do this in a loop.
Improvements Worth Making
The example above is deliberately stripped back to show the mechanism. Before you ship something like this:
Cache the token. Fetching a fresh one on every single call doubles your callouts for no reason. Tokens usually last an hour, so stash it in Platform Cache with a sensible TTL.
Use a POST body, not query parameters. The example builds credentials into the URL, which is fine for demonstrating the flow but means your client secret can land in server logs and proxy logs along the way. Send them in the request body instead.
Check the status code. A 401 back from the auth endpoint currently sails straight through and fails confusingly later.
Handle expiry. Tokens expire mid-process sometimes. Detect a 401 on the data call, get a fresh token, retry once.
But Really, Try Named Credentials First
I’ll say it once more because it matters. Everything above is roughly seventy lines of code you now own, test and maintain, plus a token caching strategy and a retry policy.
Named Credentials do all of it for free, and they support far more auth schemes than people realise. External Credentials in particular have made them much more flexible in recent releases.
Reach for custom auth when you’ve genuinely established that Named Credentials can’t handle your API. Not before. Your future self will appreciate having seventy fewer lines to look after.
If you want to understand which auth flow you should be using in the first place, I’ve covered that in the auth flows post.
Get Coding With The Force Merch!!
We now have a redbubble store setup so you can buy cool Coding With The Force merchandise! Please check it out! Every purchase goes to supporting the blog and YouTube channel.
Get Shirts Here!
Get Cups, Artwork, Coffee Cups, Bags, Masks and more here!
Check Out More Coding With The Force Stuff!
If you liked this post make sure to follow us on all our social media outlets to stay as up to date as possible with everything!
Youtube
Patreon
Github
Facebook
Twitter
Instagram
Salesforce Development Books I Recommend
Advanced Apex Programming
Salesforce Lightning Platform Enterprise Architecture
Mastering Salesforce DevOps
Good Non-SF Specific Development Books:
Clean Code
Clean Architecture