Documentation Index
Fetch the complete documentation index at: https://ona.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
Ona environments can access Azure resources using workload identity federation. You configure a federated credential on an Azure app registration or user-assigned managed identity that trusts Ona’s OIDC tokens. Environments exchange their JWT for an Azure access token without storing any secrets.
Azure-specific constraints
Azure Entra ID federated credentials have two limits that shape how you design the trust:
- Exact match on
subject — wildcards are not supported. You need one federated credential per distinct sub value you want to authorize.
- Maximum 20 federated credentials per app registration or managed identity.
Reference: Microsoft — Federated identity credential considerations.
Plan the sub claim composition before creating credentials so the number of subs you need stays comfortably under 20.
Prerequisites
- V3 tokens enabled on the OIDC Token Configuration page. See Enable V3 tokens.
- Azure CLI installed in your environment.
- An Azure app registration or user-assigned managed identity. The examples below use an app registration; the same federated-credential and CLI flow works for managed identities.
How it works
- Ona issues a JWT signed with RS256, containing identity claims about the environment, user, or service account.
- Azure validates the token against Ona’s OIDC discovery endpoint and JWKS.
- If
iss, aud, and sub match the federated credential, Azure issues a service principal access token.
- The Azure CLI or any Azure SDK uses that access token to call Azure APIs.
Step 1: Create an app registration or managed identity
Use either an app registration or a user-assigned managed identity. Note the client ID and tenant ID — both are required for the login flow.
App registration:
az ad app create --display-name ona-environment
# Note the appId returned — this is your --client-id.
User-assigned managed identity:
az identity create \
--name ona-environment \
--resource-group <RESOURCE_GROUP> \
--location <LOCATION>
Step 2: Inspect your Ona token
Run this in an Ona environment to see what sub value Azure will need to match:
ona idp token --audience api://AzureADTokenExchange --decode
Record the sub value verbatim — Azure requires an exact-match string.
Step 3: Add a federated identity credential
Add a federated identity credential that trusts Ona’s OIDC tokens for the recorded sub.
For an app registration:
az ad app federated-credential create \
--id <CLIENT_ID> \
--parameters '{
"name": "ona-project-access",
"issuer": "https://app.gitpod.io",
"subject": "organization_id:<ORG_ID>:project_id:<PROJECT_ID>",
"audiences": ["api://AzureADTokenExchange"]
}'
For a user-assigned managed identity:
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
| Field | Value |
|---|
issuer | https://app.gitpod.io |
subject | The exact sub value from your Ona token |
audiences | api://AzureADTokenExchange |
The subject must match the sub claim exactly, including case and order of key/value pairs. New federated credentials take a few minutes to propagate; token exchange may fail with AADSTS70021 during this window.
Step 4: Assign Azure RBAC roles
Grant the app registration or managed identity access to the Azure resources it needs:
az role assignment create \
--assignee <CLIENT_ID> \
--role "Key Vault Secrets User" \
--scope /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP>/providers/Microsoft.KeyVault/vaults/<VAULT_NAME>
Step 5: Authenticate from an environment
Use ona idp login azure to exchange the OIDC token and configure the Azure CLI:
ona idp login azure \
--client-id <CLIENT_ID> \
--tenant-id <TENANT_ID>
To set an active subscription as part of the login:
ona idp login azure \
--client-id <CLIENT_ID> \
--tenant-id <TENANT_ID> \
--subscription-id <SUBSCRIPTION_ID>
Subsequent az calls use the federated credentials:
az keyvault secret show --vault-name my-vault --name my-secret
Flags
| Flag | Required | Env var | Notes |
|---|
--client-id | yes | IDP_AZURE_CLIENT_ID | App registration or managed identity client ID |
--tenant-id | yes | IDP_AZURE_TENANT_ID | Directory (tenant) ID |
--subscription-id | no | IDP_AZURE_SUBSCRIPTION_ID | If set, becomes the active subscription after login |
--audience | no | — | Defaults to api://AzureADTokenExchange |
Automate on environment startup
Add the login to your automations:
# automations.yaml
tasks:
azure-login:
name: Azure Login
command: ona idp login azure --client-id <CLIENT_ID> --tenant-id <TENANT_ID>
triggeredBy:
- postDevcontainerStart
Manual exchange (without ona idp login azure)
Use this when integrating with non-CLI tooling or other Azure SDKs:
TOKEN=$(ona idp token --audience api://AzureADTokenExchange)
az login --service-principal \
--username <CLIENT_ID> \
--tenant <TENANT_ID> \
--federated-token "$TOKEN"
Or call Microsoft’s token endpoint directly:
TOKEN=$(ona idp token --audience api://AzureADTokenExchange)
curl -X POST "https://login.microsoftonline.com/<TENANT_ID>/oauth2/v2.0/token" \
-d "client_id=<CLIENT_ID>" \
-d "scope=https://management.azure.com/.default" \
-d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
-d "client_assertion=$TOKEN" \
-d "grant_type=client_credentials"
For programmatic access in Python, the Azure Identity SDK accepts a token-fetching callable via ClientAssertionCredential:
import subprocess
from azure.identity import ClientAssertionCredential
def get_ona_token() -> str:
return subprocess.run(
["ona", "idp", "token", "--audience", "api://AzureADTokenExchange"],
capture_output=True, text=True, check=True,
).stdout.strip()
credential = ClientAssertionCredential(
tenant_id="<TENANT_ID>",
client_id="<CLIENT_ID>",
func=get_ona_token,
)
Sub claim strategy
Azure’s exact-match constraint means you should pick the coarsest sub that still expresses your trust boundary. Each unique sub you want to authorize costs one of your 20 federated credentials per identity.
The default V3 environment 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.
- Without project:
organization_id:<orgID> — matches every environment in the org.
Pick one of the patterns below.
Per-project (recommended)
sub = organization_id + project_id (default — no configuration needed).
organization_id:<orgID>:project_id:<projID>
| Federated credentials | Use case |
|---|
| 1 per project | All users in a project assume the same Azure role. Simplest setup, stays well within the 20-credential limit. |
Per-user, per-project
Add creator_email (or creator_id) to the extra sub fields on the OIDC Token Configuration page. The sub becomes:
organization_id:<orgID>:project_id:<projID>:creator_email:user@example.com
| Federated credentials | Use case |
|---|
| 1 per user × project | Different users get different Azure permissions within the same project. Watch the 20-credential limit. |
Per-user, all projects
Add user_id (for user tokens) or creator_id (for environment tokens) to the extra sub fields.
organization_id:<orgID>:user_id:<userID>
| Federated credentials | Use case |
|---|
| 1 per user | User-level Azure role assumption across all projects. |
Per-repository
Add environment_initializers.git.remote_uri to the extra sub fields. Useful when you want the trust to follow a repository regardless of which Ona project wraps it.
organization_id:<orgID>:project_id:<projID>:environment_initializers.git.remote_uri:https%3A//github.com/org/repo.git
Colons in values are URL-encoded as %3A in the sub claim.
See Customizing the sub claim for the full list of fields you can add.
Limits
- Maximum 20 federated credentials per app registration or managed identity. If you need more, create additional identities with different role assignments.
- The
subject field has a 600-character limit. Long sub compositions (many extra fields with UUIDs) can approach this.
Token claims available to your application
Even though Azure only matches against sub, the rest of the JWT body is available to your application after login. Use top-level claims for downstream authorization (audit logs, tenant routing, group-based checks).
| Claim | Description |
|---|
organization_id | Ona organization UUID |
project_id | Ona project UUID, when the environment is project-scoped |
environment_id | Environment UUID (changes per environment) |
runner_id | Runner UUID |
creator_id | Ona user UUID of the creator |
creator_principal | user or service_account |
creator_email | Email of the user who created the environment |
creator_name | Display name of the creator |
creator_idp | URL of the creator’s identity provider |
creator_idp_claims | Non-sensitive claims forwarded from the SSO provider |
environment_initializers | Git remote and context URL the environment was created from |
See Token structure for the full schema and per-principal variants.
SSO claims forwarding
creator_idp_claims forwards string-valued claims from your SSO provider (for example, Entra ID groups or preferred_username). To enable additional claims:
- Open Organization Settings → Login and Security in Ona.
- Add the scopes your IdP returns on login (for example,
profile, email, groups).
- Configure your IdP to release those claims to Ona.
You can include any string-valued SSO claim in the sub using the creator_idp_claims.<key> syntax.
A note on the creator_ prefix
In Ona, environments have their own identity — they are machines, not users. The creator_ prefix makes explicit that those claims describe the user who created the environment, not the environment itself. The distinction matters when environments are launched by automations or shared across users.
V2 tokens
V2 tokens also work with Azure federated credentials. The V2 sub uses a path-based format (for example, org:<orgID>/prj:<projectID>/env:<envID>). Set the federated credential’s subject to the exact V2 sub value.
V2 tokens carry fewer claims (org, gsub, and standard JWT fields), so trust is limited to matching sub. New integrations should use V3. See V2 tokens for the full V2 reference.
Troubleshooting
AADSTS70021: No matching federated identity record found
- The
sub claim in your Ona token does not match any federated credential’s subject.
- Decode your token:
ona idp token --audience api://AzureADTokenExchange --decode.
- Compare it to the
subject you registered. The match must be exact, including case.
- Newly created federated credentials take a few minutes to propagate. Retry after the wait.
AADSTS700024: Client assertion is not within its valid time range
- The token has expired. Ona OIDC tokens are short-lived. Re-run
ona idp login azure.
AADSTS700016: Application not found in the directory
--client-id must be the client ID (appId) of the app registration or managed identity, not its object ID.
missing --client-id / missing --tenant-id
- Pass the flags or set
IDP_AZURE_CLIENT_ID / IDP_AZURE_TENANT_ID in the environment.
Further reading