Skip to content

Delegated Authorization

Delegated Authorization provides a method for Processing Framework Operations to securely interface with a customer’s private resources that are involved in a Procedure Execution’s data processing workflow.

Put simply, an operation may need to call another Trimble service on behalf of the person who started the execution. Delegated Authorization enables this without embedding user credentials in engine code.

Due to the sensitive nature of the functionality, Delegated Authorization is only available to Products and their Engine(s) that have undergone a Well Architected Review by the Cloud Platform Architecture organization.

Delegated Authorization uses four identity roles in each Subject Delegation transaction.

Before defining those roles, it is useful to outline the high-level flow:

  • A user starts a Procedure Execution.
  • Processing Framework obtains a delegated token for that execution context.
  • The running engine uses that delegated token to call target APIs.
  • The token represents a limited delegation for specific targets/scopes, not unrestricted access.

In this page, a Subject Delegation transaction refers to one token request and token use cycle for one operation run, with one Subject, one Host, one Delegate, and one or more Targets.

Identity Roles

Subject Identity

The identity of the user (or client) that owns the Procedure Execution.

This answers the question: “Whose data and permissions is the operation using?”

  • This is the execution owner context.
  • The delegated token represents this subject context.
  • If an operation reads/writes customer-owned resources, it is doing so as this subject.

Host Identity

The Processing Framework host identity used by the platform to request Subject Delegation tokens.

  • This identity belongs to Processing Framework platform.
  • It is granted special IAM permissions to call Subject Delegation flows.
  • Engine code does not use Host Identity credentials directly.

Delegate Identity

This is the identity of the Engine Owner’s TID Application. This is a unique Application solely for the purpose of being used in the context of a Processing Framework Engine.

This answers the question: “Which engine publisher or software is being authorized as the delegated party?”

  • It identifies the engine owner’s software in the delegation flow.
  • It is configured on deployment (delegated_authorization.delegate_identity).
  • It should be a dedicated application identity, not a personal identity.

Target Identities

These represent the identity or identities of the external-to-PF services that an Engine communicates with and are the scope(s) that are specified in the audience of the Access Token.

This answers the question: “Which downstream APIs can this delegated token be used with?”

  • They constrain where the delegated token is valid.
  • They are configured per operation (delegated_authorization.target_identities).
  • They should follow least-privilege principles and include only the target scopes that are required.

Delegated Authorization Identities Sequence

DPoP Concept (Why Two Headers Are Required)

Delegated access tokens are DPoP-bound. This means the token is tied to a keypair, and each outbound API call must include a fresh DPoP proof JWT signed by the corresponding private key.

In practice:

  • Authorization: DPoP <access_token> says what delegated token is being used.
  • DPoP: <dpop-jwt> proves the caller holds the private key bound to that token and binds the proof to one request.
  • The DPoP proof includes request-specific claims (htm, htu) that bind the proof to a single request.

Platform vs Engine Responsibilities

Delegated Authorization relies on a clear separation between Processing Framework platform responsibilities and engine responsibilities.

Processing Framework platform responsibilities

  • Capture the execution owner identity when the Procedure Execution is created.
  • Use the Host Identity to request delegated tokens through the Subject Delegation flow.
  • Handle platform-side security requirements such as mTLS configuration for the token request.
  • Generate and manage the DPoP-bound credential materials delivered to the running operation.
  • Refresh delegated credentials before expiry for long-running operations.

Engine responsibilities

  • Read delegated credential file paths from task_input["delegated_authorization"].
  • Load the delegated token and DPoP key material from those files at runtime.
  • Generate a fresh DPoP proof for each outbound request.
  • Call only the target APIs/scopes the operation was configured for.

The key boundary is that engine code uses delegated credentials, but does not perform the Subject Delegation token request itself. In particular, engine code does not implement mTLS for the token-vending step. That is handled by Processing Framework platform components and IAM configuration.

Procedure Execution Sequence

The high-level execution flow is shown below.

Delegated Authorization Procedure Execution Sequence

Configure an Engine for Delegated Authorization

To run an operation with Delegated Authorization, configure identity data in both deployment and operation resources.

1. Create a Delegate Identity application

Create a dedicated TID application that represents your engine software as the Delegate Identity.

2. Grant required IAM permissions

Work with the Trimble Identity/IAM team to grant the following permissions on the Delegate Identity application:

  • accessTokens:delegatedAzpAccess — granted to the PF Host Identity on the Delegate Identity. This allows Processing Framework to obtain access tokens with the Delegate Identity as the authorized party.
  • applications:delegate — granted to the deployer identity on the Delegate Identity. This authorizes the deployer to register the Delegate Identity with Processing Framework when creating the deployment.

