Azure DevOps
Proxy for the Azure DevOps Services REST API via the managed gateway.
What this connector does
Gives agents gated access to an Azure DevOps organization for:
- Listing projects.
- Listing Git repositories in a project.
- Querying and reading work items.
All operations are gated by PBAC policy. Every call flows: agent → gateway → /introspect (with this connector's resource_type) → OPA decision → Azure DevOps API → response.
The connector supports two upstream-auth modes:
-
entra_refresh(default) — PolicyArc uses the refresh token captured at the user's Microsoft sign-in to mint a fresh, correctly-audienced token per Microsoft service at call time. One sign-in works across any combination of Microsoft connectors — Graph (Mail, OneDrive, Teams) and Azure DevOps together. The minted token is cached per(user, resource)and re-minted on expiry. Azure DevOps enforces its own organization membership and permissions, and its audit trail shows the real person making each call. -
idp_passthrough— forwards the user's Microsoft sign-in token directly to Azure DevOps, with no exchange. That sign-in token carries a single audience, so passthrough works for only one Microsoft resource — either Azure DevOps or Graph (Mail/OneDrive/Teams), not both; don't mix passthrough across Azure DevOps and Graph. Choose this only for strict tenants that disable refresh-token issuance (nooffline_access), or that don't want a long-lived refresh token warehoused by PolicyArc. Otherwise preferentra_refresh.
Prerequisites
entra_refresh mode (default)
- Entra app registration — a confidential app used for login. It needs:
- a client secret (stored in PolicyArc's secret store),
- the federation redirect URI (
/oauth2/callback), - a delegated Azure DevOps API permission
(resource
499b84ac-1321-427f-aa17-267ca6975798,user_impersonation), with admin consent granted, offline_accessin the login scopes so a refresh token is captured.
- PolicyArc Entra IdP registered with provider key
microsoft, configured with the app'sclient_id/client_secretand the tenant authorize/token endpoints (https://login.microsoftonline.com/<tenant>/oauth2/v2.0/{authorize,token}). Login scopes must includeoffline_accessso the gateway captures a refresh token at sign-in. See the Entra ID IdP guide. - Azure DevOps organization connected to the same Entra tenant (Org settings → Microsoft Entra), and the user must be a member of the org with permission for the operations you want to gate.
PBAC_GATEWAY_CREDENTIAL_KEYset on the AS (encrypts the cached tokens).
idp_passthrough mode (no token exchange)
Passthrough forwards the user's sign-in token directly, so there is no token exchange:
- Entra app registration — a confidential app used only as a login IdP.
It needs a client secret, the
/oauth2/callbackredirect URI, and the delegated Azure DevOps permission (resource499b84ac-1321-427f-aa17-267ca6975798,user_impersonation) with admin consent granted. - PolicyArc Entra IdP (provider key
microsoft) — login scopes must beopenid profile email 499b84ac-1321-427f-aa17-267ca6975798/user_impersonation, so the captured token is audienced for Azure DevOps.offline_accessis optional in passthrough mode — add it only to have the forwarded token refreshed silently on expiry; omit it for tenants that disable refresh-token issuance (users re-authenticate when the token lapses). Installing the connector auto-mergesuser_impersonationinto the IdP scope list (viaIdpScopeMerger); do not also add…/.default— Entra rejects.defaultcombined with a resource-specific scope at sign-in (AADSTS70011). - Azure DevOps organization connected to the same Entra tenant and the
user a member of the org (same as
entra_refresh). PBAC_GATEWAY_CREDENTIAL_KEYset on the AS (encrypts the captured login token).
Install
The install wizard has an Authentication picker (entra_refresh / idp_passthrough).
The default is entra_refresh; choose idp_passthrough only if your deployment uses
a single Azure-DevOps-audienced token for every connector and you have no Graph
connectors configured.
1. Register the Entra IdP
Register the Entra IdP in PolicyArc (provider key microsoft) using the app's
client_id/client_secret and tenant endpoints as described in
Prerequisites. Confirm a user can log in to PolicyArc through
it before installing the connector.
2. Install the connector
From the admin UI:
- Resources → Add → Azure DevOps
- Fill in the wizard:
- Authentication — select
entra_refresh(default) oridp_passthrough. - Azure DevOps organization URL —
https://dev.azure.com/<your-org>. - IdP — select the Entra IdP registered in step 1.
- Authentication — select
- Install.
The admin UI reads setup_fields directly from this connector's manifest — same
fields shown below in the Setup fields reference.
3. Verify
Log in to PolicyArc through the Entra IdP first (so the gateway has a captured refresh token), then request a token with an Azure DevOps scope and call through the gateway:
curl "https://<your-as>/gateway/azure-devops/_apis/projects?api-version=7.1" \
-H "Authorization: Bearer <token>"
A successful 200 with the project list confirms the full chain end-to-end:
entra_refresh— refresh token captured → per-resource token minted → introspect allowed → gateway proxied → Azure DevOps responded.idp_passthrough— no exchange: the captured Azure-DevOps-audienced login token is forwarded verbatim → introspect allowed → gateway proxied → Azure DevOps responded. (If the captured token is audienced for anything else, ADO returns401; re-check the IdP login scopes per Prerequisites.)
What can go wrong
| Symptom | Cause | Fix |
|---|---|---|
502 "no stored IdP token" | The caller never logged in through the microsoft IdP (or used client_credentials, which has no user context) | Re-authenticate through the Entra IdP so the gateway can capture a refresh token. |
502 "admin consent / API permission missing" | The Entra app lacks admin-consented delegated Azure DevOps permission | Grant admin consent for the user_impersonation permission on resource 499b84ac-… in the app registration. |
502 "refresh failed or token expired" | The refresh token is expired or revoked | Re-authenticate through the Entra IdP; ensure offline_access is in the login scopes. |
401/203 from Azure DevOps | The org is not connected to the same Entra tenant, or the user isn't a member | Connect the org to the tenant (Org settings → Microsoft Entra) and add the user. |
404 at the gateway | Wrong path | Gateway path is /gateway/<resource-name>/<ADO REST path> — ADO paths start with /_apis/... or /{project}/_apis/.... |
| ADO returns an HTML/redirect page instead of JSON | Missing api-version query parameter | Append ?api-version=7.1 (or the version your API requires) to ADO REST calls. |
403 from the gateway | Agent lacks the required scope | Request a token with azuredevops:read. |
Scopes
| Scope | Meaning | Routes |
|---|---|---|
azuredevops:read | Read-only operations | list projects, list repositories, query/get work items |
Policy authors can write rules targeting input.resource.type == "urn:connector:identos:azure-devops". Per-project rules use the resource_id (azuredevops://{project}/...).
Setup fields
| Field | Required | Default | Purpose |
|---|---|---|---|
base_url | yes | — | Azure DevOps organization URL, e.g. https://dev.azure.com/your-org. |
The Entra IdP (provider key microsoft) supplies the client credentials and token
endpoint — it is selected at install time, not entered as a secret here.
MCP tools
When the managed gateway's MCP aggregator is enabled, this connector exposes the following tools to agents:
| Tool | Scope | Description |
|---|---|---|
list_projects | azuredevops:read | List projects in the organization. |
list_repositories | azuredevops:read | List Git repositories in a project. |
list_work_items | azuredevops:read | Query work items in a project via WIQL. |
get_work_item | azuredevops:read | Get a single work item by ID. |
Each tool's input schema is embedded in the manifest and forwarded verbatim to MCP clients.
Example policy
Restrict work-item access on a sensitive project to members of the security-team group:
package pbac.operator.connectors["identos.azure-devops"]
deny contains msg if {
input.resource.type == "urn:connector:identos:azure-devops"
startswith(input.resource.id, "azuredevops://classified/")
not "security-team" in input.subject.groups
msg := "only security-team may access work items in the classified project"
}
This rule lives in the operator namespace (not this connector's) because "which group is allowed" is tenant-specific.
Manifest reference
- ID:
identos.azure-devops - Version:
1.0.0 - Resource type:
urn:connector:identos:azure-devops - Capabilities:
mcp
Supported auth modes
| Type | Details |
|---|---|
entra_refresh | requires IdP microsoft |
idp_passthrough | requires IdP microsoft |
Setup fields
| ID | Label | Default | Secret? | Notes |
|---|---|---|---|---|
upstream_auth.type | Authentication | entra_refresh | no | Authentication mode. entra_refresh (default) mints a separate per-resource token for each Microsoft service from your one sign-in, so a single login works across any mix of Microsoft connectors — Graph (Mail/OneDrive/Teams) and Azure DevOps together. idp_passthrough instead forwards your Microsoft sign-in token as-is, and that token is valid for only one audience: passthrough therefore works for either Graph (Mail/OneDrive/Teams) or Azure DevOps, not both. Use entra_refresh if you need more than one. |
base_url | Azure DevOps organization URL | — | no | placeholder: https://dev.azure.com/your-org |
Scopes
| Scope |
|---|
azuredevops:read |
Routes
| Method | Pattern | Scope | Resource template |
|---|---|---|---|
GET | /_apis/projects | azuredevops:read | — |
GET | /{project}/_apis/git/repositories | azuredevops:read | azuredevops://{{project}}/repositories |
POST | /{project}/_apis/wit/wiql | azuredevops:read | azuredevops://{{project}}/workitems |
GET | /{project}/_apis/wit/workitems/{id} | azuredevops:read | azuredevops://{{project}}/workitems/{{id}} |
MCP tools
| Name | Scope | Description |
|---|---|---|
list_projects | azuredevops:read | List Azure DevOps projects in the organization. |
list_repositories | azuredevops:read | List Git repositories in a project. |
list_work_items | azuredevops:read | Query work items in a project via WIQL (Work Item Query Language). |
get_work_item | azuredevops:read | Get a single work item by ID. |