NodeJS has a dependency supported by adobe https://www.npmjs.com/package/@adobe/jwt-auth.
This is not usable for deno. Any alternative?
NodeJS has a dependency supported by adobe https://www.npmjs.com/package/@adobe/jwt-auth.
This is not usable for deno. Any alternative?
This is the working solution I came up with, after a lot of debugging. However, a proper dependency for that would be very welcome!
I'm pretty sure that a lot of people have a need for something like that.
import { create } from "https://deno.land/x/djwt@v2.7/mod.ts";
import "https://deno.land/x/dotenv/load.ts";
const clientId = Deno.env.get("client_id") || "";
const clientSecret = Deno.env.get("client_secret") || "";
const technicalAccountId = Deno.env.get("technical_account_id") || "";
const orgId = Deno.env.get("org_id") || "";
const metaScopes = JSON.parse(Deno.env.get("meta_scopes") || "");
const pemEncodedPrivateKey = Deno.env.get("private_key") || "";
const PRIVATE_KEY_HEADER = "-----BEGIN PRIVATE KEY-----";
const PRIVATE_KEY_FOOTER = "-----END PRIVATE KEY-----";
const ims = "https://ims-na1.adobelogin.com";
const str2ab = (str: string) => {
const buf = new ArrayBuffer(str.length);
const bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
};
const generateAdobeToken = async (): Promise<string | null> => {
console.log(
"*** Generating Authentication-Token for the Adobe-Target Api ***"
);
const rawBase64PrivateKey = pemEncodedPrivateKey
.substring(
PRIVATE_KEY_HEADER.length,
pemEncodedPrivateKey.length - PRIVATE_KEY_FOOTER.length
)
.trim();
const binaryDerString = atob(rawBase64PrivateKey);
const binaryDer = str2ab(binaryDerString);
const key = await crypto.subtle.importKey(
"pkcs8",
binaryDer,
{
name: "RSASSA-PKCS1-v1_5",
hash: "SHA-256",
},
true,
["sign"]
);
const jwtPayload = {
exp: Math.round(300 + Date.now() / 1000),
iss: orgId,
sub: technicalAccountId,
aud: `${ims}/c/${clientId}`,
};
(jwtPayload as any)[`${ims}/s/${metaScopes[0]}`] = true;
let token;
try {
token = await create({ alg: "RS256", typ: "JWT" }, jwtPayload, key);
} catch (e) {
console.error("Local token generation failed.");
throw e;
}
const form = new FormData();
form.append("client_id", clientId);
form.append("client_secret", clientSecret);
form.append("jwt_token", token);
const postOptions = {
method: "POST",
body: form,
};
console.log("\n*** Retrieving auth-token from adobe ***");
return fetch(`${ims}/ims/exchange/jwt/`, postOptions)
.catch((e) => {
console.error("Could not fetch token from adobe: Call failed!", e);
throw e;
})
.then((res) => {
return res.json().then((data) => {
return {
ok: res.ok,
json: data,
};
});
})
.then(({ ok, json }) => {
const { access_token, error, error_description } = json;
if (ok && access_token) {
return json.access_token;
}
if (error && error_description) {
const swapError = new Error(error_description);
(swapError as any).code = error;
throw swapError;
} else {
console.error(
`The response body is as follows: ${JSON.stringify(json)}`
);
}
return null;
});
};
export const requestAdobeApiToken = async () => {
try {
return await generateAdobeToken();
} catch (e) {
console.error("Requesting token failed!", e);
return null;
}
};
I took your code and simplified the key import and JWT signing using http://deno.land/x/jose.
import * as jose from "https://deno.land/x/jose/index.ts";
const key = await jose.importPKCS8(pemEncodedPrivateKey, "RS256");
const token = await new jose.SignJWT({
[`${ims}/s/${metaScopes[0]}`]: true,
})
.setProtectedHeader({ alg: "RS256" })
.setIssuer(orgId)
.setSubject(technicalAccountId)
.setAudience(`${ims}/c/${clientId}`)
.setIssuedAt()
.setExpirationTime("5m")
.sign(key);