0

I have Python code which uses a PAT token to authorize with Azure Devops.

On a high level, the Python file extracts header from a location within the repo, authenticated to Azure Devops via PAT tokens, and copies those headers to Azure Devops Wiki.

I am trying to explore using Service Principal to establish authentication between Python and Azure Devops.

I have the spn client_id, tenant_id and the secret with me. How can I generate a bearer token after I authenticate using the above credentials?

Expecting to generate a bearer token which I can use in place of my PAT token.

Rajesh Mopati
  • 1,329
  • 1
  • 2
  • 7
vedant
  • 1
  • 1

2 Answers2

0

I registered one Azure AD application and added DevOps API permission as below:

enter image description here

You need to use Delegated flow like authorization code flow, username password flow etc… with scope as 499b84ac-1321-427f-aa17-267ca6975798/.default to generate bearer token for Azure DevOps.

Authorization code flow:

Initially, I ran below authorization request in browser and got code value in address bar successfully like below:

https://login.microsoftonline.com/Tenant_Id/oauth2/v2.0/authorize
?client_id=Client_ID
&response_type=code
&redirect_uri=https://jwt.ms
&response_mode=query
&scope= 499b84ac-1321-427f-aa17-267ca6975798/.default
&state=12345

enter image description here

I ran below python code to generate bearer token for Azure DevOps like this:

import urllib3
urllib3.disable_warnings()
uri = "https://login.microsoftonline.com/tenantID/oauth2/v2.0/token"
payload= {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Host': 'login.microsoftonline.com',
    'client_id': 'appID',
    'scope': '499b84ac-1321-427f-aa17-267ca6975798/.default',
    'client_secret': 'enter_secret',
    'grant_type': 'authorization_code',
    'code': 'paste_code_value_from_above_step',
    'redirect_uri':'https://jwt.ms'
}

http = urllib3.PoolManager()
response = http.request('POST', uri, payload)

print(response.data)

my_dict = eval(response.data)
token = my_dict['access_token']
print(token)

Response:

enter image description here

To confirm that, you can decode the token in jwt.ms and check aud and scp claims like below:

enter image description here

To generate bearer token with username password flow, you can make use of below python code:

import urllib3
urllib3.disable_warnings()
uri = "https://login.microsoftonline.com/tenantID/oauth2/v2.0/token"
payload= {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Host': 'login.microsoftonline.com',
    'client_id': 'appID',
    'scope': '499b84ac-1321-427f-aa17-267ca6975798/.default',
    'client_secret': 'secret',
    'grant_type': 'password',
    'username':'nmo@xxxxxxxxxx.onmicrosoft.com',
    'password':'xxxxxxxxxx'
}

http = urllib3.PoolManager()
response = http.request('POST', uri, payload)

print(response.data)

my_dict = eval(response.data)
token = my_dict['access_token']
print(token)

Response:

enter image description here

Rajesh Mopati
  • 1,329
  • 1
  • 2
  • 7
0

I was also in a similar situation and the below python code works for me: You need to add SPN to the organization user list in DevOps with Basic Access Level, and it should have permission (read/contributor) to the project under that organization.

import requests

# Azure AD details
tenant_id = '<tenant id>'
client_id = '<client id>'
client_secret = 'client secret'
scope = '499b84ac-1321-427f-aa17-267ca6975798/.default'

# Obtain an access token
token_url = f'https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token'
token_data = {
    'grant_type': 'client_credentials',
    'client_id': client_id,
    'client_secret': client_secret,
    'scope': scope
}
token_r = requests.post(token_url, data=token_data)
token = token_r.json().get("access_token")


# Call Azure DevOps API
headers = {
    'Authorization': f'Bearer {token}',
    'Content-Type': 'application/json',
}

# Example: Fetch list of projects in an Azure DevOps Organization
organization_name = '<organization name>'
api_url = f'https://dev.azure.com/{organization_name}/_apis/projects'

response = requests.get(api_url, headers=headers)

print(response.text)
Sky
  • 37
  • 5