MCPcopy Index your code
hub / github.com/github/spec-kit / AzureDevOpsAuth

Class AzureDevOpsAuth

src/specify_cli/authentication/azure_devops.py:20–117  ·  view source on GitHub ↗

Azure DevOps authentication provider. Supports four auth schemes: * ``basic-pat`` — PAT with empty username, Base64-encoded as ``: `` * ``bearer`` — pre-acquired OAuth / Azure AD token * ``azure-cli`` — acquires a token via ``az account get-access-token`` * ``azure-ad`` — a

Source from the content-addressed store, hash-verified

18
19
20class AzureDevOpsAuth(AuthProvider):
21 """Azure DevOps authentication provider.
22
23 Supports four auth schemes:
24
25 * ``basic-pat`` — PAT with empty username, Base64-encoded as ``:<PAT>``
26 * ``bearer`` — pre-acquired OAuth / Azure AD token
27 * ``azure-cli`` — acquires a token via ``az account get-access-token``
28 * ``azure-ad`` — acquires a token via OAuth2 client credentials flow
29 """
30
31 key = "azure-devops"
32 supported_auth_schemes = ("basic-pat", "bearer", "azure-cli", "azure-ad")
33
34 def auth_headers(self, token: str, auth_scheme: str) -> dict[str, str]:
35 """Build the ``Authorization`` header for the given scheme."""
36 if auth_scheme == "basic-pat":
37 encoded = base64.b64encode(f":{token}".encode("ascii")).decode("ascii")
38 return {"Authorization": f"Basic {encoded}"}
39 if auth_scheme in ("bearer", "azure-cli", "azure-ad"):
40 return {"Authorization": f"Bearer {token}"}
41 raise ValueError(
42 f"AzureDevOpsAuth does not support auth scheme {auth_scheme!r}"
43 )
44
45 def resolve_token(self, entry: AuthConfigEntry) -> str | None:
46 """Resolve token, with special handling for azure-cli and azure-ad."""
47 if entry.auth == "azure-cli":
48 return self._acquire_via_az_cli()
49 if entry.auth == "azure-ad":
50 return self._acquire_via_client_credentials(entry)
51 return super().resolve_token(entry)
52
53 # -- Token acquisition ------------------------------------------------
54
55 @staticmethod
56 def _acquire_via_az_cli() -> str | None:
57 """Run ``az account get-access-token`` and return the access token."""
58 try:
59 result = subprocess.run( # noqa: S603, S607
60 [
61 "az",
62 "account",
63 "get-access-token",
64 "--resource",
65 _ADO_RESOURCE_ID,
66 "--output",
67 "json",
68 ],
69 capture_output=True,
70 text=True,
71 timeout=30,
72 check=False,
73 )
74 if result.returncode != 0:
75 return None
76 payload = _json.loads(result.stdout)
77 token = payload.get("accessToken", "").strip()

Calls

no outgoing calls