Integrators can use this request form to begin the permission enablement process for Delegated Authorization:

For background and IAM/platform setup context, see:

3. Create the deployment with delegate identity

Set the Delegate Identity on the deployment resource.

{
"deployment": {
"identifier": "my-engine-deployment",
"delegated_authorization": {
"delegate_identity": "<delegate-identity-client-id>"
}
}
}

You can find the value for deployment.json here.

When the deployment is created, Processing Framework validates that the deployer identity holds the applications:delegate permission on the specified Delegate Identity. If this check fails, the deployment will be rejected.

4. Create operations with target identities

Set Target Identities on each operation that requires Delegated Authorization.

{
"operation": {
"identifier": "my_operation",
"delegated_authorization": {
"target_identities": [
"<target-identity-scope-1>",
"<target-identity-scope-2>"
]
}
}
}

You can find the value for operation.json here.

Use Delegated Authorization in an Operation

At runtime, the operation receives a delegated_authorization object in task input. The object contains file paths for:

  • access_token
  • dpop_public
  • dpop_private

The engine reads these files during operation execution.

The credential files are stored in a volatile in-memory (tmpfs) volume shared between the Orchestrator and Engine containers. They are never written to persistent storage and are only available during the operation’s execution lifetime.

Engine Responsibility Flow

The following diagram focuses specifically on what engine code is responsible for during Delegated Authorization handling.

Delegated Authorization Engine Responsibilities

Task Input Shape

{
"delegated_authorization": {
"access_token": "/secrets/delegated_auth/access_token",
"dpop_public": "/secrets/delegated_auth/dpop_public.pem",
"dpop_private": "/secrets/delegated_auth/dpop_private.pem"
}
}

Required Request Headers

When calling a DPoP-protected API with the delegated token, include both headers:

  • Authorization: DPoP <access_token>
  • DPoP: <dpop-jwt>

The DPoP JWT is generated from the DPoP private key and includes request-bound claims (htm, htu) plus the access-token hash claim (ath).

htu must match the exact URL being requested, including query parameters.

A DPoP proof is accepted for only 5 minutes after its iat timestamp. APICloud rejects proofs outside that window.

Python Example (Reference Engine Pattern)

This example shows a complete request flow that an engine can use to call a DPoP-protected API with the delegated credentials provided in task input.

This example uses an EC P-256 key. RSA keys are also supported but require a different JWK shape and signing algorithm.

At a high level, the code performs four steps:

  1. Reads delegated auth file paths from task_input["delegated_authorization"].
  2. Loads the access token and DPoP keypair from those files.
  3. Creates a request-bound DPoP proof (htm, htu, ath) with a fresh iat.
  4. Sends the API request with both required headers (Authorization and DPoP).
import base64
import datetime
import hashlib
import math
import uuid
from pathlib import Path
import jwt
import requests
from cryptography.hazmat.primitives import serialization
def call_pf_executions(task_input: dict) -> dict:
# Call the PF executions endpoint to retrieve executions visible to the delegated subject.
base_uri = "https://cloud.api.trimble.com/Processing/api/1/api"
# DPoP claims must match the exact URL (including query params) and HTTP method.
request_url = f"{base_uri}/executions"
method = "GET"
query_params = {"per_page": 5}
# Build the exact final URL (with query string) used for both the request and the htu claim.
prepared_request = requests.Request(method, request_url, params=query_params).prepare()
url = prepared_request.url
# Read delegated secret file paths from task input.
auth_cfg = task_input["delegated_authorization"]
access_token_path = Path(auth_cfg["access_token"])
dpop_public_path = Path(auth_cfg["dpop_public"])
dpop_private_path = Path(auth_cfg["dpop_private"])
# Load delegated auth materials from files.
access_token = access_token_path.read_text(encoding="utf-8").strip()
dpop_public = dpop_public_path.read_bytes().strip()
dpop_private = dpop_private_path.read_bytes().strip()
# Convert the DPoP public key into JWK fields for the JWT header.
# This example assumes EC P-256 material; RSA keys use a different JWK shape and signing alg.
public_key = serialization.load_pem_public_key(dpop_public)
x = public_key.public_numbers().x.to_bytes(math.ceil(public_key.key_size / 8), "big")
y = public_key.public_numbers().y.to_bytes(math.ceil(public_key.key_size / 8), "big")
b64_x = base64.urlsafe_b64encode(x).decode("ascii").rstrip("=")
b64_y = base64.urlsafe_b64encode(y).decode("ascii").rstrip("=")
# Build freshness and access-token binding claims.
now = int(datetime.datetime.now(tz=datetime.UTC).timestamp())
ath = base64.urlsafe_b64encode(hashlib.sha256(access_token.encode("utf-8")).digest()).decode("ascii").rstrip("=")
dpop_headers = {
"typ": "dpop+jwt",
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": b64_x,
"y": b64_y,
},
}
dpop_payload = {
"jti": str(uuid.uuid4()),
"iat": now,
"htm": method,
"htu": url,
"ath": ath,
}
# Sign a fresh DPoP proof right before the API call.
dpop_proof = jwt.encode(dpop_payload, dpop_private, algorithm="ES256", headers=dpop_headers)
# Send the request with both required delegated-auth headers.
headers = {
"Authorization": f"DPoP {access_token}",
"DPoP": dpop_proof,
"Content-Type": "application/json",
}
response = requests.get(url, headers=headers, timeout=30)
response.raise_for_status()
return response.json()

