DEV Community

Cover image for Part 1: Authentication Hell: Getting AWS Lambda Talking to X
Ashley Armstrong
Ashley Armstrong

Posted on

Part 1: Authentication Hell: Getting AWS Lambda Talking to X

How a simple idea turned into a multi-day battle with APIs, OAuth, and enough authentication errors to fill a spellbook.


The Idea

Every project starts with a simple question.

For me, it was:

Could I build an autonomous Wizarding World content engine that generates its own content, creates its own artwork, and publishes directly to X without human involvement?

The concept seemed straightforward.

The vision was:

AI generates quote
        ↓
AI generates artwork
        ↓
AWS stores content
        ↓
X publishes post
Enter fullscreen mode Exit fullscreen mode

In theory, this looked like a weekend project.

In reality, it became an education in authentication systems, API permissions, OAuth flows, and the many ways technology can tell you "no".


Building the Foundation

I decided to build everything on AWS.

The initial stack looked like this:

AWS Lambda
Amazon DynamoDB
Amazon Bedrock
Amazon S3
EventBridge
X API
Enter fullscreen mode Exit fullscreen mode

The first objective was intentionally small:

Post a single tweet from AWS Lambda.

Not an AI-generated tweet.

Not an image.

Just a tweet.

How hard could that be?


The First Lambda

The first Lambda function was incredibly simple.

Create Lambda
Install twitter-api-v2
Add environment variables
Call tweet endpoint
Enter fullscreen mode Exit fullscreen mode

Five minutes later everything was deployed.

I clicked Test.

It failed.

Welcome to OAuth.


The Authentication Rabbit Hole

If you've worked with X's API before, you'll know there isn't just one way to authenticate.

There are several.

At first glance they all appear to do similar things:

Bearer Token
OAuth 2.0
OAuth 2.0 User Context
OAuth 1.0a
Enter fullscreen mode Exit fullscreen mode

The challenge is understanding which one works with which endpoint.

I made what seemed like the obvious choice.

I used the Bearer Token.

The result?

403 Forbidden
Enter fullscreen mode Exit fullscreen mode

Not very helpful.

After digging through the logs, I eventually found the real message:

Unsupported Authentication

Authenticating with OAuth 2.0 Application-Only is forbidden for this endpoint.
Enter fullscreen mode Exit fullscreen mode

This was my first major lesson.

Just because you're authenticated doesn't mean you're authorised.


Understanding the Difference

The problem wasn't my code.

The problem was the type of identity I was presenting to X.

A Bearer Token represents:

The application
Enter fullscreen mode Exit fullscreen mode

A User Context token represents:

The user
Enter fullscreen mode Exit fullscreen mode

Posting a tweet requires a user.

Not just an application.

In other words:

Application:
"Hello, I am WizardThoughts."

X:
"Great."

Application:
"I would like to tweet."

X:
"No."
Enter fullscreen mode Exit fullscreen mode

Chasing the Right Token

I spent hours moving between:

AWS Lambda
CloudWatch Logs
X Developer Portal
Enter fullscreen mode Exit fullscreen mode

Testing.

Deploying.

Testing again.

Every change seemed to produce a different error.

Sometimes:

401 Unauthorized
Enter fullscreen mode Exit fullscreen mode

Other times:

403 Forbidden
Enter fullscreen mode Exit fullscreen mode

And occasionally:

Unsupported Authentication
Enter fullscreen mode Exit fullscreen mode

At one point I genuinely believed I had broken the entire account configuration.

The reality was much simpler:

I was using the wrong type of token.


The First Real Breakthrough

Rather than trying to post tweets, I decided to verify authentication first.

Instead of:

client.v2.tweet(...)
Enter fullscreen mode Exit fullscreen mode

I switched to:

client.v2.me()
Enter fullscreen mode Exit fullscreen mode

This endpoint simply returns information about the currently authenticated user.

If that worked, I would know the authentication was correct.

If it failed, the token was wrong.

Suddenly I received:

{
  "username": "WizardThoughts"
}
Enter fullscreen mode Exit fullscreen mode

That was the first real victory.

For the first time, AWS Lambda had successfully authenticated with X.

No guessing.

No assumptions.

Proof.


The OAuth 2 Dead End

With authentication seemingly solved, I moved on to images.

That's when another problem appeared.

The basic tweet APIs worked.

Media uploads did not.

The logs revealed a new pattern.

Every media upload attempt failed with:

403 Forbidden
Enter fullscreen mode Exit fullscreen mode

After more debugging, I discovered another important distinction.

Text posting could work using:

OAuth 2 User Context
Enter fullscreen mode Exit fullscreen mode

Media uploads were much happier with:

OAuth 1.0a User Context
Enter fullscreen mode Exit fullscreen mode

Which meant I needed four credentials:

CONSUMER_KEY
CONSUMER_SECRET
ACCESS_TOKEN
ACCESS_TOKEN_SECRET
Enter fullscreen mode Exit fullscreen mode

Suddenly the full picture started making sense.


The Moment Everything Clicked

The final authentication test looked like this:

const client = new TwitterApi({
  appKey: process.env.CONSUMER_KEY,
  appSecret: process.env.CONSUMER_SECRET,
  accessToken: process.env.ACCESS_TOKEN,
  accessSecret: process.env.ACCESS_TOKEN_SECRET
});

const me = await client.v2.me();
Enter fullscreen mode Exit fullscreen mode

Response:

{
  "username": "WizardThoughts"
}
Enter fullscreen mode Exit fullscreen mode

Success.

Not a partial success.

Not a maybe.

A genuine, repeatable success.

The authentication layer was finally solved.


The First Automated Tweet

Once the OAuth issues were resolved, posting a tweet became almost trivial.

The same infrastructure that had spent days refusing to cooperate suddenly worked exactly as intended.

The first tweet appeared.

Not created manually.

Not posted from a browser.

Automatically.

Directly from AWS Lambda.

That single post represented far more than a tweet.

It proved the entire foundation was viable.


Lessons Learned

Three lessons stood out above everything else.

1. Authentication Is a Product Feature

Most developers treat authentication as setup.

It isn't.

It's part of the application.

Understanding identities, scopes, permissions, and token types is essential.


2. Trust Error Messages

Most of my mistakes came from ignoring what the logs were telling me.

Eventually every answer was in CloudWatch.

The challenge was learning how to read it.


3. Prove Identity First

Before attempting any complex API action:

Authenticate
↓
Verify user
↓
Then perform action
Enter fullscreen mode Exit fullscreen mode

Testing with v2.me() saved hours of debugging.


What's Next?

At this point the bot could successfully:

✅ Authenticate with X

✅ Run in AWS Lambda

✅ Post automated tweets

But it still had one major limitation.

Every tweet had to be written manually.

In Part 2, we'll teach the bot how to think for itself using Amazon Bedrock, prompt engineering, and growth-focused content generation.

Because a bot that can post is useful.

A bot that can create its own content is where the magic begins. ⚡🪄


Next: Part 2 — Prompt Engineering for Growth: Creating Viral Wizarding Content

Top comments (0)