Local Development Access Token

Developers building integrations that require programmatic access to AEM as a Cloud Service need a simple, quick way to obtain temporary access tokens for AEM to facilitate local development activities. To satisfy this need, AEM’s Developer Console allows developers to self-generate temporary access tokens that can be used to programmatically access AEM.

Transcript
Let’s take a look at AEM as a Cloud service local development access tokens, and how they can be obtained and used programmatically to access AEM from external applications. Local development access tokens are generated for a specific AEM as a Cloud service environment and provide access to both author and published services on that environment. Local to moment access tokens are temporary and are to be used to aid in the development of external applications that must connect to AEM. Instead of a developer having to deal with obtaining and managing bonafide service credentials, they can open the respective AEM environments developer console, and self-generate temporary access tokens that allow them to develop their integration. Later, we’ll look at how this is different from service credentials, which are used to facilitate the final service to service interactions between external apps and AEM. So, let’s go ahead and open our development environments developer console.
We’ll tap on the integrations tab, which provides us the options to generate a local development access token.
Tapping on this generates an access token for my specific user.
So, I’ll go ahead and save this JSON file.
So now I’ll run the sample application from the command line. I’ll copy the downloaded local development access token from my downloads folder to my local project folder. Though keep in mind you should not actually be committing these to Git since these are credentials.
Now let’s go ahead and run our node JS application from the command line. So, for this I’ll execute the command node and pass in our index dot JS file, and then I’ll pass in a set of parameters. The first is the AEM host name to connect to.
Next is the assets folder, whose assets we want to update with the metadata.
Next will be the property name, which is the metadata property name to update, and property value, which is the value to update the property name with. And lastly, we’ll use the file parameter to point to the downloaded JSON file that contains our local development access token.
Let’s execute this command.
And in our output, we can see our applications made a number of HTTP requests to AEM, and they all have returned with the 200 okay status indicating that they were successful.
So, let’s jump over to AEM author service and just verify that our metadata was indeed updated.
I’ll pick one of the image assets in the specified folder Click on the advanced tab, and there we go, our copyright, which is mapped to the metadata DC rates property has been updated to the value specified in the property value parameter.
So, let’s jump back to the application and look at two specific things. The first is how we actually get the access token from the JSON file that we pass into a parameter.
And this is rather simple. We simply look for the access token property in the JSON object contained by that file.
And secondly, once we have the access token, how exactly do we add it to requests to AEM? So for this, let’s check out the list assets by folder function. All this does is make an HTTP requests to AEM’s assets HTTP APIs to list the contents of an assets folder. The interesting piece is in the header’s object, and specifically the authorization header. To use an access token, you must set the authorization header to pass in the access token as a bearer token. So, to do this, you need to add the word bearer, capital B followed by a space, and then the access token. Once this header is set on that request, AEM will authenticate based on the access token. Similarly, if we look at the update metadata function, which makes HTTP put requests to update the metadata, we see that we have the same authorization header with the same bearer token added as well. So, you’ll need to make sure that any requests that need to authenticate to AEM contain this header. -

Generate a Local Development Access Token

Getting a Local Development Access Token

The Local Development Access Token provides access to AEM Author and Publish services as the user who generated the token, along with their permissions. Despite this being a development token, do not share this token, or store in source control.

  1. In Adobe Admin Console ensure you, the developer, are a member of:

    • Cloud Manager - Developer IMS Product Profile (grants access to AEM Developer Console)
    • Either the AEM Administrators or AEM Users IMS Product Profile for the AEM environment’s service the access token integrates with
    • Sandbox AEM as a Cloud Service environment only require membership in either the AEM Administrators or AEM Users Product Profile
  2. Log in to Adobe Cloud Manager

  3. Open the Program containing the AEM as a Cloud Service environment to integrate with

  4. Tap the ellipsis next to the environment in the Environments section, and select Developer Console

  5. Tap in the Integrations tab

  6. Tap the Local token tab

  7. Tap Get Local Development Token button

  8. Tap on the download button in the top-left corner to download the JSON file containing accessToken value, and save the JSON file to a safe location on your development machine.

    • This is your 24 hour, developer access token to the AEM as a Cloud Service environment.

AEM Developer Console - Integrations - Get Local Development Token

Used the Local Development Access Token use-local-development-access-token

Local Development Access Token - External Application

  1. Download the temporary Local Development Access Token from AEM Developer Console
    • The Local Development Access Token expires every 24 hours, so developers need to do download new access tokens daily
  2. An External Application is being developed that programmatically interacts with AEM as a Cloud Service
  3. The External Application reads in the Local Development Access Token
  4. The External Application constructs HTTP requests to AEM as a Cloud Service, adding the Local Development Access Token as a Bearer token to the HTTP requests’ Authorization header
  5. AEM as a Cloud Service receives the HTTP request, authenticates the request, and performs the work requested by the HTTP request, and returns an HTTP response back to the External Application

