Skip to main content
Requires Enterprise plan. Contact sales for access.
Ona environments can access Azure resources using workload identity federation. You create a user-assigned managed identity in Azure with a federated credential that trusts Ona’s OIDC tokens. The environment exchanges its JWT for an Azure access token without any stored secrets.

Prerequisites

How it works

  1. Ona issues a JWT with claims about the environment, user, and organization.
  2. Azure validates the token against Ona’s OIDC discovery endpoint.
  3. If the token’s issuer, subject, and audience match the federated credential, Azure issues an access token.
  4. The environment uses the access token to call Azure APIs.
Azure requires an exact match on the sub claim. Wildcards are not supported. This means you need one federated credential per distinct sub value you want to authorize. See Sub claim strategy for Azure for recommendations.

Step 1: Create a user-assigned managed identity

Create a user-assigned managed identity in Azure:
az identity create \
  --name ona-environment \
  --resource-group <RESOURCE_GROUP> \
  --location <LOCATION>
Note the clientId and tenantId from the output. You need these in Step 3.

Step 2: Add a federated identity credential

Add a federated identity credential that trusts Ona’s OIDC tokens:
az identity federated-credential create \
  --name ona-project-access \
  --identity-name ona-environment \
  --resource-group <RESOURCE_GROUP> \
  --issuer https://app.gitpod.io \
  --subject "organization_id:<ORG_ID>:project_id:<PROJECT_ID>" \
  --audiences api://AzureADTokenExchange
Replace <ORG_ID> and <PROJECT_ID> with your Ona organization and project IDs. Key parameters:
ParameterValue
--issuerhttps://app.gitpod.io
--subjectThe exact V3 sub claim value from your Ona token
--audiencesapi://AzureADTokenExchange (Azure’s standard audience for federated credentials)
The subject must exactly match the sub claim in the Ona token. Decode your token to verify: ona idp token --audience api://AzureADTokenExchange --decode

Multiple federated credentials

You can add up to 20 federated credentials per managed identity. Each credential matches a different sub value. For example, to allow two projects:
# Project A
az identity federated-credential create \
  --name project-a \
  --identity-name ona-environment \
  --resource-group <RESOURCE_GROUP> \
  --issuer https://app.gitpod.io \
  --subject "organization_id:<ORG_ID>:project_id:<PROJECT_A_ID>" \
  --audiences api://AzureADTokenExchange

# Project B
az identity federated-credential create \
  --name project-b \
  --identity-name ona-environment \
  --resource-group <RESOURCE_GROUP> \
  --issuer https://app.gitpod.io \
  --subject "organization_id:<ORG_ID>:project_id:<PROJECT_B_ID>" \
  --audiences api://AzureADTokenExchange

Step 3: Assign Azure RBAC roles

Grant the managed identity access to the Azure resources it needs:
az role assignment create \
  --assignee <MANAGED_IDENTITY_CLIENT_ID> \
  --role "Key Vault Secrets User" \
  --scope /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP>/providers/Microsoft.KeyVault/vaults/<VAULT_NAME>

Step 4: Authenticate from an environment

Get an Ona OIDC token and exchange it for Azure credentials using the Azure CLI:
# Get the Ona OIDC token
TOKEN=$(ona idp token --audience api://AzureADTokenExchange)

# Exchange for Azure credentials
az login --service-principal \
  --username <MANAGED_IDENTITY_CLIENT_ID> \
  --tenant <TENANT_ID> \
  --federated-token "$TOKEN"
After login, use Azure resources normally:
az keyvault secret show --vault-name my-vault --name my-secret

Automate on environment startup

Add the login to your automations:
# automations.yaml
tasks:
  azure-login:
    name: Azure Login
    command: |
      TOKEN=$(ona idp token --audience api://AzureADTokenExchange)
      az login --service-principal \
        --username <MANAGED_IDENTITY_CLIENT_ID> \
        --tenant <TENANT_ID> \
        --federated-token "$TOKEN"
    triggeredBy:
      - postDevcontainerStart

Using the Azure SDK

For programmatic access, use the Azure Identity SDK’s ClientAssertionCredential:
from azure.identity import ClientAssertionCredential
import subprocess

def get_ona_token():
    result = subprocess.run(
        ["ona", "idp", "token", "--audience", "api://AzureADTokenExchange"],
        capture_output=True, text=True, check=True
    )
    return result.stdout.strip()

credential = ClientAssertionCredential(
    tenant_id="<TENANT_ID>",
    client_id="<MANAGED_IDENTITY_CLIENT_ID>",
    func=get_ona_token
)

Sub claim strategy for Azure

Azure’s exact-match requirement on the sub claim means you need a predictable, stable sub value. The default V3 sub depends on whether the environment belongs to a project:
  • With project: organization_id:<orgID>:project_id:<projID>. Stable across all environments in the project. One federated credential covers all environments created from that project.
  • Without project: organization_id:<orgID>. Stable but broad. Any environment in the org matches.
Environments created from an Ona project produce a project-scoped sub by default. This is the best fit for Azure because:
  • The sub is stable across environment restarts and recreations
  • One federated credential per project is manageable
  • Access is scoped to a specific codebase
organization_id:<orgID>:project_id:<projID>

Alternative: user-scoped sub

Add user_id to the extra sub fields on the OIDC Token Configuration page. Each user gets a stable sub:
organization_id:<orgID>:user_id:<userID>
Create one federated credential per user who needs Azure access.

Environments without a project

Environments created without a project produce a sub of organization_id:<orgID>. A single federated credential then grants access to all environments in the org. If this is too broad, add extra sub fields (e.g., user_id or environment_id) to narrow the scope.

Limits

  • Maximum 20 federated credentials per managed identity.
  • If you need more, create additional managed identities with different role assignments.
  • The subject field has a 600-character limit.

Using V2 tokens with Azure

V2 tokens also work with Azure federated credentials. The V2 sub claim uses a path-based format (e.g., org:<orgID>/prj:<projectID>/env:<envID>). Set the federated credential’s --subject to the exact V2 sub value. V2 tokens include fewer claims (org, gsub, and standard JWT fields only), so Azure trust is limited to matching the sub claim. See the OIDC overview V2 section for the full V2 sub format reference.

Troubleshooting

“AADSTS70021: No matching federated identity record found”
  • The sub claim in your Ona token does not match any federated credential’s subject value.
  • Decode your token: ona idp token --audience api://AzureADTokenExchange --decode
  • Compare the sub value with the --subject you configured. They must match exactly, including casing.
“AADSTS700024: Client assertion is not within its valid time range”
  • The token has expired. Ona tokens are valid for 1 hour. Re-run the login command.
“AADSTS700016: Application not found in the directory”
  • Verify the --username (client ID) matches the managed identity’s client ID, not its object ID.

Further reading