In this pattern, DPoP proof creation occurs immediately before the request so the proof remains within the 5-minute validity window.

Engine Developer Checklist

  • Configure Delegated Auth on both resources: set delegate_identity on deployment and target_identities on each operation that needs delegated access.
  • Treat delegated_authorization values as file paths: read them from disk at runtime; do not expect raw token or key values in task input.
  • Generate a DPoP proof per request: create a new proof immediately before each outbound call.
  • Match htu exactly: include the full URL with query parameters exactly as sent on the wire.
  • Avoid credential logging: never log full access tokens, DPoP proofs, or private key material.
  • Handle authorization failures explicitly: return clear errors when delegated auth inputs are missing or when target APIs reject authorization.

Where the Identities Appear in This Code

  • Subject Identity: represented by the delegated access_token loaded from the access_token file path. If you decode that JWT, subject-related claims identify the execution owner context.
  • Host Identity: used by Processing Framework earlier, before your operation code runs, to obtain the delegated token and write the auth files.
  • Delegate Identity: configured on the deployment as delegated_authorization.delegate_identity and reflected in the delegated token that the engine receives.
  • Target Identities: configured on the operation as delegated_authorization.target_identities; in request code, this corresponds to which protected API you call (url) and whether the delegated token audience or scope is valid for that target.

Token Refresh Behavior

Processing Framework orchestration manages Delegated Authorization credentials while the operation runs.

  • Credentials are refreshed by platform orchestration before expiry as needed for long-running tasks.
  • For long-running operations with multiple outbound calls, re-read the token and key files before each call, or at minimum when retrying after an authorization failure, so refreshed credentials are picked up.
  • If credentials are refreshed between file reads, one request may be built with mixed old/new materials and fail authorization. Treat this as retryable: reload all delegated auth files, regenerate DPoP, and retry with a small bounded retry policy.

Secure Logging Guidance

  • Do log: the endpoint URL, HTTP method, status code, and high-level error context.
  • Do not log: full Authorization header, full DPoP header, raw access token, or private key content.
  • If debugging claims: log only selected non-sensitive claim fields, such as htm, htu, and iat, and avoid dumping full JWTs.

Troubleshooting

  • delegated_authorization missing from task input: the operation is likely not configured for Delegated Authorization, or execution metadata is incomplete. Verify that the operation resource has delegated_authorization.target_identities configured.
  • Deployment rejected at creation: verify that the deployer identity has been granted the applications:delegate permission on the Delegate Identity. Submit a ticket to the TID/IAM team if the permission has not been provisioned.
  • DPoP proof rejected by target API: verify that the DPoP header is present, generated for the exact request URL and HTTP method, and created in the last 5 minutes (iat freshness window). This is an engine-side issue resolvable by the engine developer.
  • Authorization failure from target API: verify that operation target_identities includes the required audience or scope for the called service. This is resolvable by the engine owner.
  • Token request or refresh failures — PF system-level: if token requests fail across all operations regardless of Delegate Identity, this indicates a PF platform authorization issue. This cannot be resolved by engine owners and requires intervention by PF system administrators.
  • Token request or refresh failures — Engine-specific: if token requests fail only for operations using a specific Delegate Identity, the accessTokens:delegatedAzpAccess permission may have been revoked on that Delegate Identity. Only the engine owner can resolve this by working with the TID/IAM team.
  • Token request or refresh failures — TID outage: if all token requests fail regardless of Delegate Identity, the TID service may be unavailable. This can only be resolved by TID operators.