FilBot3
FilBot3

Reputation: 3708

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

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)

Upvotes: 3

Views: 6088

Answers (4)

gufadgarn
gufadgarn

Reputation: 11

you can also do this

import requests
from requests.auth import HTTPBasicAuth

requests.post(
url=your_link, 
auth=HTTPBasicAuth("", PAT),
headers=headers
)

Upvotes: 1

S2L
S2L

Reputation: 1934

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

Upvotes: 0

Kamal Mohamed Ali
Kamal Mohamed Ali

Reputation: 11

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.

Upvotes: 0

FilBot3
FilBot3

Reputation: 3708

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.

Upvotes: 5

Related Questions