The Sample External Application

We’ll create a simple external JavaScript application to illustrate how to programmatically access AEM as a Cloud Service over HTTPS using the local developer access token. This illustrates how any application or system running outside of AEM, regardless of framework or language, can use the access token to programmatically authenticate to, and access, AEM as a Cloud Service. In the next section, we’ll update this application code to support the approach for generating a token for production use.

This sample application is run from the command line, and updates AEM asset metadata using AEM Assets HTTP APIs, using the following flow:

  1. Reads in parameters from the command line (getCommandLineParams())
  2. Obtains the access token used to authenticate to AEM as a Cloud Service (getAccessToken(...))
  3. Lists all assets in a AEM asset folder specified in a command-line parameters (listAssetsByFolder(...))
  4. Update listed assets’ metadata with values specified in command-line parameters (updateMetadata(...))

The key element in programmatically authenticating to AEM using the access token is adding an Authorization HTTP request header to all HTTP requests made to AEM, in the following format:

  • Authorization: Bearer ACCESS_TOKEN

Running the External Application

  1. Ensure that Node.js is installed on your local development machine, which is used to run the external application

  2. Download and unzip the sample external application

  3. From the command line, in this project’s folder, run npm install

  4. Copy the downloaded the Local Development Access Token to a file named local_development_token.json in the root of the project

    • But remember, never commit any credentials to Git!
  5. Open index.js and review the external application code and comments.

    code language-javascript
    const fetch = require('node-fetch');
    const fs = require('fs');
    const auth = require('@adobe/jwt-auth');
    
    // The root context of the Assets HTTP API
    const ASSETS_HTTP_API = '/api/assets';
    
    // Command line parameters
    let params = { };
    
    /**
    * Application entry point function
    */
    (async () => {
        console.log('Example usage: node index.js aem=https://author-p1234-e5678.adobeaemcloud.com propertyName=metadata/dc:rights "propertyValue=WKND Limited Use" folder=/wknd-shared/en/adventures/napa-wine-tasting file=credentials-file.json' );
    
        // Parse the command line parameters
        params = getCommandLineParams();
    
        // Set the access token to be used in the HTTP requests to be local development access token
        params.accessToken = await getAccessToken(params.developerConsoleCredentials);
    
        // Get a list of all the assets in the specified assets folder
        let assets = await listAssetsByFolder(params.folder);
    
        // For each asset, update it's metadata
        await assets.forEach(asset => updateMetadata(asset, {
            [params.propertyName]: params.propertyValue
        }));
    })();
    
    /**
    * Returns a list of Assets HTTP API asset URLs that reference the assets in the specified folder.
    *
    * https://experienceleague.adobe.com/docs/experience-manager-cloud-service/assets/admin/mac-api-assets.html?lang=en#retrieve-a-folder-listing
    *
    * @param {*} folder the Assets HTTP API folder path (less the /content/dam path prefix)
    */
    async function listAssetsByFolder(folder) {
        return fetch(`${params.aem}${ASSETS_HTTP_API}${folder}.json`, {
                method: 'get',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer ' + params.accessToken // Provide the AEM access token in the Authorization header
                },
            })
            .then(res => {
                console.log(`${res.status} - ${res.statusText} @ ${params.aem}${ASSETS_HTTP_API}${folder}.json`);
    
                // If success, return the JSON listing assets, otherwise return empty results
                return res.status === 200 ? res.json() : { entities: [] };
            })
            .then(json => {
                // Returns a list of all URIs for each non-content fragment asset in the folder
                return json.entities
                    .filter((entity) => entity['class'].indexOf('asset/asset') === -1 && !entity.properties.contentFragment)
                    .map(asset => asset.links.find(link => link.rel.find(r => r === 'self')).href);
            });
    }
    
    /**
    * Update the metadata of an asset in AEM
    *
    * https://experienceleague.adobe.com/docs/experience-manager-cloud-service/assets/admin/mac-api-assets.html?lang=en#update-asset-metadata
    *
    * @param {*} asset the Assets HTTP API asset URL to update
    * @param {*} metadata the metadata to update the asset with
    */
    async function updateMetadata(asset, metadata) {
        await fetch(`${asset}`, {
                method: 'put',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer ' + params.accessToken // Provide the AEM access token in the Authorization header
                },
                body: JSON.stringify({
                    class: 'asset',
                    properties: metadata
                })
            })
            .then(res => {
                console.log(`${res.status} - ${res.statusText} @ ${asset}`);
            });
    }
    
    /**
    * Parse and return the command line parameters. Expected params are:
    *
    * - aem = The AEM as a Cloud Service hostname to connect to.
    *              Example: https://author-p12345-e67890.adobeaemcloud.com
    * - folder = The asset folder to update assets in. Note that the Assets HTTP API do NOT use the JCR `/content/dam` path prefix.
    *              Example: '/wknd-shared/en/adventures/napa-wine-tasting'
    * - propertyName = The asset property name to update. Note this is relative to the [dam:Asset]/jcr:content node of the asset.
    *              Example: metadata/dc:rights
    * - propertyValue = The value to update the asset property (specified by propertyName) with.
    *              Example: "WKND Free Use"
    * - file = The path to the JSON file that contains the credentials downloaded from AEM Developer Console
    *              Example: local_development_token_cm_p1234-e5678.json
    */
    function getCommandLineParams() {
        let parameters = {};
    
        // Parse the command line params, splitting on the = delimiter
        for (let i = 2; i < process.argv.length; i++) {
            let key = process.argv[i].split('=')[0];
            let value = process.argv[i].split('=')[1];
    
            parameters[key] = value;
        };
    
        // Read in the credentials from the provided JSON file
        if (parameters.file) {
            parameters.developerConsoleCredentials = JSON.parse(fs.readFileSync(parameters.file));
        }
    
        console.log(parameters);
    
        return parameters;
    }
    
    async function getAccessToken(developerConsoleCredentials) {s
        if (developerConsoleCredentials.accessToken) {
            // This is a Local Development access token
            return developerConsoleCredentials.accessToken;
        }
    }
    

    Review the fetch(..) invocations in the listAssetsByFolder(...) and updateMetadata(...), and notice headers define the Authorization HTTP request header with a value of Bearer ACCESS_TOKEN. This is how the HTTP request originating from the external application authenticates to AEM as a Cloud Service.

    code language-javascript
    ...
    return fetch(`${params.aem}${ASSETS_HTTP_API}${folder}.json`, {
                method: 'get',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': 'Bearer ' + params.accessToken // Provide the AEM access token in the Authorization header
                },
    })...
    

    Any HTTP requests to AEM as a Cloud Service, must set the Bearer access token in the Authorization header. Remember, each AEM as a Cloud Service environment requires its own access token. Development’s access token does not work on Stage or Production, Stage’s does not work on Development or Production, and Production’s does not work on Development or Stage!

  6. Using the command line, from the root of the project execute the application, passing in the following parameters:

    code language-shell
    $ node index.js \
        aem=https://author-p1234-e5678.adobeaemcloud.com \
        folder=/wknd-shared/en/adventures/napa-wine-tasting \
        propertyName=metadata/dc:rights \
        propertyValue="WKND Limited Use" \
        file=local_development_token.json
    

    The following parameters are passed in:

    • aem: The scheme and host name of the AEM as a Cloud Service environment the application interacts with (ex. https://author-p1234-e5678.adobeaemcloud.com).
    • folder: The asset folder path whose assets are updated with the propertyValue; do NOT add the /content/dam prefix (ex. /wknd-shared/en/adventures/napa-wine-tasting)
    • propertyName: The asset property name to update, relative to [dam:Asset]/jcr:content (ex. metadata/dc:rights).
    • propertyValue: The value to set the propertyName to; values with spaces need to be encapsulated with " (ex. "WKND Limited Use")
    • file: The relative file path to the JSON file downloaded from AEM Developer Console.

    A successful execution of the application results output for each asset updated:

    code language-shell
    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_239751461.jpg.json
    200 - OK @ https://author-p1234-e5678.adobeaemcloud.com/api/assets/wknd-shared/en/adventures/napa-wine-tasting/AdobeStock_280313729.jpg.json
    200 - OK @ https://author-p1234-e5678.adobeaemcloud.com/api/assets/wknd-shared/en/adventures/napa-wine-tasting/AdobeStock_286664352.jpg.json
    

Verify metadata update in AEM

Verify that the metadata has been updated, by logging in to the AEM as a Cloud Service environment (ensure the same host passed into the aem command-line parameter is accessed).

  1. Log into the AEM as a Cloud Service environment that the external application interacted with (use the same host provided in the aem command-line parameter)
  2. Navigate to the Assets > Files
  3. Navigate it the asset folder specified by the folder command-line parameter, for example WKND > English > Adventures > Napa Wine Tasting
  4. Open the Properties for any (non-Content Fragment) asset in the folder
  5. Tap to the Advanced tab
  6. Review the value of the updated property, for example Copyright which is mapped to the updated metadata/dc:rights JCR property, which reflects the value provided in the propertyValue parameter, for example WKND Limited Use

WKND Limited Use Metadata Update

Next steps

Now that we’ve programmatically accessed AEM as a Cloud Service using the local development token. Next we need to update the application to handle using Service Credentials, so this application can be used in a production context.

recommendation-more-help
e25b6834-e87f-4ff3-ba56-4cd16cdfdec4