azure-devops-wif-retrieve-oidc-token

A WIF service connection lets a pipeline job request a short-lived Azure DevOps OIDC token that is then exchanged for an ARM access token; any code in the job can call the OIDC endpoint.

derived azure emits CanRetrieveToken

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'devops_oidc': None} ExternalIdentityMapsTo {'arm_identity': None}

where

node_type(?devops_oidc) in [ExternalIdentity, FederatedIdentity] node_type(?arm_identity) in [MachineIdentity, ApplicationIdentity] ?devops_oidc.issuer matches 'https://vstoken.dev.azure.com/'

emit

source typeFederatedIdentity
target typeMachineIdentity
source?devops_oidc
target?arm_identity
permissionsPOST https://vstoken.dev.azure.com/{org_id}/_apis/distributedtask/hubs/build/plans/{plan_id}/jobs/{job_id}/oidctoken
conditionstrust_condition
state logicACTIVE when the WIF trust is ACTIVE (ExternalIdentityMapsTo trust condition is correctly pinned). CONDITIONAL(trust_condition) when the trust is CONDITIONAL (subject/issuer unpinned or wildcard). The resulting token is short-lived (typically 10 minutes) and NOT a stored secret — it cannot be exfiltrated for long-term use, but it IS usable immediately to make ARM API calls as ?arm_identity during the job run.

Narrative

Pipeline jobs using WIF service connection {arm_identity.name} call the DevOps OIDC endpoint to obtain a short-lived JWT that ARM exchanges for an access token; any code in the job can trigger this exchange and act as {arm_identity.name} for the duration of the run.

Raw rule rules/derived/azure/devops.yaml

id: azure-devops-wif-retrieve-oidc-token
emits: CanRetrieveToken
description: A WIF service connection lets a pipeline job request a short-lived Azure DevOps OIDC token
  that is then exchanged for an ARM access token; any code in the job can call the OIDC endpoint.
applies_to:
- azure
match:
- - devops_oidc: null
  - ExternalIdentityMapsTo
  - arm_identity: null
where:
- node_type(?devops_oidc) in [ExternalIdentity, FederatedIdentity]
- node_type(?arm_identity) in [MachineIdentity, ApplicationIdentity]
- ?devops_oidc.issuer matches 'https://vstoken.dev.azure.com/'
emit:
  source_type: FederatedIdentity
  target_type: MachineIdentity
  source: ?devops_oidc
  target: ?arm_identity
  permissions:
  - POST https://vstoken.dev.azure.com/{org_id}/_apis/distributedtask/hubs/build/plans/{plan_id}/jobs/{job_id}/oidctoken
  conditions:
  - trust_condition
  state_logic: "ACTIVE when the WIF trust is ACTIVE (ExternalIdentityMapsTo trust condition is correctly\
    \ pinned). CONDITIONAL(trust_condition) when the trust is CONDITIONAL (subject/issuer unpinned or\
    \ wildcard). The resulting token is short-lived (typically 10 minutes) and NOT a stored secret \u2014\
    \ it cannot be exfiltrated for long-term use, but it IS usable immediately to make ARM API calls as\
    \ ?arm_identity during the job run."
  confidence: 0.9
  derived_from:
  - ?devops_oidc ExternalIdentityMapsTo ?arm_identity
  false_positive_note: "The OIDC token is short-lived and audience-restricted (aud=api://AzureADTokenExchange).\
    \ It is NOT a client secret \u2014 it cannot be used directly without the token exchange and is scoped\
    \ to the ARM trust. However, within a pipeline run, it DOES yield full ARM access as the service principal\
    \ for the duration of the job. A pipeline step can also call the OIDC endpoint manually (idToken task\
    \ output) and pass the JWT to other azure-cli / custom tooling that performs the ARM token exchange."
  narrative: Pipeline jobs using WIF service connection {arm_identity.name} call the DevOps OIDC endpoint
    to obtain a short-lived JWT that ARM exchanges for an access token; any code in the job can trigger
    this exchange and act as {arm_identity.name} for the duration of the run.
move · open · esc close