Features: - Add LinkedIn OAuth integration and auto-posting functionality - Add scheduler service for automated post publishing - Add metadata field to generated_posts for LinkedIn URLs - Add privacy policy page for LinkedIn API compliance - Add company management features and employee accounts - Add license key system for company registrations Fixes: - Fix timezone issues (use UTC consistently across app) - Fix datetime serialization errors in database operations - Fix scheduling timezone conversion (local time to UTC) - Fix import errors (get_database -> db) Infrastructure: - Update Docker setup to use port 8001 (avoid conflicts) - Add SSL support with nginx-proxy and Let's Encrypt - Add LinkedIn setup documentation - Add migration scripts for schema updates Services: - Add linkedin_service.py for LinkedIn API integration - Add scheduler_service.py for background job processing - Add storage_service.py for Supabase Storage - Add email_service.py improvements - Add encryption utilities for token storage Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
6.0 KiB
LinkedIn Auto-Posting Setup Guide
This guide walks you through setting up the LinkedIn auto-posting feature.
Overview
Employees can link their LinkedIn accounts to automatically post scheduled content directly to LinkedIn, eliminating manual copy-paste work.
Prerequisites
- A LinkedIn Developer account
- Access to your server's environment variables
- HTTPS domain (required for OAuth)
Step 1: Create LinkedIn App
- Go to LinkedIn Developers
- Click "Create app"
- Fill in app details:
- App name: Your app name (e.g., "LinkedInWorkflow Auto-Poster")
- LinkedIn Page: Select your company page
- App logo: Upload a logo (optional)
- Check the agreement and create the app
Step 2: Configure OAuth Settings
-
In your app dashboard, go to the Auth tab
-
Add OAuth 2.0 redirect URLs:
https://yourdomain.com/settings/linkedin/callback -
Request the following OAuth 2.0 scopes:
openid- For user identityprofile- For user profile dataemail- For user emailw_member_social- CRITICAL - For posting on user's behalf
Note:
w_member_socialrequires app review by LinkedIn. Submit for review if not already approved. -
Note your Client ID and Client Secret (found in Auth tab)
Step 3: Generate Encryption Key
Run the helper script to generate a secure encryption key:
python scripts/generate_encryption_key.py
Copy the generated key for the next step.
Step 4: Configure Environment Variables
Add these variables to your .env file:
# LinkedIn API (Custom OAuth for auto-posting)
LINKEDIN_CLIENT_ID=your_client_id_here
LINKEDIN_CLIENT_SECRET=your_client_secret_here
LINKEDIN_REDIRECT_URI=https://yourdomain.com/settings/linkedin/callback
# Token Encryption
ENCRYPTION_KEY=your_generated_fernet_key_here
⚠️ Security: Never commit these values to git! Keep them secure.
Step 5: Run Database Migration
Apply the database migration to create the linkedin_accounts table:
# Using psql
psql $DATABASE_URL -f config/migrate_add_linkedin_accounts.sql
# Or via Supabase dashboard SQL editor
# Copy and paste the contents of config/migrate_add_linkedin_accounts.sql
Step 6: Install Dependencies
Install the new dependencies:
pip install -r requirements.txt
Step 7: Restart Application
Restart your application to load the new environment variables:
# If using systemd
sudo systemctl restart linkedinworkflow
# If using docker
docker-compose restart
# If running directly
# Stop the current process and restart
Step 8: Test the Integration
- Log in as an employee user
- Go to Settings (
/settings) - Click "Mit LinkedIn verbinden"
- Complete the LinkedIn OAuth flow
- Verify the account shows as connected
- Schedule a test post
- Wait for the scheduled time (or manually trigger the scheduler)
- Verify the post appears on LinkedIn
Troubleshooting
OAuth Errors
"Invalid redirect_uri"
- Make sure the redirect URI in your .env matches exactly with the one configured in LinkedIn app settings
- Include the protocol (
https://) and no trailing slash
"Insufficient permissions"
- Ensure
w_member_socialscope is requested and approved - Some scopes require LinkedIn app review
Token Errors
"Token expired"
- LinkedIn tokens typically last 60 days
- The system will attempt to refresh automatically
- If refresh fails, user needs to reconnect
"Encryption error"
- Verify
ENCRYPTION_KEYis set in environment - Never change the encryption key after storing tokens (or all tokens become unreadable)
Posting Errors
"Rate limit exceeded"
- LinkedIn has rate limits on posting
- The system will fall back to email notification
- Wait before retrying
"Image upload failed"
- Check that the image URL is publicly accessible
- Supabase storage URLs must be publicly readable
- System will post without image if upload fails
Rate Limits
LinkedIn API has the following rate limits (as of 2024):
- Posts: ~100 per day per user
- API calls: Varies by endpoint
Plan your posting schedule accordingly.
Security Best Practices
- Never log tokens: Tokens are encrypted at rest and should never be logged in plaintext
- HTTPS only: OAuth requires HTTPS in production
- Secure cookies: Session cookies use httponly, secure, and samesite flags
- Token rotation: Encourage users to reconnect periodically
- Audit logging: Monitor for suspicious activity in
linkedin_accounts.last_error
Architecture
┌─────────────────┐
│ Employee │
│ (Settings Page) │
└────────┬────────┘
│ 1. OAuth Flow
▼
┌─────────────────────────┐
│ linkedin_accounts table │
│ (encrypted tokens) │
└────────┬────────────────┘
│ 2. Scheduler checks
▼
┌─────────────────────────┐
│ LinkedIn API Service │
│ (UGC Posts API) │
└─────────────────────────┘
Monitoring
Key metrics to monitor:
- Token expiry: Check
linkedin_accounts.token_expires_at - API errors: Check
linkedin_accounts.last_error - Success rate: Compare scheduled posts vs. published posts
- Fallback rate: How often email fallback is used
Query to check accounts needing attention:
SELECT
user_id,
linkedin_name,
last_error,
last_error_at,
token_expires_at
FROM linkedin_accounts
WHERE
(last_error IS NOT NULL OR token_expires_at < NOW() + INTERVAL '7 days')
AND is_active = true;
Support
For issues or questions:
- Check logs for detailed error messages
- Review LinkedIn API documentation
- Verify environment configuration
- Test with a single user account first