Early access. This feature is in early access, which means it’s undergoing ongoing testing and development while we gather feedback, validate functionality, and improve outputs. Contact the ConductorOne Support team if you’d like to try it out or share feedback.
Any platform that issues OIDC tokens can be used with workload federation. This guide covers the generic token exchange flow for providers that don’t have a built-in preset.
Provider requirements
Your OIDC provider must:
- Serve a
/.well-known/openid-configuration document at the issuer URL
- Have a publicly accessible JWKS (JSON Web Key Set) endpoint
- Issue JWTs with standard claims:
iss, aud, exp, iat
Prerequisites
- A service principal with a Custom OIDC federation trust. See set up federation if you haven’t created one yet. Use the Custom OIDC preset and enter your provider’s issuer URL.
- The trust’s client ID (for example
quiet-bear-88456@yourcompany.conductor.one/wfe)
Exchange the token
Send the external OIDC JWT to ConductorOne’s token exchange endpoint:
curl -s -X POST "https://yourcompany.conductor.one/auth/v1/token" \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token=$YOUR_JWT" \
-d "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \
-d "client_id=quiet-bear-88456@yourcompany.conductor.one/wfe"
Response on success:
{
"access_token": "eyJhbGciOiJFZERTQSIs...",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 1800
}
Use the access_token in an Authorization: Bearer header for API calls.
Token requirements
The JWT must satisfy these requirements:
| Requirement | Detail |
|---|
Issuer (iss) | Must match the provider’s issuer URL exactly |
Audience (aud) | Must contain your ConductorOne tenant domain (for example yourcompany.conductor.one) |
Expiration (exp) | Must not be expired |
Issued at (iat) | Must be within the last 10 minutes |
| Signature | Must be verifiable via the provider’s JWKS endpoint |
Writing CEL expressions
The CEL expression evaluates against the JWT’s decoded claims. Write an expression that validates the sub claim and any additional claims for defense-in-depth scoping:
claims.sub == "expected-subject" && claims.custom_claim == "expected-value"
Tips for writing expressions
- Always validate the
sub claim or an equivalent unique identifier
- Use additional claims for defense-in-depth: organization, project, environment
- CEL string functions are available:
contains(), startsWith(), endsWith(), matches(), size()
- For namespaced claims (like AWS), use bracket notation:
claims["https://example.com/"].field
- Expressions are limited to 1,024 bytes
Use the Test CEL tool at Settings > Workload Federation to validate your expressions against sample claims before deploying.
Once you have the access token, set the CONDUCTORONE_ACCESS_TOKEN environment variable and all ConductorOne tools pick it up automatically:
export CONDUCTORONE_ACCESS_TOKEN=$(curl -s -X POST ... | jq -r '.access_token')
# Use with any tool
cone whoami
terraform apply
Alternatively, set CONDUCTORONE_OIDC_TOKEN with the raw JWT and CONDUCTORONE_CLIENT_ID with the trust client ID. The Go SDK, Cone CLI, and Terraform provider all support this pattern and handle the token exchange internally.
Security best practices for custom OIDC providers
Always validate the sub claim and use additional claims for defense-in-depth scoping. A CEL expression that only checks one claim may be too permissive if the provider issues tokens with broad subjects.