Copy link to clipboard
Copied
Hi all.
I just signed up to test/use PDF Services. The system set up my new dev account with some JWT credentials with big warnings that everything it just set up for me was instatly deprecated and should be migrated. Well, that's just Adobe (from my years of experience with them) I thought, and carried on.
I tested a .net htmlToPdf doc generation (using the sample code in the .net SDK), which seemed to work - but then thought I should listen to the big red warnings and follow Adobe's advice about upgrading/migrating.
The problem is, it now seems that there are no relevant samples, no documentation and no .net SDK support for using the API with the new pattern. Are all the .net SDK samples pointing to an old api that is deprecated? Did Adobe just point me towards a migration into a world where there is no support or documentation or any way of knowing how to use it yet?
Hopefully I'm missing the obvious here.... but baffled.
Adobe being Adobe again or just a daft user bafflement?
You are absolutely right. The messaging here could have been better. I have a blog post coming out ASAP on this, but it may be a day or two before published. Let me TLDR it:
1) Yes, there is a new auth schema, but your current auth (JWT) will work till January 1, 2025.
2) The SDKs for our stuff, and most APIs here at Adobe, have not yet been updated to use the new auth. So the "old" JWT auth is what you would use.
3) The REST APIs, however, can use either.
So for right now, if you are a SDK
...Copy link to clipboard
Copied
You are absolutely right. The messaging here could have been better. I have a blog post coming out ASAP on this, but it may be a day or two before published. Let me TLDR it:
1) Yes, there is a new auth schema, but your current auth (JWT) will work till January 1, 2025.
2) The SDKs for our stuff, and most APIs here at Adobe, have not yet been updated to use the new auth. So the "old" JWT auth is what you would use.
3) The REST APIs, however, can use either.
So for right now, if you are a SDK user, sit tight. Ignore the scary warnings in the console, and note that our SDKs/docs will be updated soon.
If you are a REST user, and would like to use the new stuff, I'm happy to share code demonstrating how it works. The good news is that it's a heck of a lot simpler.
Copy link to clipboard
Copied
Thank you for taking the time to reply Raymond - it really is appreciated by us all. Where's the blog post coming out? I'd love to read it when done.
If you have any code to share about the new stuff that would be great. I'm an asp.net core developer looking to get simple htmlToPdf things under way for starters - which I understand could be quite a tricky multi-step process using the REST tools - so keen to get any insight and get stuck in. So, .net code preferred but I'll take anything! 😉
It's difficult to know how far we'll go with the services because we're finding quite a lot of resistance from Adobe when it comes to pricing - but I assume they will get back to us eventually with an idea of how pricing tiers work (and importantly what the tiers are).
Really hoping this is a service being made available to regular folks and more than just the Microsoft's of the world. Could all be very exciting!
Copy link to clipboard
Copied
Blog post is Monday. The demo I use is NOT our APIs, but should still be helpful. To be honest, I was a bit scared when I first started learning about these changes. OAuth has never been easy for me in the past. Having actually *done* the work now? It's super easy and *simpler* than before. Obviously everyone's developer skills are different, but if I had an existing Acrobat Services API implementation, I'd schedule no more than an hour or so to update the code (and a bit of time for testing).
Unfortunately, I don't do anything in .Net at all. I could share the Node code with you, and in theory you perhaps rebuild? To be clear, all the Node code is doing is a network request. I'd imagine that would be easy enough to "translate" to .Net.
Copy link to clipboard
Copied
Yes, Node code would be great. It's not my first language but I'll certainly get something from it I'm sure. Thanks again for taking the time Raymond.
Copy link to clipboard
Copied
The code for this blog post isn't in a GitHub repo (when I get a sample using our APIs I'll have some), but here's the gist.
Here is a function I'd use to generate an access token. It does this by making a JWT, and hitting an endpoint to convert it to an access token. Here's the function:
async function getAccessToken(config) {
let scope = `https://ims-na1.adobelogin.com/s/${config.metaScopes}`;
let cred = {
"iss":config.orgId,
"sub":config.technicalAccountId,
"aud":`https://ims-na1.adobelogin.com/c/${config.clientId}`
};
cred[scope] = true;
const token = sign(cred, config.privateKey, {expiresIn: 300, algorithm: 'RS256'});
let body = `client_id=${config.clientId}&client_secret=${config.clientSecret}&jwt_token=${token}`;
let getATReq = await fetch('https://ims-na1.adobelogin.com/ims/exchange/jwt', {
method:'POST',
headers: {
'Content-Type':' application/x-www-form-urlencoded'
},
body: body
});
return await getATReq.json();
}
And here's how I call it. The capitalized variables are my credentials, each named (hopefully) in a way that makes sense:
let config = {
clientId: CLIENT_ID,
clientSecret: CLIENT_SECRET,
technicalAccountId: TECHNICAL_ACCOUNT_ID,
orgId: ORG_ID,
privateKey: KEY,
metaScopes:'ent_ccas_sdk'
}
let { access_token } = await getAccessToken(config);
Once you have the access token, you can then go to town.
In the new system, you will ONLY use a client id and client secret. So for example:
let access_token = await getAccessToken(CLIENT_ID, CLIENT_SECRET);
And here's the updated function:
async function getAccessToken(clientId, clientSecret) {
const params = new URLSearchParams();
params.append('client_secret', clientSecret);
params.append('grant_type', 'client_credentials');
params.append('scope', 'openid,AdobeID,read_organizations');
let resp = await fetch(`https://ims-na1.adobelogin.com/ims/token/v2?client_id=${clientId}`,
{
method: 'POST',
body: params
}
);
let data = await resp.json();
return data.access_token;
}
Hope this helps!
Copy link to clipboard
Copied
This helps.
I will take it back down into my burrow and go to work. Thank you Raymond.
Copy link to clipboard
Copied
I can see the .net SDK is under way (looks like it's headed for a Q2 release) with the new pattern ...
https://github.com/adobe/PDFServices.NET.SDK.Samples/tree/release-q2
Copy link to clipboard
Copied
Got this working with Postman (manually, the Postman resources button in the Adobe console doesn't work for me).
My experiments are with HTML to PDF (supplying a URL to the api not a zipe file).
In case anyone else gets stuck with injecting js variables into the doc (not documented well at all from what I could find) - then it seems referencing window.json.yourVariableName in your html should suffice. It's the "window.json" bit that was a mystery.
Copy link to clipboard
Copied
The C# tests aren't going so well. The Auth is now working (thanks Raymond) - but it "seems" the API requires json to be supplied in what some might consider to be an unexpected format (maybe some won't?).
A simple example (minial api payload for an HTML to PDF conversion).
{
"inputUrl": "https://www.test.com/pdftest.html",
"json": { "var1": "jsTest" },
"includeHeaderFooter": false
}
This is how I would expect a JSON object to be formatted. For sure, it would be the output of JSON.stringify() on a js client and indeed a System.Text.JsonSerializer in C# (asp.net core).
However, send this to the API and it will reject the call with the following result.
{
"error": {
"code": "INVALID_REQUEST_FORMAT",
"message": "Invalid request format."
}
}
After more Postman experiemnts that I care to admit to (and another "detailed" look at the api docs), it seems that the api wants json formatted as follows:
{
"inputUrl": "https://www.test.com/pdftest.html",
"json": "{ \"var1\": \"jsTest\" }",
"includeHeaderFooter": false
}
Note the fully "quoted" child object and escaped quoted properties within it.
Now this is going to be a bit of a mission because it seems to be asking for a format different from that generated by tools that I'm used to - buuuut with an obvious gap in my knowledge here, i'm off for more research before I start rolling eyeballs at the api.
If anyone has wisdom to throw into the mix about why the api expects things this way (vs what things like JSON.stringify() and System.Text.JsonSerializer generate) - I'm all eyeballs and ears.
For now, it's just an update in case anyone else stumbles with the formatting.
Copy link to clipboard
Copied
Odd the api wants only child objects as strings (insetad of true json objects) but not the root level object ... do you wants strings or json Adobe? Can we choose one? 🙂
Copy link to clipboard
Copied
Ok ... solved. We just need to inconsistently "double serialise" the objects ... somewhat daftly in my view (I'm officially eyeball rolling at the api now). I'd love to understand why this pattern was chosen .... Adobe?
So, an example C# object that you might prepare for serialisation...
Imports System.text.Json;
var data = new PdfApiData() {
inputUrl = "https://www.test.com/pdftest.html",
includeHeaderFooter = false,
json = new PdfApiJsonVars() {
var1 = "jsonVariableTest1",
var2 = "jsonVariabletest2"
}
};
var payload = System.Text.Json.JsonSerializer.Serialize(data);
This would sensibly serialise to:
{
"inputUrl": "https://www.test.com/pdftest.html",
"json": { "var1": "jsonVariableTest1", "var2": "jsonVariableTest2" },
"includeHeaderFooter": false
}
But Adobe will reject that. One solution is to break up the payload object and double serialize only the child object.
Imports System.Text.Json;
var jsonVars = new PdfApiJsonVars() {
var1 = "jsonVariableTest1",
var2 = "jsonVariabletest2"
};
var data = new PdfApiData() {
inputUrl = "https://www.test.com/pdftest.html",
includeHeaderFooter = false,
json = JsonSerializer.Serialize(jsonVars)
};
var payload = JsonSerializer.Serialize(data);
So, double serialising the child object. Not so difficult but more than a little daft in my view. I haven't yet tested if Adobe want us to triple serialise child childs etc.
I'd still love an explanation of why the api was implemented like this. Seems odd - please educate me 🙂
Copy link to clipboard
Copied
I don't use HTML to PDF w/ JSON often as I'd normally do the dynamic stuff locally, that being said, the REST APIs do say the json argument is a string, so I would have expected, in Node for example, to pass JSON.stringify(ob). Are you saying you are double stringify-ing your JSON?
Copy link to clipboard
Copied
It's confusing here because a normal stringify (like js) isn't really a stringify - it's actually a serialise to a string. That's not the same as forcing an object to NOT be json, wrapping it in quotes and escaping inner quotes.
I suppose the api requirement is kind of double stringifying - although more double serialising of child objects. The second serialisation forces the child objects to be escaped as strings instead of valid json. This is really about who you ask (platform) as to it's oddness. I believe Python had a serialisation method that did in fact turn child objects into escaped strings (instead of valid json, and so requiring recursive deserialisation).
A stringify (like a js stringify) would leave child objects as valid json - as would a .net serialize - and most sensible serialisers. The oddness here is the requirement to not supply valid json - but instead a hybrid of a json object with double serialised child nodes. I've not seen a modern api like it.
My code works though. Doubling up on serialisation does the job so I'm taking the win. It's just odd. <shrug />
Copy link to clipboard
Copied
Ah gotcha. Like I said, I don't use that particular endpoint. Our DocGen endpoint which uses JSON too lets you pass it as is. I'll file an ER w/ engineering to see if we can make this consistent.
Copy link to clipboard
Copied
Hi Raymond. I've not been able to locate your blog. Are you at liberty to share a url?
Copy link to clipboard
Copied
Copy link to clipboard
Copied
Will this endpoint work with eSign too? It appears that the the Integration Key has been removed, but the OAuth enpoints used for sign give a "Invalid grant_type client_credentials" when trying to use the client_credentials flow.
Copy link to clipboard
Copied
I don't use the Sign API so I'm not sure how to help. I'd suggest posting on their forums.