Fetch posts without Ghost Admin API from Cloudflare Worker

Hello,

I’m trying to fetch the posts from a local Ghost install using a Cloudflare Worker.

Here is what I tried and where I’m stuck.

Ghost Admin API library doesn’t seem to work since it requires Node.js modules like fs for example. I was able to add in webpack config this and seems to be ok:

module.exports = {
    target: "webworker",
    entry: "./index.js",
    node: {
        fs: "empty"
    }
}

Anyway … I tried without it to make sure that it doesn’t block anything else.

Now, I tried without the library. First, I tried to generate the Authorization token like this:

// Admin API key goes here
const key = MY_ADMIN_API_KEY;

// Split the key into ID and SECRET
const [id, secret] = key.split(':');

// Create the token (including decoding secret)
const token = jwt.sign({}, Buffer.from(secret, 'hex'), {
  keyid: id,
  algorithm: 'HS256',
  expiresIn: '5m',
  audience: `/v3/admin/`
});

return token;

The token seems to be generated ok.

If I try to use it like this:

const url = 'http://localhost:2368/ghost/api/v3/admin/posts/';
const headers = { Authorization: `Ghost ${token}` };
const payload = { posts: [{ title: 'Hello World' }] };
await axios.get(url, payload, { headers })
    .then(response => console.log(response))
    .catch(error => console.error(error));

I simply get error without anything else.

So I moved to Postman and pasted the generated token into Authorization → Type: API Key

Key: Ghost
Value: Generated token at previous step
Add to Header

The response that I get is this:

{
    "errors": [
        {
            "message": "Authorization failed",
            "context": "Unable to determine the authenticated user or integration. Check that cookies are being passed through if using session authentication.",
            "type": "NoPermissionError",
            "details": null,
            "property": null,
            "help": null,
            "code": null,
            "id": "ff6286a0-6ef6-11ec-8255-f9406afb7644"
        }
    ]
}

Any ideas what might be the issue here?

Apparently the token that I’m generating is not ok.

// Admin API key goes here
const key = GHOST_ADMIN_API_KEY;

// Split the key into ID and SECRET
const [id, secret] = key.split(':');

// Create the token (including decoding secret)
const token = jwt.sign({}, Buffer.from(secret, 'hex'), {
  keyid: id,
  algorithm: 'HS256',
  expiresIn: '5m',
  audience: `/v4/admin/`
});

return token;

If I simply do:

curl -H "Authorization: Ghost token_generated_previously" https://hauntedthemes.ghost.io/ghost/api/v4/admin/posts/

I get:

{
   "errors":[
      {
         "message":"Invalid token",
         "context":null,
         "type":"BadRequestError",
         "details":null,
         "property":null,
         "help":null,
         "code":"INVALID_JWT",
         "id":"ad3d9560-6f94-11ec-b67c-9bff0097dc8f"
      }
   ]
}

Not sure what I’m missing here.

Seems to be the correct way to generate a token:

https://ghost.org/docs/admin-api/#token-authentication

https://github.com/TryGhost/SDK/blob/main/packages/admin-api/lib/token.js

Interesting. I’ve used Ghost Admin API from everywhere - Node.js(both admin client and JWT generation), Python, curl, Postman. Have not had a problem , unless I am mistyping something.
Make sure:

  • Your Admin Api key is correct
  • You’re not sending request after token expires
  • audience matches your request URL
  • You’re not hitting the wrong Ghost server - most obvious but had to say it :slight_smile: I see localhost and Ghost Pro site names there

It would be helpful to know your Ghost version, jwt library you’re using (jsonwebtoken?) and version

Hi @kose_mark ,

Thanks for reaching out. So here is where I am right now.

  1. Your Admin Api key is correct → Double check it, it is
  2. You’re not sending request after token expires → I don’t think it’s expired since it has a 5m validity
  3. Audience matches your request URL → It’s /v4/admin/ in both.
  4. … > Double checked everything to make sure that is ok.

So here is how I continued:

I’m still generating the token like the above method that seems ok.

Then I wrote:

var data = await fetch("https://hauntedthemes.ghost.io/ghost/api/v4/admin/posts/'", {
  headers: {
    "Authorization": `Ghost ${token}`,
    "Content-Type": "application/json"
  },
  method: "GET"
}).then(res => res.json());

return new Response(JSON.stringify(data));

And this returns now:

{
  "errors": [
    {
      "message": "Validation error, cannot read post.",
      "context": "Validation (matches) failed for id undefined.id",
      "type": "ValidationError",
      "details": null,
      "property": null,
      "help": null,
      "code": null,
      "id": "f887c720-70a7-11ec-b67c-9bff0097dc8f"
    }
  ]
}

@kose_mark

I think I found my issue and why my requests don’t work ok.

Apparently Ghost Admin API is using axios library that it’s not compatible with Cloudflare Workers.

As a conclusion, for now at least, we can’t use Ghost Admin API inside a Cloudflare Worker. Instead, I’m using fetch to build the calls and seems to work.

I just have to figure out what are the proper values that needs to be added. For example, the above example https://hauntedthemes.ghost.io/ghost/api/v4/admin/posts/ throws a Validation error but works if I simply put https://hauntedthemes.ghost.io/ghost/api/v4/admin/posts/?limit=30.

To figure this out, I went to Ghost Dashboard and simply made the request there and looked at the Headers.

Yeah, I’m not familiar with Cloudflare workers except that it is a serverless product.

Looks like you figured out what’s needed, but feel free to share your experience and questions in the thread