We use different AWS services in Salesforce for different use cases like storage, cloud computing, database services, queue service, etc. To integrate Salesforce and AWS, we have to use the AWS Signature Signing process. Salesforce provides a standard process to authenticate using Named Credential but sometimes we need to integrate custom API hosted on the AWS cloud. To use such a custom API, we need to use a custom AWS Signature 4 signing process. This post will explain how to generate an authentication token using AWS Signature 4 Signing in Salesforce Apex.
Let us see what is AWS Signature 4 and its benefit.
Signature Version 4 (SigV4) is the process to add authentication information to AWS API requests sent by HTTP. Authentication with AWS Signature Version provides the following benefits
- Verification of the identity of the requester
- In-transit data protection
- Protect against reuse of the signed portions of the request
Let us see how we can generate an authentication token using AWS Signature Version 4 in Apex. As per AWS deocumentation below steps are required to generate the token
- Create a canonical request
- Create a string to sign
- Calculate the signature
- Add the signature to the HTTP request
1. Create a canonical request
The signing process first creates a string that includes information from our request in a standardized (canonical) format. This ensures that when AWS receives the request, it can calculate the same signature that we calculated. It is a kind of checksum process to verify what we receive is what we sent.
private string createCanonicalRequest()
{
//Task1. Step 2 - Add the canonical URI parameter
String canonicalUri = '/'+resource;
//Task1. Step 3 - Add the canonical query string,
String canonicalQueryString = '';
//Task1. Step 4 - Add the canonical headers
string canonicalHeaders=canonicalHeaders();
//Task1. Step 5 - Add the signed headers
String signedHeaders =signedHeaders();
//Task1. Step 6 - Use a hash (digest) function like SHA256 to create a hashed value from the payload in the body of the HTTP or HTTPS request.
Blob payload = Blob.valueOf('');
String payloadHash = EncodingUtil.convertToHex(Crypto.generateDigest('SHA-256', payload));
//Task1. Step 7: Combine elements to create create canonical request
String canonicalRequest = method + '\n'
+ canonicalUri + '\n'
+ canonicalQueryString + '\n'
+ canonicalHeaders + '\n'
+ signedHeaders + '\n'
+ payloadHash;
return canonicalRequest;
}
2. Create a string to sign
The string to sign includes meta-information about our API request and about the canonical request that we have created in the first step. Date format is very important in this step.
private string createStringToSign(string canonicalRequest)
{
//Task2 - Step 1 algorithm designation
String algorithm = algorithm();
//Task2 - Step 2 Append the request date value
String credentialScope = credentialScope();
//Task2 - Step 3 Append the credential scope value,
String stringToSign = algorithm + '\n' + amzdate + '\n' + credentialScope + '\n' +
EncodingUtil.convertToHex(Crypto.generateDigest('sha256', Blob.valueOf(canonicalRequest)));
return stringToSign;
}
3. Calculate the signature
This step will calculate the signature for the request string based on AWS secret, region, and service.
private string calculateSignature(string stringToSign)
{
//Task3 - Step 1 generate signing key
Blob signingKey = createSigningKey(secret);
//Task3 - Step 2 generate signature
String signature = createSignature(stringToSign, signingKey);
return signature;
}
4. Add the signature to the HTTP request
After calculating the signature, we have to add it to the request. We can add the signature to a request in one of two ways:
- An HTTP header named
Authorization
- The query string
//Task 4: Add the signature to the HTTP request
String authorizationHeader = algorithm() + ' '
+ 'Credential=' + key + '/'
+ credentialScope() + ', '
+ 'SignedHeaders=' + signedHeaders() + ', '
+ 'Signature=' + calculatedSignature;
req.setHeader('Authorization',authorizationHeader);
It should be in the same format without any extra space and character.
Complete Code:
Note: Even space and any other character like / can create a different signatures. So please check extra space or another character if the signature is not generating properly.
Test using Custom API
Based on the above code we will get HTTPRequest with header and Authentication token. We can add other required parameters and send this request to AWS for processing.
Let us take an example, any mongo or .net API is hosted in AWS. This API should support AWS signature version 4 signing process. The following is API detail
Endpoint Url : https://patient.sysinfo.com/api/patient
Let us integrate the above custom API in Salesforce. The above API url should be added in Named Credential or added in the Remote site setting. Create custom metadata type AWSInfo to store AWS Authentication and other information. Add below fields in custom metadata AWSInfo.
Metadata Field Name | Metadata API Name | Type |
Host Name | HostName__c | Text |
Resource | Resource__c | Text |
Key | Key__c | Text |
Secrate | Secret__c | Text |
Region | Region__c | Text |
Service | Service__c | Text |
Store required AWS detail in this custom metadata type.
Apex Code:
This HTTPRequest is based on the above-mentioned URL. Please add the required parameter or other header parameter as per your requirement.
References:
Other Similar Posts:
Download S3 File using AWS Signature Version 4.0
View Comments (4)
Hi Dhanik,
Thanks for informative article.
AWSInfo__c must be AWSInfo__mdt in class 'AWSService'. You have created metdata.
Yes Anamika, It is AWSInfo__mdt. Thank You for informing me.
Regards
Dhanik
Hi Dhanik,
Can you please add a screenshot of the custom metadata values you are using. What data should hostname__c and resource have?
Hello Vaishnavi,
Host Name is an external API base URL like patient.sysinfo.com and the resource will be api/patient so the complete external URL will be patient.sysinfo.com/api/patient
Thank You,
Dhanik