| name | oauth-platform-integration |
| description | Add new OAuth platform connectors to Twin Me Soul Signature Platform. Use when the user wants to integrate a new platform (Spotify, Netflix, LinkedIn, TikTok, etc.) for soul signature data extraction. |
OAuth Platform Integration Skill
This skill streamlines adding new OAuth-enabled platforms to the Twin Me platform for authentic soul signature data collection.
When to Use This Skill
Trigger this skill when:
- User requests adding a new platform connector (e.g., "Add Spotify integration")
- Implementing OAuth flow for entertainment or professional platforms
- Setting up data extraction for a new service
Prerequisites
Before adding a platform, verify:
- Platform has a public OAuth 2.0 API
- OAuth credentials obtained (CLIENT_ID, CLIENT_SECRET)
- Platform API documentation accessible
Integration Checklist
1. OAuth App Configuration (External)
For each platform, configure OAuth app settings:
Required redirect URIs:
- Local development:
http://localhost:8086/oauth/callback
- Production:
https://twin-ai-learn.vercel.app/oauth/callback
Required OAuth scopes (platform-specific):
- Spotify:
user-read-recently-played, user-top-read, user-library-read
- Discord:
identify, guilds, messages.read
- GitHub:
user, repo (read-only)
- LinkedIn:
r_liteprofile, r_emailaddress
- Netflix: (No official API - requires browser extension approach)
2. Environment Variables
Add to .env file:
# [PLATFORM_NAME] OAuth
[PLATFORM]_CLIENT_ID=your-client-id
[PLATFORM]_CLIENT_SECRET=your-client-secret
Example:
SPOTIFY_CLIENT_ID=abc123def456
SPOTIFY_CLIENT_SECRET=xyz789uvw012
3. Platform API Configuration
Add platform config to api/services/platformAPIMappings.js:
const PLATFORM_CONFIGS = {
[newPlatform]: {
name: 'Platform Display Name',
authUrl: 'https://platform.com/oauth/authorize',
tokenUrl: 'https://platform.com/oauth/token',
scopes: ['scope1', 'scope2', 'scope3'],
apiBaseUrl: 'https://api.platform.com/v1',
endpoints: {
userProfile: '/me',
recentActivity: '/me/recent',
topItems: '/me/top',
},
tokenType: 'Bearer',
refreshable: true,
rateLimit: {
requests: 100,
window: 3600,
},
},
};
4. OAuth Route Implementation
Add OAuth routes to api/routes/entertainment-connectors.js (or mcp-connectors.js for professional platforms):
router.get('/connect/:platform', async (req, res) => {
const { platform } = req.params;
const userId = req.user.id;
const config = PLATFORM_CONFIGS[platform];
if (!config) {
return res.status(404).json({ error: 'Platform not supported' });
}
const state = crypto.randomBytes(16).toString('hex');
await saveOAuthState(userId, platform, state);
const authUrl = new URL(config.authUrl);
authUrl.searchParams.append('client_id', process.env[`${platform.toUpperCase()}_CLIENT_ID`]);
authUrl.searchParams.append('redirect_uri', `${process.env.CLIENT_URL}/oauth/callback`);
authUrl.searchParams.append('response_type', 'code');
authUrl.searchParams.append('scope', config.scopes.join(' '));
authUrl.searchParams.append('state', state);
res.json({ authUrl: authUrl.toString() });
});
router.get('/callback/:platform', async (req, res) => {
const { platform } = req.params;
const { code, state } = req.query;
try {
const validState = await verifyOAuthState(state);
if (!validState) {
throw new Error('Invalid OAuth state');
}
const tokens = await exchangeCodeForTokens(platform, code);
await savePlatformConnection(validState.userId, platform, {
accessToken: tokens.access_token,
refreshToken: tokens.refresh_token,
expiresAt: Date.now() + (tokens.expires_in * 1000),
});
res.redirect(`${process.env.CLIENT_URL}/get-started?connected=${platform}`);
} catch (error) {
console.error(`OAuth callback error for ${platform}:`, error);
res.redirect(`${process.env.CLIENT_URL}/get-started?error=${platform}`);
}
});
5. Data Extraction Service
Add extraction logic to api/services/dataExtraction.js:
async function extract[PlatformName]Data(userId) {
const connection = await getPlatformConnection(userId, '[platform]');
if (!connection || !connection.accessToken) {
throw new Error('Platform not connected');
}
try {
const accessToken = decryptToken(connection.accessToken);
const config = PLATFORM_CONFIGS['[platform]'];
const response = await axios.get(
`${config.apiBaseUrl}${config.endpoints.recentActivity}`,
{
headers: {
'Authorization': `${config.tokenType} ${accessToken}`,
},
}
);
const extractedData = transformPlatformData(response.data, '[platform]');
await saveSoulData(userId, {
platform: '[platform]',
dataType: 'recent_activity',
rawData: response.data,
extractedPatterns: extractedData,
extractedAt: new Date(),
});
return {
success: true,
itemsExtracted: extractedData.length,
platform: '[platform]',
};
} catch (error) {
if (error.response?.status === 401) {
await markConnectionRequiresReauth(userId, '[platform]');
return {
success: false,
requiresReauth: true,
error: 'Token expired - reconnection required',
};
}
throw error;
}
}
6. Frontend Platform Card
Add platform card to src/pages/GetStarted.tsx:
const platformConfig = {
[newPlatform]: {
name: 'Platform Name',
icon: PlatformIcon,
description: 'What this platform reveals about your soul signature',
setupTime: '10 seconds setup',
category: 'essential' | 'optional',
insights: [
'Primary Insight Type',
'Secondary Insight Type',
'+N more',
],
color: '#BRAND_COLOR',
},
};
7. Database Schema Update
Ensure data_connectors table supports the new platform:
SELECT * FROM data_connectors
WHERE provider = '[platform_name]';
ALTER TABLE data_connectors
ADD COLUMN IF NOT EXISTS platform_metadata JSONB DEFAULT '{}'::jsonb;
8. Soul Signature Mapping
Define how platform data maps to personality traits in api/services/soulSignatureAnalyzer.js:
const PLATFORM_TRAIT_MAPPINGS = {
[newPlatform]: {
traits: {
openness: (data) => calculateOpenness(data.genres, data.diversity),
conscientiousness: (data) => calculateConscientiousness(data.organizationLevel),
extraversion: (data) => calculateExtraversion(data.socialActivity),
agreeableness: (data) => calculateAgreeableness(data.interactions),
neuroticism: (data) => calculateNeuroticism(data.emotionalContent),
},
communication: (data) => analyzeCommunicationStyle(data.messages),
interests: (data) => extractInterests(data.content),
},
};
Testing Checklist
After implementation, verify:
Common Pitfalls
Redirect URI Mismatch
Error: "redirect_uri did not match"
Solution: Ensure OAuth app has BOTH localhost AND production URLs configured
Token Encryption Key Mismatch
Error: "Unsupported state or unable to authenticate data"
Solution: Use current ENCRYPTION_KEY from .env, clear old tokens before reconnecting
Rate Limiting
Error: 429 Too Many Requests
Solution: Implement exponential backoff, respect platform rate limits in PLATFORM_CONFIGS
Scope Permissions
Error: 403 Forbidden on specific endpoints
Solution: Verify OAuth scopes match required API permissions
Platform-Specific Notes
Spotify
- Requires both user authorization AND token refresh flow
- Rate limit: 180 requests per minute
- Best practice: Batch requests for user's top tracks/artists
Discord
- Guilds endpoint requires
guilds scope
- Message access limited to servers bot has joined
- Rate limit: 50 requests per second globally
GitHub
- Only allows ONE callback URL per OAuth app
- Recommendation: Create separate dev OAuth app for localhost
- Rate limit: 5,000 requests/hour for authenticated requests
LinkedIn
- V2 API requires separate permissions for profile vs email
- Limited data access compared to V1 (deprecated)
- Rate limit: varies by endpoint
Netflix / Streaming Platforms
Note: Most streaming platforms (Netflix, HBO, Disney+) have NO public API
Alternative Approach: Browser extension for data extraction
- See
browser-extension/ directory
- Uses content scripts to capture viewing history
- Sends data to backend via secure API
File References
For detailed implementation examples, see:
- OAuth Config:
./oauth-config-examples.md
- Extraction Patterns:
./extraction-patterns.md
- API Rate Limiting:
./rate-limiting-guide.md
Quick Start Template
Use the provided script to scaffold a new platform integration:
cd twin-me
node skills/oauth-platform-integration/scripts/scaffold-platform.js <platform-name>
This generates:
- Environment variable template
- Platform config skeleton
- OAuth route boilerplate
- Extraction service stub
- Frontend platform card
- Test suite
Success Criteria
Platform integration is complete when:
- ✅ OAuth flow works end-to-end (local + production)
- ✅ Tokens stored encrypted in database
- ✅ Data extraction returns > 0 items
- ✅ Platform appears as "Connected" in UI
- ✅ Soul signature updates with new data
- ✅ No console errors or token decryption issues
Skill Version: 1.0.0
Last Updated: January 18, 2025
Maintainer: Twin Me Development Team