@@ -31,43 +31,52 @@ def get_oidc_token(self, audience: str) -> Optional[str]:
3131class AzureDevOpsOIDCTokenSupplier :
3232 """
3333 Supplies OIDC tokens from Azure DevOps pipelines.
34+
35+ Constructs the OIDC token request URL using official Azure DevOps predefined variables.
36+ See: https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables
3437 """
3538
3639 def get_oidc_token (self , audience : str ) -> Optional [str ]:
37- # Retrieve necessary environment variables
38- access_token = os .environ .get ('SYSTEM_ACCESSTOKEN' )
39- collection_uri = os .environ .get ('SYSTEM_TEAMFOUNDATIONCOLLECTIONURI' )
40- project_id = os .environ .get ('SYSTEM_TEAMPROJECTID' )
41- plan_id = os .environ .get ('SYSTEM_PLANID' )
42- job_id = os .environ .get ('SYSTEM_JOBID' )
43- hub_name = os .environ .get ('SYSTEM_HOSTTYPE' )
40+ # Note: Azure DevOps OIDC tokens have a fixed audience of "api://AzureADTokenExchange"
41+ # The audience parameter is ignored but kept for interface compatibility with other OIDC suppliers
42+
43+ # Check for required Azure DevOps environment variables
44+ access_token = os .environ .get ("SYSTEM_ACCESSTOKEN" )
45+ collection_uri = os .environ .get ("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" )
46+ project_id = os .environ .get ("SYSTEM_TEAMPROJECTID" )
47+ plan_id = os .environ .get ("SYSTEM_PLANID" )
48+ job_id = os .environ .get ("SYSTEM_JOBID" )
49+ hub_name = os .environ .get ("SYSTEM_HOSTTYPE" , "build" ) # Default to "build"
4450
4551 # Check for required variables
4652 if not all ([access_token , collection_uri , project_id , plan_id , job_id ]):
4753 # not in Azure DevOps pipeline
4854 return None
4955
50- # Construct the URL
51- request_url = f"{ collection_uri } { project_id } /_apis/distributedtask/hubs/{ hub_name } /plans/{ plan_id } /jobs/{ job_id } /oidctoken?api-version=7.1-preview.1"
52-
53- # See https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables
54- headers = {
55- "Authorization" : f"Bearer { access_token } " ,
56- "Content-Type" : "application/json"
57- }
58-
59- # Set up the JSON payload for the request body
60- payload = {
61- 'audience' : audience
62- }
6356 try :
64- # Make the POST request
65- response = requests .post (request_url , headers = headers , json = payload )
66- response .raise_for_status () # Raise an exception for bad status codes
57+ # Construct the OIDC token request URL
58+ # Format: {collection_uri}{project_id}/_apis/distributedtask/hubs/{hubName}/plans/{planId}/jobs/{jobId}/oidctoken
59+ request_url = f"{ collection_uri } { project_id } /_apis/distributedtask/hubs/{ hub_name } /plans/{ plan_id } /jobs/{ job_id } /oidctoken"
60+
61+ # Add API version (audience is fixed to "api://AzureADTokenExchange" by Azure DevOps)
62+ endpoint = f"{ request_url } ?api-version=7.2-preview.1"
63+ headers = {
64+ "Authorization" : f"Bearer { access_token } " ,
65+ "Content-Type" : "application/json" ,
66+ "Content-Length" : "0"
67+ }
68+
69+ # Azure DevOps OIDC endpoint requires POST request with empty body
70+ response = requests .post (endpoint , headers = headers )
71+ if not response .ok :
72+ return None
73+
74+ # Azure DevOps returns the token in 'oidcToken' field
75+ response_json = response .json ()
76+ if "oidcToken" not in response_json :
77+ return None
6778
68- # Return the OIDC token from the JSON response
69- return response .json ()['oidcToken' ]
70- except requests .exceptions .RequestException as e :
71- print (f"Error requesting OIDC token: { e } " )
79+ return response_json ["oidcToken" ]
80+ except Exception :
81+ # If any error occurs, return None to fall back to other auth methods
7282 return None
73-
0 commit comments