Skip to main content

Configure a DXP Provider

A DXP Provider is a Stack9 Core instance where your users log in. When DXP is enabled in provider mode, the navigation bar shows DXP tiles (Email, Marketing, Pages) alongside regular app tiles. Clicking a tile issues a short-lived one-time code and redirects the user to the DXP Client instance — no second login required.


Prerequisites

  • Stack9 Core instance running v5.0.0 or later.
  • The DXP modules you want to expose must be listed in Modules in stack9.config.json (e.g. @april9au/stack9-marketing-module).
  • A DXP Client instance is already deployed and its URL is known.
  • A shared secret string agreed upon between provider and client operators (any random string; minimum 16 characters recommended).

Step 1: Set environment variables

These variables must be present in the APP_SECRETS block (or equivalent env injection) for the Core instance:

VariableRequiredDescription
APP_DXP_CODE_EXCHANGE_SECRETYesPre-shared secret used by the Client to authenticate server-to-server code exchange. Must match APP_DXP_CODE_EXCHANGE_SECRET on the Client exactly.
APP_DXP_CODE_TTL_SECONDSNoLifetime of one-time codes in seconds. Default: 30. Increase only for testing purposes.
SERVER_BASE_URLYesThe Provider's own public base URL (e.g. https://acme.stack9.com). Included in the exchange payload so the Client can validate the origin and render a "Back to" link.
APP_S9_APIS_API_KEYYesThe tenant's API key for the Stack9 APIs service. Delivered to the Client via code exchange so it can proxy API calls on behalf of the tenant.
APP_S9_APIS_BASE_URLYesBase URL of the Stack9 APIs service (e.g. https://api.stack9.com).
AUTH_TENANT_IDYesThe organisation/tenant identifier. Included in the code payload as orgId.

Example APP_SECRETS fragment:

{
"APP_DXP_CODE_EXCHANGE_SECRET": "your-shared-secret-here",
"APP_DXP_CODE_TTL_SECONDS": "30",
"APP_S9_APIS_API_KEY": "sk-...",
"APP_S9_APIS_BASE_URL": "https://api.stack9.com"
}

Step 2: Configure stack9.config.json

Add a dxp block to the instance's stack9.config.json. Set mode to "provider" and list each DXP app you want to expose as a tile.

{
"ProjectName": "acme-core",
"Stack9CoreVersion": "5.0.0",
"Modules": [
"@april9au/stack9-core-module",
"@april9au/stack9-marketing-module",
"@april9au/stack9-email-module",
"@april9au/stack9-pages-module"
],
"dxp": {
"mode": "provider",
"apps": [
{
"appKey": "mkt",
"path": "/mkt"
},
{
"appKey": "email",
"path": "/email"
},
{
"appKey": "pages",
"path": "/pages"
}
]
}
}

dxp block fields

FieldTypeDescription
mode"provider"Activates provider mode. The DXP tile redirect endpoints become active.
appsarrayEach entry represents one DXP app tile.
apps[].appKey"email" | "mkt" | "pages"Identifies the DXP module. Must match a module installed in Modules.
apps[].pathstringThe path on the Client instance to redirect to after sign-in (e.g. /mkt).
apps[].iconNamestring (optional)Overrides the default icon for this tile.
apps[].descriptionstring (optional)Overrides the default tile description.

Startup validation: The instance will refuse to start if APP_DXP_CODE_EXCHANGE_SECRET is missing, if apps is empty, or if any appKey references a module not in Modules.


Step 3: Run bootstrap

Bootstrap provisions the app_permissions records required for DXP tile visibility. Without this step, DXP tiles will not appear in navigation for any user.

node main.js bootstrap

Bootstrap is idempotent — it safe to re-run. It will:

  • Create or update an app_permissions row for each DXP appKey.
  • Set minimumRole ≤ Privileged (3) so tiles are visible to users with that access level.

Permissions are self-managed: You do not need to create App Permissions entries manually in the Stack9 admin UI. Bootstrap creates them automatically.


How it works at runtime

When a user clicks a DXP tile, the Provider's navigation handler calls:

GET /api/auth/dxp/redirect/:appType

This endpoint:

  1. Generates a cryptographically random 32-byte one-time code.
  2. Stores the code in Redis alongside the user's identity, roles, API key, and origin URL. The code expires after DXP_CODE_TTL_SECONDS (default: 30 seconds).
  3. Redirects the browser to the Client:
{clientUrl}/api/auth/dxp/callback?code={code}&origin={providerOrigin}&redirect={path}
sequenceDiagram
participant U as Browser
participant P as Provider
participant R as Redis

U->>P: Click DXP tile (authenticated)
P->>P: Generate one-time code (32-byte hex)
P->>R: Store code payload (TTL: 30s)
P-->>U: HTTP 302 → Client /api/auth/dxp/callback?code=X&origin=Y&redirect=/mkt
Note over U: Browser follows redirect to Client

The Client then performs a server-to-server exchange with the Provider (see the DXP Auth Flow concept page and the Configure DXP Client guide).


Verifying the setup

  1. Log in to the Provider instance.
  2. Open the navigation bar — DXP tiles should appear for each configured appKey.
  3. Click a DXP tile — you should be redirected to the Client and automatically signed in.
  4. Check the Provider server logs for:
    • dxp.provider.code.issued — confirms the code was generated and stored.

If tiles do not appear, check:

  • APP_DXP_CODE_EXCHANGE_SECRET is set and not empty.
  • Bootstrap has been run after the dxp config block was added.
  • The appKey values in config match installed modules.

App permissions and tile visibility

Each DXP tile has an app_permissions row with minimumRole set to Privileged (3) by default. A user must have a role level of at least 3 to see the tile. Administrators can raise the minimumRole in the App Permissions admin panel to restrict access further.

Do not lower minimumRole below the Provider's DXP-granted level — this would have no effect since tile visibility is gated by the minimum, not the user's maximum role.