2

I saw this question about Python requests attempting to authenticate to Azure DevOps REST API and receiving HTTP Status 203, Non-Authoritative Information. When viewing the text response, its just the HTML of the login page and not actually logging me in. I used the Authorization: Basic <BASE64 PAT> listed on their REST API page, but it doesn't seem to be working. Here is my code example:

"""Using Python and Requests to interact with Azure DevOps REST API
"""


import base64
import pprint as pp
import requests


with open('ado_pat.txt', 'r') as file:
    PERSONAL_AUTHENTICATION_TOKEN = file.read().replace('\n', '')

PAT_BASE_64 = base64.b64encode(
    b'{PERSONAL_AUTHENTICATION_TOKEN}').decode('ascii')
COLLECTION = 'collection_name'
ORGANIZATION_URL = f'https://dev.azure.com/{COLLECTION}'
RESOURCE_PATH = '/_apis/projects?api-version=5.1'
HEADERS = {
    'Authorization': f'Basic {PAT_BASE_64}',
    'Accept': 'application/json'
}

try:
    ADO_RESPONSE = requests.get(
        ORGANIZATION_URL + RESOURCE_PATH, headers=HEADERS)

    pp.pprint(ADO_RESPONSE)
    pp.pprint(ADO_RESPONSE.text)
    ADO_RESPONSE.raise_for_status()
except requests.exceptions.HTTPError as err:
    pp.pprint(err)

This is the response that I get:

<Response [203]>
(''\r\n'

Then it displays the whole login page. I would use the microsoft/azure-devops-python-api but I don't really understand or see the methods I can call nor really understand how that works.

--EDIT WORKING EXAMPLE --

This example works now.

"""Using Python and Requests to interact with Azure DevOps REST API
"""


import base64
import pprint as pp
import requests


with open('ado_pat.txt', 'r') as file:
    PERSONAL_AUTHENTICATION_TOKEN = file.read().replace('\n', '')

USERNAME = ""
USER_PASS = USERNAME + ":" + PERSONAL_AUTHENTICATION_TOKEN
B64USERPASS = base64.b64encode(USER_PASS.encode()).decode()

COLLECTION = 'collection_name'
ORGANIZATION_URL = f'https://dev.azure.com/{COLLECTION}'
RESOURCE_PATH = '/_apis/projects?api-version=5.1'
HEADERS = {
    'Authorization': 'Basic %s' % B64USERPASS
}

try:
    ADO_RESPONSE = requests.get(
        ORGANIZATION_URL + RESOURCE_PATH, headers=HEADERS)

    pp.pprint(ADO_RESPONSE)
    pp.pprint(ADO_RESPONSE.text)
    ADO_RESPONSE.raise_for_status()
except requests.exceptions.HTTPError as err:
    pp.pprint(err)

FilBot3
  • 3,460
  • 6
  • 33
  • 55
  • 1
    Does this answer your question? [Using a personal access token for azure devops API repository manipulation](https://stackoverflow.com/questions/58592919/using-a-personal-access-token-for-azure-devops-api-repository-manipulation) – Shayki Abramczyk Oct 29 '19 at 20:38
  • I'll update my code to the working version. What I really got is that the M$ example didn't actually work, and isn't very good. – FilBot3 Oct 29 '19 at 21:00
  • Thanks for sharing your solution here, you could Accept it as an Answer , so it could help other community members who get the same issues and we could archive this thread, thanks.https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work – Frank Wang-MSFT Nov 01 '19 at 07:37
  • Accepted. I had looked at it before but I needed to wait longer than other things came up. – FilBot3 Nov 01 '19 at 13:20

4 Answers4

4

Using a comment's link, I was able to get the code above working. This is the end result working code.

"""Using Python and Requests to interact with Azure DevOps REST API
"""


import base64
import json
import pprint as pp
import requests


with open('ado_pat.txt', 'r') as file:
    PERSONAL_AUTHENTICATION_TOKEN = file.read().replace('\n', '')

USERNAME = ""
USER_PASS = USERNAME + ":" + PERSONAL_AUTHENTICATION_TOKEN
B64USERPASS = base64.b64encode(USER_PASS.encode()).decode()

COLLECTION = 'collection_name'
ORGANIZATION_URL = f'https://dev.azure.com/{COLLECTION}'
RESOURCE_PATH = '/_apis/securitynamespaces?api-version=5.1'
HEADERS = {
    'Authorization': 'Basic %s' % B64USERPASS
}

try:
    ADO_RESPONSE = requests.get(
        ORGANIZATION_URL + RESOURCE_PATH, headers=HEADERS)

    pp.pprint(ADO_RESPONSE)
    pp.pprint(ADO_RESPONSE.text)
    ADO_RESPONSE.raise_for_status()
except requests.exceptions.HTTPError as err:
    pp.pprint(err)


with open('output.json', 'w') as file:
    file.write(json.dumps(ADO_RESPONSE.json(), indent=4))

It seems that the API docs didn't specify how to encode the PAT properly and needed to be reworked.

FilBot3
  • 3,460
  • 6
  • 33
  • 55
1

you can also do this

import requests
from requests.auth import HTTPBasicAuth

requests.post(
url=your_link, 
auth=HTTPBasicAuth("", PAT),
headers=headers
)
gufadgarn
  • 11
  • 1
0

In case this helps someone because I have been struggling for hours, I was running the code from GitLab runners and pulling the PAT from the CI/CD variables. It turns out, when variables are set to protected and/or masked, this messes up the encoding and the PAT becomes incorrectly encoded. Disabling the protection and masking fixed it for me.

0

Below is the way to do it via Rest Client vscode extension. My mistake for 203 error, was using Bearer authz instead of Basic.

@org = MyCompany
@authz = Basic My_Personal_Access_Token_Base64_with_colon_prefix

GET https://dev.azure.com/{{org}}/_apis/projects
Authorization: {{authz}}
Content-Type: application/json

Bash script inferred from ado website to generate colon-prefix base64 of PAT:

pat=ThePersonalAccessTokenAsFromADO
b64pat=$(printf "%s"":$pat" | base64)
echo $b64pat
S2L
  • 1,746
  • 1
  • 16
  • 20