๐ฏ Duda Accounts Service
๐ Overviewโ
The Duda Accounts service manages Duda sub-accounts for client access, SSO (Single Sign-On) for editor access, and site permissions. This enables agencies to provide clients with direct access to the Duda website editor.
Source File: external/Integrations/Duda/services/accounts.service.js
External API: Duda Accounts API
Primary Use: Client onboarding and editor access
๐๏ธ Collections Usedโ
configโ
- Operations: Read
- Model:
external/models/config.js - Usage Context: Fetch Duda API credentials for authentication
๐ Data Flowโ
flowchart TD
A[Create Account Request] --> B[Fetch API Credentials]
B --> C[Generate Base64 Token]
C --> D[POST to Duda API]
D --> E{Success?}
E -->|Yes| F[Return true]
E -->|No| G[Throw Error]
style D fill:#e8f5e9
style F fill:#c8e6c9
๐ง Business Logic & Functionsโ
createAccount(dudaHeader, accountName)โ
Purpose: Create a Duda sub-account (CUSTOMER type) for client access
Source: services/accounts.service.js
External API Endpoint: POST https://api.duda.co/api/accounts/create
Parameters:
dudaHeader(ObjectId) - Config ID containing Duda API credentialsaccountName(String) - Unique account name for the client
Returns: Promise<Boolean> - Returns true on success
Business Logic Flow:
-
Fetch API Credentials
let query = await Config.findById(dudaHeader);
if (!query) {
throw notFound('Api token not found');
} -
Validate Account Name
if (!accountName) {
throw badRequest('Account name is required.');
} -
Build Request
const access_token = utils.base64(query);
const dataToSend = {
account_name: accountName,
account_type: 'CUSTOMER', // Always CUSTOMER type
}; -
Call Duda API
const options = {
method: 'POST',
url: 'https://api.duda.co/api/accounts/create',
headers: {
'content-type': 'application/json',
authorization: `Basic ${access_token}`,
},
data: dataToSend,
};
await axios(options);
return true;
API Request Example:
POST https://api.duda.co/api/accounts/create
Authorization: Basic {base64_token}
Content-Type: application/json
{
"account_name": "client-company-123",
"account_type": "CUSTOMER"
}
Error Handling:
- 400 Bad Request: Missing account name
- 404 Not Found: API credentials not found
- 409 Conflict: Account name already exists (from Duda)
Example Usage:
await accountsService.createAccount(dudaConfigId, 'acme-corp');
// Creates Duda account for Acme Corp client
Key Business Rules:
- Account Type: Always creates CUSTOMER type accounts (not STAFF or AGENCY)
- Unique Names: Account names must be unique across Duda platform
- No Return Data: Duda doesn't return account details, just success/failure
grantSiteAccess(dudaHeader, { siteName, accountName, permissions })โ
Purpose: Grant a Duda account access to a specific site with defined permissions
Source: services/accounts.service.js
External API Endpoint: POST https://api.duda.co/api/accounts/:accountName/sites/:siteName/permissions
Parameters:
dudaHeader(ObjectId) - Config IDsiteName(String) - Duda site IDaccountName(String) - Duda account namepermissions(String[]) - Permission levels- Options:
['EDIT'],['PUBLISH'],['EDIT', 'PUBLISH'], etc.
- Options:
Returns: Promise<Boolean> - Returns true on success
Business Logic Flow:
-
Fetch Credentials
let query = await Config.findById(dudaHeader);
if (!query) {
throw notFound('Api token not found');
} -
Validate Permissions
if (!permissions) {
throw badRequest('Invalid request body.');
} -
Grant Access
const access_token = utils.base64(query);
const dataToSend = {
permissions,
};
const options = {
method: 'POST',
url: `https://api.duda.co/api/accounts/${accountName}/sites/${siteName}/permissions`,
headers: {
'content-type': 'application/json',
authorization: `Basic ${access_token}`,
},
data: dataToSend,
};
await axios(options);
return true;
API Request Example:
POST https://api.duda.co/api/accounts/acme-corp/sites/site_abc123/permissions
Authorization: Basic {base64_token}
Content-Type: application/json
{
"permissions": ["EDIT", "PUBLISH"]
}
Permission Levels:
- EDIT: Can edit site content and design
- PUBLISH: Can publish/unpublish site
- STATS_TAB: Can view analytics
- DEV_MODE: Can access developer mode
- REPUBLISH: Can republish site
Example Usage:
await accountsService.grantSiteAccess(dudaConfigId, {
siteName: 'site_abc123',
accountName: 'acme-corp',
permissions: ['EDIT', 'PUBLISH'],
});
// Acme Corp can now edit and publish the site
Key Business Rules:
- Multiple Permissions: Can grant multiple permissions at once
- Overwrites Previous: New permission call overwrites previous permissions
- Account Must Exist: Account must be created before granting access
getSSOLink(dudaHeader, account_name, params)โ
Purpose: Generate a Single Sign-On (SSO) link for direct editor access without login
Source: services/accounts.service.js
External API Endpoint: GET https://api.duda.co/api/accounts/sso/:account_name/link
Parameters:
dudaHeader(ObjectId) - Config IDaccount_name(String) - Duda account nameparams(Object) - SSO parameterstarget(String) - Destination:'EDITOR','DASHBOARD','STATS'site_name(String, optional) - Specific site to openlang(String, optional) - Language code (e.g.,'en','es')
Returns: Promise<Object>
{
url: String; // Time-limited SSO link
}
Business Logic Flow:
-
Fetch Credentials
let query = await Config.findById(dudaHeader);
if (!query) {
throw notFound('Api token not found');
} -
Generate SSO Link
const access_token = utils.base64(query);
let options = {
method: 'GET',
url: `https://api.duda.co/api/accounts/sso/${account_name}/link`,
headers: {
'Content-Type': 'application/json',
authorization: `Basic ${access_token}`,
},
params, // target, site_name, lang
};
const response = await axios(options);
return response.data;
API Request Example:
GET https://api.duda.co/api/accounts/sso/acme-corp/link?target=EDITOR&site_name=site_abc123
Authorization: Basic {base64_token}
API Response Example:
{
"url": "https://dashboard.duda.co/sso/abc123xyz?token=..."
}
SSO Targets:
- EDITOR: Opens site editor for specific site (requires
site_name) - DASHBOARD: Opens account dashboard (all sites)
- STATS: Opens site analytics (requires
site_name)
Example Usage:
// Editor access for specific site
const ssoData = await accountsService.getSSOLink(dudaConfigId, 'acme-corp', {
target: 'EDITOR',
site_name: 'site_abc123',
lang: 'en',
});
// Redirect user to SSO link
res.redirect(ssoData.url);
Key Business Rules:
- Time-Limited: SSO links expire after a short period (check Duda docs)
- Single Use: Links may be single-use only
- Requires Site Access: Account must have permissions for the site
- Target Required: Must specify target destination
- Site Required for EDITOR/STATS: Must provide site_name for EDITOR and STATS targets
๐ Integration Pointsโ
Client Onboarding Workflowโ
// Complete client onboarding
async function onboardClient(dudaConfigId, clientName, siteId) {
// 1. Create Duda account
await accountsService.createAccount(dudaConfigId, clientName);
// 2. Grant site access
await accountsService.grantSiteAccess(dudaConfigId, {
siteName: siteId,
accountName: clientName,
permissions: ['EDIT', 'PUBLISH'],
});
// 3. Generate SSO link for immediate access
const ssoData = await accountsService.getSSOLink(dudaConfigId, clientName, {
target: 'EDITOR',
site_name: siteId,
});
return ssoData.url;
}
Embeddable Editorโ
// Embed Duda editor in iframe
const ssoData = await accountsService.getSSOLink(dudaConfigId, accountName, {
target: 'EDITOR',
site_name: siteId,
});
// Display in iframe
const iframeHtml = `<iframe src="${ssoData.url}" width="100%" height="800px"></iframe>`;
๐งช Edge Cases & Special Handlingโ
Account Already Existsโ
Issue: Account name already taken in Duda
Handling: Duda API returns error, propagated to caller
// Duda returns 409 Conflict
// Code doesn't handle duplicates explicitly
Missing Permissionsโ
Issue: Permissions parameter empty or undefined
Handling: Throws 400 Bad Request
if (!permissions) {
throw badRequest('Invalid request body.');
}
Invalid Targetโ
Issue: Invalid SSO target specified
Handling: Duda API returns error
Expired SSO Linksโ
Issue: User clicks expired SSO link
Handling: Duda shows error page, regenerate link
โ ๏ธ Important Notesโ
- Account Type: Always creates
CUSTOMERtype accounts (not STAFF or AGENCY) - Account Names: Must be unique across entire Duda platform
- Permission Overwrite: New permissions replace old ones, not additive
- SSO Expiration: Links expire quickly (minutes), generate on-demand
- Single Use Links: SSO links may be single-use, don't cache
- Site Access Required: Account needs permissions before SSO to EDITOR/STATS
- No Account Data: Create account doesn't return account details
- Language Support: SSO supports multiple languages via
langparameter - Target Validation: EDITOR and STATS targets require
site_nameparameter - Security: SSO links bypass authentication, protect them carefully
๐ Related Documentationโ
- Duda Integration Overview: index.md
- Sites Service: sites.md - Site management
- Duda Accounts API: Official Documentation
- SSO Documentation: Duda SSO Guide
- Config Model:
external/models/config.js