AEM Cloud Service - Generate JWT Token, AEM Access Token and Upload Assets using aem-upload tool

Goal

AEM Upload (https://github.com/adobe/aem-upload) is a NodeJS library you could use to Upload Assets in Bulk to AEM Cloud Services. For authentication...

          1) Get a Local Development Token from Developer Console (expires in 24 hours), may be good for just testing your code...

          2) Get a JWT Token using Service Credentials (valid for 365 days or until revoked), exchange it for an AEM Access Token (when expires in 24 hours) for long running external services connecting to AEM. 

This post is on using "2" to upload assets to AEM Cloud Services instance from an external service. For product api to get the access token check documentation. For performing the same job using a java standalone, check this post

Demo | Github


Service Credentials

                   Create a Technical Account in Developer Console and get the Service Credentials JSON....



Technical Account Write Access

                   Tech account created in AEM with the otb integration has Read Access, add a group with Write Access to Create Assets... using Security Console (Tools > Security > User Management - https://author-p9999-e999999.adobeaemcloud.com/ui#/aem/security/users.html)


Running the Utility

1) Install Node and the packages - "@adobe/aem-upload" , "jws" running following commands...

                npm install @adobe/aem-upload

               npm install jws

2) Save the following code to file aem-file-upload-jwt.js and copy the service credentials json from developer console, set it as the value of CONFIG_COPIED_FROM_DEV_CONSOLE

3) Run the utility node .\aem-file-upload-jwt.js

const HTTPS = require('https');
const QS = require('querystring');
const JWS = require("jws");
const { FileSystemUploadOptions, FileSystemUpload } = require('@adobe/aem-upload');

const CONFIG_COPIED_FROM_DEV_CONSOLE = {
"ok": true,
"integration": {
"imsEndpoint": "ims-na1.adobelogin.com",
"metascopes": "ent_aem_cloud_api",
"technicalAccount": {
"clientId": "cm-pxxxx-exxxx-integration-1",
"clientSecret": "p8e-xxx-xxxxxxxxxxxxxAmV"
},
"email": "b925c5cexxxxxxxxxxxxb6c9e@techacct.adobe.com",
"id": "A37Cxxxxxxxxxxxx@techacct.adobe.com",
"org": "2FBC7BxxxxxxxxxxxxFBB@AdobeOrg",
"privateKey": "-----BEGIN RSA PRIVATE KEY-----\r\nMIIxxxxxxxxxxxx1MymhgRoYbMfBulvMvnE\r\n-----END RSA PRIVATE KEY-----\r\n",
"publicKey": "-----BEGIN CERTIFICATE-----\r\nMIIDHDxxxxxxxxxxxx6GlMjx9z+CsOvurM=\r\n-----END CERTIFICATE-----\r\n",
"certificateExpirationDate": "2024-03-26T16:20:18.000Z"
},
"statusCode": 200
}

const PRIVATE_KEY = CONFIG_COPIED_FROM_DEV_CONSOLE["integration"]["privateKey"];
const CLIENT_ID = CONFIG_COPIED_FROM_DEV_CONSOLE["integration"]["technicalAccount"]["clientId"];
const CLIENT_SECRET = CONFIG_COPIED_FROM_DEV_CONSOLE["integration"]["technicalAccount"]["clientSecret"];
const ORG_ID = CONFIG_COPIED_FROM_DEV_CONSOLE["integration"]["org"];
const TECH_ACCOUNT = CONFIG_COPIED_FROM_DEV_CONSOLE["integration"]["id"];
const IMS_HOST = CONFIG_COPIED_FROM_DEV_CONSOLE["integration"]["imsEndpoint"];
const META_SCOPE = "https://" + IMS_HOST + "/s/" + CONFIG_COPIED_FROM_DEV_CONSOLE["integration"]["metascopes"];

const AEM = "https://author-p10961-e880305.adobeaemcloud.com"
const UPLOAD_FOLDER = "/content/dam/aem-upload";
const LOCAL_FILE_PATH = "C:/Users/nalabotu/Pictures/bricks.jpeg";

triggerUploadSteps();

async function triggerUploadSteps(){
const jwtToken = getJWTToken();

console.log("jwtToken-----" + jwtToken.substring(0,20) + ".....TRIMMED...");

let aemToken = await getAEMToken(jwtToken);

console.log("aemToken-----" + aemToken.substring(0,20) + ".....TRIMMED...");

doUpload(aemToken);
}

function getAEMToken(jwtToken){
const postData = {
'client_id': CLIENT_ID,
'client_secret': CLIENT_SECRET,
"jwt_token" : jwtToken
};

let payload = QS.stringify(postData);

const options = {
hostname: IMS_HOST,
path: "/ims/exchange/jwt",
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
};

let reqPromise = new Promise(function(resolve, reject) {
let req = HTTPS.request(options, (res) => {
const chunks = [];
res.on('data', data => chunks.push(data));

res.on('end', () => {
let resBody = Buffer.concat(chunks).toString();

if(res.statusCode == 200){
resolve(JSON.parse(resBody)["access_token"]);
}else{
reject( { statusCode : res.statusCode, resBody : resBody} );
}
});
});

req.on('error', (e) => {
reject(e);
});

req.write(payload);
req.end();
});

return reqPromise;
}

function getJWTToken(){
const jwt_payload = {
iss: ORG_ID,
sub: TECH_ACCOUNT,
exp: Math.floor((Date.now() / 1000) + 3600 * 8),
aud: "https://ims-na1.adobelogin.com/c/" + CLIENT_ID,
[META_SCOPE]: true
};

const jwtToken = JWS.sign({
header: { alg: 'RS256' },
payload: jwt_payload,
secret: PRIVATE_KEY,
});

return jwtToken;
}

function doUpload(accessToken){
const options = new FileSystemUploadOptions()
.withUrl(AEM + UPLOAD_FOLDER)
.withHeaders({
Authorization: 'Bearer ' + accessToken
});

const fileUpload = new FileSystemUpload();

fileUpload.upload(options, [
LOCAL_FILE_PATH
]).then(result => {
let noErrors = true;

result.getErrors().forEach(error => {
noErrors = false;
console.log("\t" + error);
});

result.getFileUploadResults().forEach(fileResult => {
fileResult.getErrors().forEach(fileErr => {
noErrors = false;
console.log("\t" + fileErr);
});
});

if(noErrors){
console.log("\nUPLOAD SUCCESSFUL\n");
}else{
console.error("\nUPLOAD FAILED\n");
}
}).catch(err =>
console.error("\nUPLOAD FAILED\n", err)
);
}

4) Console output...



No comments:

Post a Comment