Azure DevOps REST API returning HTTP Status 203 when using Python Requests

Question:

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]>
(''rn'

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)

Asked By: FilBot3

||

Answers:

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.

Answered By: FilBot3

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.

Answered By: Kamal Mohamed Ali

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: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
Answered By: S2L