Authentication & Iframe Governance
Secure your application with OAuth 2.0 and manage seamless Iframe embedding within the DonutWork dashboard.
Authentication & Embedding
DonutWork uses a robust security model to ensure that third-party applications can securely access data while providing a unified user experience. This guide covers the Dual-Auth workflow and the requirements for enterprise-grade Iframe embedding.
1. The OAuth 2.0 Workflow
Applications use a standard OAuth 2.0 Authorization Code flow to obtain scoped access to a company's data.
Step 1: Authorization Request
Redirect the user to the DonutWork authorization server. This occurs when a user first installs or accesses your app.
Endpoint: GET /i3/oauth/authorize
Parameters:
client_id: Your unique application identifier.redirect_uri: The endpoint where the code will be sent.response_type: Alwayscode.scope: Space- or comma-separated list of permissions in canonical formatresource:action(e.g.,customers:read charges:write).scopeminimum requirement: include at leastcompany:readto complete a valid OAuth connection flow for app integrations.state: A unique string to prevent CSRF attacks.
Step 2: Token Exchange
Once the user approves the request, DonutWork redirects them to your redirect_uri with a code. Exchange this code for an access token immediately.
Endpoint: POST /i3/oauth/token (Form-Encoded)
Body:
client_id: Your application identifier.client_secret: Your secure application secret.code: The authorization code received in the redirect.redirect_uri: Must match the URI used in Step 1.
Step 3: Persistence
Store the returned access_token and refresh_token securely. These tokens allow your app to make direct API calls to DonutWork (e.g., to /2026-02-01/customers.json).
2. Iframe Context & Security
When an application is rendered inside the DonutWork dashboard, it is embedded via an Iframe. Every request to your application's entry point includes a security signature.
Request Parameters
DonutWork appends the following query parameters to your application URL:
company: The unique identifier of the active company.host: The Base64-encoded DonutWork host URL.timestamp: The Unix timestamp of the request.nonce: A unique value for the iframe launch.hmac: A cryptographic signature of the launch parameters.
When the user selects a declared customer:details action from the customer page's More actions menu, DonutWork also appends:
action_type: The declaredui_actions[].key.action_id: The ID of the customer whose page launched the action.
These values are visible in the iframe URL. DonutWork includes them in the HMAC and checks that the user can view the customer and access the app (customers.view and developer.view). They identify the requested context; they do not grant your app access to customer data or authorize a mutation. The app must use its own authorized API access for data requests and require an explicit user action before making changes.
Signature Verification (HMAC-SHA256)
To verify that the request is authentic and originated from DonutWork, you must validate the hmac parameter using your client_secret.
Verification Procedure:
- Remove the
hmacparameter from the key-value pairs. - Sort the remaining parameters alphabetically by key.
- Concatenate the sorted keys and values as
key=valuepairs joined with&, without URL encoding them again. Include every parameter, including any already present in yourcanvas_uriand the optionalaction_typeandaction_id. - Calculate the HMAC-SHA256 of this string using your
client_secretas the key. - Compare the result with the provided
hmacin constant time. Reject missing or malformed signatures, and check timestamp freshness and nonce reuse according to your session policy.
const crypto = require('crypto');
function verifyHmac(queryParams, clientSecret) {
const { hmac, ...params } = queryParams;
const message = Object.keys(params)
.sort()
.map(key => `${key}=${params[key]}`)
.join('&');
const expectedHmac = crypto
.createHmac('sha256', clientSecret)
.update(message)
.digest('hex');
if (typeof hmac !== 'string' || !/^[a-f0-9]{64}$/i.test(hmac)) return false;
return crypto.timingSafeEqual(Buffer.from(hmac, 'hex'), Buffer.from(expectedHmac, 'hex'));
}1. ARGS = QueryString.Remove("hmac")
2. SORTED_MSG = ARGS.SortByKey().Join("&")
3. SIGNATURE = HMAC_SHA256(SORTED_MSG, APP_CLIENT_SECRET)
4. VALIDATE_CONSTANT_TIME(SIGNATURE, QueryString["hmac"])Verify the signature before trusting company, action_type, or action_id. Refreshing an iframe URL can repeat the launch, so do not treat a signed URL alone as authorization to perform an irreversible operation.
3. UI Integration Requirements
Because the application runs in a cross-site Iframe, standard browser security policies apply.
Session Management
Modern browsers block third-party cookies by default. To maintain a session within the Iframe, your session cookies must use the following attributes:
SameSite=None: Allows the cookie to be sent in a cross-site context.Secure=true: Required whenSameSite=Noneis used.
If your cookies do not have SameSite=None; Secure, users will be logged out immediately after redirection or when navigating within the app.
Content Security Policy (CSP)
Ensure your server headers allow DonutWork to embed your application:
Content-Security-Policy: frame-ancestors 'self' https://hub.donutwork.com;
X-Frame-Options: ALLOW-FROM https://hub.donutwork.com4. Capability Discovery
As the final step of the authentication flow, your application should declare its "capabilities" to the platform. This allows DonutWork to proxy requests to your app (Actions).
Action: PUT /2026-02-01/integrations.json
This declaration should include your app_token (used by DonutWork to call you) and a manifest of supported endpoints. Refer to the Application Discovery Guide for details.
For uninstall flow details (DELETE uninstall_url with company_id + HMAC verify), refer to How to Build.