A serverless AWS infrastructure for integrating with the Threads API, enabling OAuth authentication and automated posting to Threads.
This project provides a complete Infrastructure as Code (IaC) solution for:
- OAuth Authentication Flow: Handle Threads OAuth callbacks and securely store user access tokens
- Automated Posting: Create and publish posts to Threads on behalf of authenticated users
- Token Management: Exchange short-lived tokens for long-lived tokens and store them securely in AWS Secrets Manager
- API Gateway Endpoints: Expose REST APIs for OAuth callbacks and post creation
User → OAuth Flow → Callback Lambda → Secrets Manager
↓
User → API Request → API Lambda → Threads API
↑
Secrets Manager
-
Callback Lambda (source/callback/main.py)
- Handles OAuth redirect from Threads
- Exchanges authorization code for access tokens
- Converts short-lived tokens to long-lived tokens
- Stores tokens in AWS Secrets Manager
-
API Lambda (source/api/main.py)
- Accepts post creation requests
- Retrieves user tokens from Secrets Manager
- Creates Threads post containers
- Publishes posts to Threads
-
API Gateway
GET /callback- OAuth callback endpoint (public)POST /post- Post creation endpoint (API key protected)
-
AWS Secrets Manager
- Stores Threads app credentials (APP_ID, APP_SECRET)
- Stores user access tokens (short-lived and long-lived)
thread-connector-iac/
├── source/
│ ├── api/
│ │ └── main.py # Lambda function for posting to Threads
│ └── callback/
│ └── main.py # Lambda function for OAuth callback
├── terraform/
│ ├── modules/
│ │ ├── api_gateway/ # Reusable API Gateway module
│ │ ├── lambda/ # Reusable Lambda module
│ │ ├── security_groups/ # VPC security groups (if needed)
│ │ └── vpc/ # VPC configuration (if needed)
│ ├── apigateway.tf # API Gateway resources
│ ├── lambda.tf # Lambda function definitions
│ ├── locals.tf # Local variables and computed values
│ ├── outputs.tf # Terraform outputs
│ ├── providers.tf # AWS provider configuration
│ ├── variables.tf # Input variables
│ └── versions.tf # Terraform version constraints
├── terraform_iam/
│ ├── modules/
│ │ └── iam/ # IAM module (if separated)
│ ├── iam.tf # IAM roles and policies
│ └── ... # IAM-specific configuration
└── README.md
This project follows a modular Terraform structure with:
- Separation of Concerns: Lambda functions, API Gateway, and IAM are defined in separate files
- Reusable Modules: Common infrastructure patterns (Lambda, API Gateway) are abstracted into modules
- Environment-based Configuration: Variables allow deployment to different environments (dev, staging, prod)
- Security Best Practices:
- API key authentication for posting endpoint
- IAM roles with least privilege access
- Secrets Manager for credential storage
- Input sanitization in Lambda functions
- Terraform >= 1.0
- AWS CLI configured with appropriate credentials
- Python 3.11 (for Lambda runtime)
- boto3 (AWS SDK for Python)
- requests library (for HTTP requests)
- AWS Account with permissions to create:
- Lambda functions
- API Gateway REST APIs
- IAM roles and policies
- Secrets Manager secrets
- CloudWatch log groups
- Threads App ID and App Secret
- Registered OAuth redirect URI in Threads app settings
Create an AWS CLI profile or ensure default credentials are configured:
aws configure --profile threads-conn-deployCreate a secret in AWS Secrets Manager with your Threads app credentials:
aws secretsmanager create-secret \
--name threads_app_credentials \
--secret-string '{"APP_ID":"your-app-id","APP_SECRET":"your-app-secret"}' \
--region us-east-1If using separate IAM deployment:
cd terraform_iam
terraform init
terraform plan
terraform applycd terraform
terraform init
terraform plan
terraform applyAfter deployment, Terraform will output the OAuth redirect URI. Register this URL in your Threads app settings:
terraform output oauth_redirect_uriExample output:
https://abc123xyz.execute-api.us-east-1.amazonaws.com/dev/callback
Get the API key for the posting endpoint:
terraform output threads_api_key_value- Direct users to the Threads authorization URL:
https://threads.net/oauth/authorize?
client_id=YOUR_APP_ID&
redirect_uri=YOUR_CALLBACK_URL&
scope=threads_basic,threads_content_publish&
response_type=code
-
After authorization, Threads redirects to your callback endpoint with an authorization code
-
The callback Lambda automatically:
- Exchanges the code for an access token
- Converts it to a long-lived token
- Stores it in Secrets Manager under
threads/tokens/{user_id}
Send a POST request to the API endpoint:
curl -X POST https://YOUR_API_URL/dev/post \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"user_id": "default",
"post_text": "Hello from the Threads API!"
}'Request Body:
user_id(string, required): User identifier (must match the stored token)post_text(string, required): Text content to post
Response:
{
"id": "1234567890",
"user_id": "default"
}import requests
import json
api_url = "https://YOUR_API_URL/dev/post"
api_key = "YOUR_API_KEY"
headers = {
"X-API-Key": api_key,
"Content-Type": "application/json"
}
data = {
"user_id": "default",
"post_text": "Hello from Python!"
}
response = requests.post(api_url, json=data, headers=headers)
print(json.dumps(response.json(), indent=2))Key variables in terraform/variables.tf:
| Variable | Description | Default |
|---|---|---|
aws_region |
AWS region for deployment | us-east-1 |
profile_name |
AWS CLI profile name | threads-conn-deploy |
project_name |
Logical name for the project | threads-connector |
environment |
Deployment environment | dev |
credentials_secret_name |
Secret name for app credentials | threads_app_credentials |
secret_name_prefix |
Prefix for user token secrets | threads/tokens |
Callback Lambda:
THREADS_TOKEN_URL- Threads OAuth token endpointREDIRECT_URI- OAuth redirect URICREDENTIALS_SECRET_NAME- Name of app credentials secretSECRET_NAME_PREFIX- Prefix for user token secrets
API Lambda:
SECRET_NAME_PREFIX- Prefix for user token secrets
After deployment, Terraform provides:
callback_api_invoke_url- Full URL for OAuth callback endpointoauth_redirect_uri- OAuth redirect URI to register with Threadsthreads_api_invoke_url- Full URL for posting endpointthreads_api_key_value- API key for authentication (sensitive)threads_api_key_id- API key ID
- API Key Protection: The posting endpoint requires an API key. Keep this secret
- Input Sanitization: User IDs are sanitized to prevent injection attacks
- Least Privilege IAM: Lambda functions have minimal required permissions
- HTTPS Only: All endpoints use HTTPS encryption
- Secret Rotation: Consider implementing secret rotation for long-lived tokens
- Rate Limiting: Consider adding AWS WAF for rate limiting and DDoS protection
aws logs tail /aws/lambda/threads-connector-dev-api --follow
aws logs tail /aws/lambda/threads-connector-dev-callback --follow# Test callback endpoint
curl "https://YOUR_API_URL/dev/callback?code=test_code"
# Test posting endpoint
curl -X POST https://YOUR_API_URL/dev/post \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"user_id":"default","post_text":"Test post"}'- Token not found: Ensure the user has completed OAuth flow and token is stored
- API key invalid: Retrieve the API key using
terraform output threads_api_key_value - Permission denied: Check IAM roles have correct Secrets Manager permissions
- Threads API errors: Verify app credentials and token validity
To destroy all infrastructure:
cd terraform
terraform destroy
# If IAM was deployed separately
cd ../terraform_iam
terraform destroySee LICENSE file for details.
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
For issues related to:
- Threads API: See Threads API Documentation
- AWS Services: See AWS Documentation
- This Project: Open an issue in the repository