Integrations with Adobe Experience Manager (AEM) as a Cloud Service must be able to securely authenticate to AEM service. AEM’s Developer Console grants access to Service Credentials, which are used to facilitate external applications, systems and services to programmatically interact with AEM Author or Publish services over HTTP.
Service Credentials may appear similar Local Development Access Tokens but are different in a few key ways:
Both Service Credentials and the access tokens they generate, and Local Development Access Tokens, should be kept secret. As all three can be used to obtain, access to their respective AEM as a Cloud Service environment.
Service Credentials generation is broken into two steps:
Service Credentials, unlike Local Development Access Tokens, require a Technical Account to be created by an Adobe Org IMS Administrator before they can be downloaded. Discrete Technical Accounts should be created for each client that requires programmatic access to AEM.
Technical Accounts are created once, however the Private Keys uses to manage Service Credentials associated with the Technical Account can be managed over time. For example, new Private Key/Service Credentials must be generated prior to the current Private Key’s expiration, to allow for uninterrupted access by a user of the Service Credentials.
Once the AEM as Cloud Service environment’s Service Credentials have been initialized, other AEM developers in your Adobe IMS Org can download them.
Downloading the Service Credentials follows the similar steps as the initialization.
The Service Credentials provide the details needed to generate a JWT, which is exchanged for an access token used to authenticate with AEM as a Cloud Service. The Service Credentials must be stored in a secure location accessible by the external applications, systems, or services that use it to access AEM. How and where the Service Credentials are managed are unique per customer.
For simplicity, this tutorial passes the Service Credentials in via the command line. However, work with your IT Security team to understand how to store and access these credentials in accordance with your organization’s security guidelines.
service_token.json
in the root of the project
The Service Credentials, a fully formed JSON object, are not the same as the JWT nor the access token. Instead the Service Credentials (which contain a private key), are used to generate a JWT, which is exchanged with Adobe IMS APIs for an access token.
To access AEM as a Cloud Service using the Service Credentials, the external application must be updated in three ways:
In this tutorial, Adobe’s @adobe/jwt-auth
npm module is used to both, (1) generate the JWT from the Service Credentials, and (2) exchange it for an access token, in a single function call. If your application is not JavaScript based, please review the sample code in other languages for how to create a JWT from the Service Credentials, and exchange it for an access token with Adobe IMS.
Review the getCommandLineParams()
so see how the Service Credentials JSON file is read using the same code used to read in the Local Development Access Token JSON.
function getCommandLineParams() {
...
// Read in the credentials from the provided JSON file
// Since both the Local Development Access Token and Service Credentials files are JSON, this same approach can be re-used
if (parameters.file) {
parameters.developerConsoleCredentials = JSON.parse(fs.readFileSync(parameters.file));
}
...
return parameters;
}
Once the Service Credentials are read, they are used to generate a JWT which is then exchanged with Adobe IMS APIs for an access token. This access token can then be used to access AEM as a Cloud Service.
This example application is Node.js-based, so it’s best to use @adobe/jwt-auth npm module to facilitate the (1) JWT generation and (20 exchange with Adobe IMS. If your application is developed using another language, please review the appropriate code samples on how to construct the HTTP request to Adobe IMS using other programming languages.
Update the getAccessToken(..)
to inspect the JSON file contents and determine if it represents a Local Development Access Token or Service Credentials. This can be easily achieved by checking for the existence of the .accessToken
property, which only exists for Local Development Access Token JSON.
If Service Credentials are provided, the application generates a JWT and exchanges it with Adobe IMS for an access token. Use the @adobe/jwt-auth’s auth(...)
function which generates a JWT and exchanges it for an access token in a single function call. The parameters to auth(..)
method are a JSON object comprised of specific information available from the Service Credentials JSON, as described below in code.
async function getAccessToken(developerConsoleCredentials) {
if (developerConsoleCredentials.accessToken) {
// This is a Local Development access token
return developerConsoleCredentials.accessToken;
} else {
// This is the Service Credentials JSON object that must be exchanged with Adobe IMS for an access token
let serviceCredentials = developerConsoleCredentials.integration;
// Use the @adobe/jwt-auth library to pass the service credentials generated a JWT and exchange that with Adobe IMS for an access token.
// If other programming languages are used, please see these code samples: https://www.adobe.io/authentication/auth-methods.html#!AdobeDocs/adobeio-auth/master/JWT/samples/samples.md
let { access_token } = await auth({
clientId: serviceCredentials.technicalAccount.clientId, // Client Id
technicalAccountId: serviceCredentials.id, // Technical Account Id
orgId: serviceCredentials.org, // Adobe IMS Org Id
clientSecret: serviceCredentials.technicalAccount.clientSecret, // Client Secret
privateKey: serviceCredentials.privateKey, // Private Key to sign the JWT
metaScopes: serviceCredentials.metascopes.split(','), // Meta Scopes defining level of access the access token should provide
ims: `https://${serviceCredentials.imsEndpoint}`, // IMS endpoint used to obtain the access token from
});
return access_token;
}
}
Now, depending on which JSON file - either the Local Development Access Token JSON or the Service Credentials JSON - is passed in via tha `file` command line parameter, the application will derive an access token.
Remember, that while the Service Credentials expire every 365 days, the JWT and corresponding access token expire frequently, and need to be refreshed before they expire. This can be done by using a `refresh_token` [provided by Adobe IMS](https://www.adobe.io/authentication/auth-methods.html#!AdobeDocs/adobeio-auth/master/OAuth/OAuth.md#access-tokens).
With these changes in place, the Service Credentials JSON was downloaded from the AEM Developer Console and for simplicity, saved as service_token.json
in the same folder as this index.js
. Now, let’s execute the application replacing the command-line parameter file
with service_token.json
, and updating the propertyValue
to a new value so the effects are apparent in AEM.
$ node index.js \
aem=https://author-p1234-e5678.adobeaemcloud.com \
folder=/wknd-shared/en/adventures/napa-wine-tasting \
propertyName=metadata/dc:rights \
propertyValue="WKND Restricted Use" \
file=service_token.json
The output to the terminal looks like:
200 - OK @ https://author-p1234-e5678.adobeaemcloud.com/api/assets/wknd-shared/en/adventures/napa-wine-tasting.json
403 - Forbidden @ https://author-p1234-e5678.adobeaemcloud.com/api/assets/wknd-shared/en/adventures/napa-wine-tasting/AdobeStock_277654931.jpg.json
403 - Forbidden @ https://author-p1234-e5678.adobeaemcloud.com/api/assets/wknd-shared/en/adventures/napa-wine-tasting/AdobeStock_239751461.jpg.json
403 - Forbidden @ https://author-p1234-e5678.adobeaemcloud.com/api/assets/wknd-shared/en/adventures/napa-wine-tasting/AdobeStock_280313729.jpg.json
403 - Forbidden @ https://author-p1234-e5678.adobeaemcloud.com/api/assets/wknd-shared/en/adventures/napa-wine-tasting/AdobeStock_286664352.jpg.json
The 403 - Forbidden lines, indicate errors in the HTTP API calls to AEM as a Cloud Service. These 403 Forbidden errors occur when attempting to update the assets’ metadata.
The reason for this is the Service Credentials-derived access token authenticates the request to AEM using an auto-created technical account AEM user, that by default, only has read access. To provide the application write access to AEM, the technical account AEM user associated with the access token must be granted permission in AEM.
The Service Credentials-derived access token uses a technical account AEM User that has membership in the Contributors AEM user group.
Once the technical account AEM user exists in AEM (after first HTTP request with the access token), this AEM user’s permissions can be managed the same as other AEM users.
integration.email
value, which should look similar to: 12345678-abcd-9000-efgh-0987654321c@techacct.adobe.com
.With the technical account permitted in AEM to have write permissions on assets, rerun the application:
$ node index.js \
aem=https://author-p1234-e5678.adobeaemcloud.com \
folder=/wknd-shared/en/adventures/napa-wine-tasting \
propertyName=metadata/dc:rights \
propertyValue="WKND Restricted Use" \
file=service_token.json
The output to the terminal looks like:
200 - OK @ https://author-p1234-e5678.adobeaemcloud.com/api/assets/wknd-shared/en/adventures/napa-wine-tasting.json
200 - OK @ https://author-p1234-e5678.adobeaemcloud.com/api/assets/wknd-shared/en/adventures/napa-wine-tasting/AdobeStock_277654931.jpg.json
200 - OK @ https://author-p1234-e5678.adobeaemcloud.com/api/assets/wknd-shared/en/adventures/napa-wine-tasting/AdobeStock_286664352.jpg.json
200 - OK @ https://author-p1234-e5678.adobeaemcloud.com/api/assets/wknd-shared/en/adventures/napa-wine-tasting/AdobeStock_239751461.jpg.json
200 - OK @ https://author-p1234-e5678.adobeaemcloud.com/api/assets/wknd-shared/en/adventures/napa-wine-tasting/AdobeStock_280313729.jpg.json
aem
command-line parameter)folder
command-line parameter, for example WKND > English > Adventures > Napa Wine Tastingmetadata/dc:rights
JCR property, which now reflects the value provided in the propertyValue
parameter, for example WKND Restricted UseNow that we’ve programmatically accessed AEM as a Cloud Service using a local development access token, and a production-ready service-to-service access token!