OAuth 2.0 Authentication
🔐 OAuth 2.0 Flow
Mailchimp uses standard OAuth 2.0 authorization code flow with automatic datacenter detection.
🌐 Environment Variables
| Variable | Description | Example |
|---|---|---|
MAILCHIMP_CLIENT_ID | OAuth client ID | 1234567890 |
MAILCHIMP_CLIENT_SECRET | OAuth client secret | abcdef123456 |
MAILCHIMP_REDIRECT_URI | OAuth callback URL | https://api.dashclicks.com/v1/integrations/mailchimp/callback |
MAILCHIMP_AUTHORIZATION_URL | OAuth authorization endpoint | https://login.mailchimp.com/oauth2/authorize |
MAILCHIMP_TOKEN_URL | Token exchange endpoint | https://login.mailchimp.com/oauth2/token |
MAILCHIMP_DATACENTER_URL | Metadata/datacenter endpoint | https://login.mailchimp.com/oauth2/metadata |
MAILCHIMP_RESPONSE_TYPE | OAuth response type | code |
MAILCHIMP_GRANT_TYPE | OAuth grant type | authorization_code |
📋 API Endpoints
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| GET | /v1/integrations/mailchimp/auth/login | Initiate OAuth flow | ✅ JWT |
| GET | /v1/integrations/mailchimp/callback | OAuth callback handler | ❌ |
| DELETE | /v1/integrations/mailchimp/auth | Delete stored token | ✅ JWT |
🔄 Authentication Flow
Step 1: Initiate OAuth
Endpoint: GET /auth/login
Query Parameters:
forward_url(required) - URL to redirect after OAuth completes
Request:
GET /v1/integrations/mailchimp/auth/login?forward_url=https://app.dashclicks.com/integrations
Authorization: Bearer {jwt_token}
Process:
- Check for existing token in database
- If token exists and valid → Redirect to
forward_urlwith success - If token invalidated → Delete and re-authenticate
- If no token → Generate JWT state token and redirect to Mailchimp
JWT State Token:
{
aid: "account_id", // DashClicks account ID
uid: "user_id", // DashClicks user ID
forward_url: "https://..." // Return URL
}
Authorization URL Format:
https://login.mailchimp.com/oauth2/authorize
?response_type=code
&client_id={MAILCHIMP_CLIENT_ID}
&redirect_uri={MAILCHIMP_REDIRECT_URI}
&state={JWT_STATE_TOKEN}
Success Response (existing connection):
HTTP/1.1 301 Moved Permanently
Location: https://app.dashclicks.com/integrations?status=success&integration=mailchimp&token={token_id}
Redirect Response (new connection):
HTTP/1.1 302 Found
Location: https://login.mailchimp.com/oauth2/authorize?...
Step 2: OAuth Callback
Endpoint: GET /callback
Query Parameters:
code(required) - Authorization code from Mailchimpstate(required) - JWT state token from Step 1
Process:
- Decode JWT state token
- Exchange authorization code for access token
- Fetch datacenter metadata
- Store token with datacenter info in MongoDB
- Redirect to
forward_urlwith success status
Token Exchange Request:
POST https://login.mailchimp.com/oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id={CLIENT_ID}
&client_secret={CLIENT_SECRET}
&redirect_uri={REDIRECT_URI}
&code={AUTHORIZATION_CODE}
Token Exchange Response:
{
"access_token": "a0b219e049fb4f027fd927ade6a21d38",
"token_type": "bearer",
"scope": null
}
Datacenter Metadata Request:
GET https://login.mailchimp.com/oauth2/metadata
Authorization: Bearer {access_token}
Datacenter Metadata Response:
{
"dc": "us8",
"login": {
"email": "user@example.com",
"login_id": "12345",
"login_name": "User Name",
"login_email": "user@example.com"
},
"accountname": "Example Account",
"user_id": 67890,
"api_endpoint": "https://us8.api.mailchimp.com"
}
MongoDB Document Created:
{
token: {
access_token: "a0b219e049fb4f027fd927ade6a21d38",
token_type: "bearer",
scope: null,
metadata: {
dc: "us8" // Datacenter extracted from metadata
}
},
account_id: "12345",
owner: "user_Lwh9EzeD8",
token_invalidated: false
}
Success Response:
HTTP/1.1 301 Moved Permanently
Location: https://app.dashclicks.com/integrations?status=success&integration=mailchimp&token={token_id}
Error Response:
HTTP/1.1 301 Moved Permanently
Location: https://app.dashclicks.com/integrations?status=error&reason={error_message}&integration=mailchimp
Step 3: Delete Token
Endpoint: DELETE /auth
Request:
DELETE /v1/integrations/mailchimp/auth
Authorization: Bearer {jwt_token}
Success Response:
{
"success": true,
"message": "Access Token has been successfully deleted"
}
Error Response (token not found):
{
"success": false,
"errno": 400,
"message": "Access Token Not Found"
}
🔑 Token Management
Token Storage
Tokens stored in integrations.mailchimp.key collection:
{
_id: ObjectId("..."),
token: {
access_token: "...",
token_type: "bearer",
scope: null,
metadata: { dc: "us8" }
},
account_id: "12345",
owner: "user_Lwh9EzeD8",
token_invalidated: false
}
Token Lookup
Model Method: searchTokenQuery(owner_id, account_id)
const result = await MailchimpKey.findOne({
account_id: account_id,
owner: owner_id,
})
.lean()
.exec();
Returns:
- Success:
{ id, token, account_id, owner, ... } - Not found:
{ error: 'No token found' }
Token Invalidation
Tokens can be marked as invalidated:
{
token_invalidated: true; // Flag for soft deletion
}
When invalidated token is found during login, it's deleted and user re-authenticates.
🎯 Authorization Middleware
All protected endpoints require JWT authentication:
req.auth = {
status: 'VALID_TOKEN',
auth: {
workspace_id: '1234567890',
account_id: '12345',
uid: 'user_Lwh9EzeD8',
scope: 'mailchimp, mailchimp.create mailchimp.delete mailchimp.read',
// ... other JWT claims
},
};
Required Scopes
| Operation | Required Scopes |
|---|---|
| Login | mailchimp, mailchimp.create |
| Delete | mailchimp, mailchimp.delete |
| Export | mailchimp, mailchimp.read |
🌍 Datacenter Detection
Why Datacenters Matter
Mailchimp accounts are region-specific. API requests must use datacenter-specific URLs:
https://{dc}.api.mailchimp.com/3.0/...
Common datacenters:
us1- United States (East)us8- United States (Central)us19- United States (West)
Automatic Detection
The integration automatically:
- Fetches metadata after OAuth
- Extracts
dcfield - Stores in token metadata
- Uses for all subsequent API calls
⚠️ Error Handling
| Error | Status | Response |
|---|---|---|
Missing forward_url | 400 | { success: false, errno: 400, message: "forward url is required!" } |
| Token already exists | 403 | { success: false, errno: 400, message: "Access Token is already stored! Please delete it." } |
| Query code missing | 403 | { success: false, errno: 400, message: "Query Code not found" } |
| Token not found (delete) | 404 | { success: false, errno: 400, message: "Access Token Not Found" } |
| OAuth exchange failed | 400 | Redirects to forward_url with error |
🔒 Security Features
JWT State Token
- Expiry: 1 hour
- Secret:
process.env.APP_SECRET - Claims: Account ID, User ID, Forward URL
- Purpose: Prevent CSRF attacks and maintain session state
Token Encryption
Tokens stored in MongoDB may be encrypted at rest depending on deployment configuration.
Single Connection Per User
Only one active connection allowed per user/account combination to prevent token proliferation.
📝 Important Notes
- 🔒 Never Expires: Mailchimp access tokens have no expiration
- 🔄 No Refresh: No refresh token needed - access tokens are persistent
- 🌍 Datacenter Required: All API calls need correct datacenter
- 👤 One Token: Single connection per user/account pair
- ⚡ Fast Auth: No token refresh overhead in API calls