בעבודה עם Azure Devops API מתוך קוד Back End, ה API של Azure Devops צריך לדעת שהיישום שלי מורשה לקבל מידע ועבור איזה משתמש הוא מורשה לקבל את המידע. פיתרון קלאסי לבעיה כזאת הוא OAuth, וזה באמת עובד כשאני כותב אפליקציית ווב, אבל בכתיבת אוטומציה שלא קשורה לממשק גרפי מסוים לא תמיד זה נוח. במקום זה הייתי רוצה לקבל טוקן לצד שרת ותמיד לעבוד איתו.
ב Azure Devops API טוקן כזה כבר לא מונפק ב OAuth אלא בשיטת אימות ישנה יותר שנקראת Basic Auth. ב Basic Auth אנחנו צריכים להעביר שם משתמש וסיסמה בתוך ה Authorization Header של הבקשה, והטוקן משמש בתור הסיסמה. את שם המשתמש משאירים ריק.
כל הסיפור הזה הוא הקדמה לתשובה ב Stack Overflow שמסבירה איך לדבר עם ה API הזה מפייתון:
import requests
import base64
pat = 'tcd******************************tnq'
authorization = str(base64.b64encode(bytes(':'+pat, 'ascii')), 'ascii')
headers = {
'Accept': 'application/json',
'Authorization': 'Basic '+authorization
}
response = requests.get(
url="https://dev.azure.com/jack0503/_apis/projects?api-version=5.1", headers=headers)
print(response.text)
הקוד לא ארוך אבל משאיר שאלה גדולה פתוחה - למה הם הוסיפו נקודותיים לפני הטוקן?
אתם, שקראתם את ההקדמה כבר יודעים. הנקודותיים שם בשביל להפריד בין שם המשתמש הריק לסיסמה (שהיא הטוקן), אבל אני די בטוח שמי שיקרא קוד כזה אחריכם לא ממש יבין על מה מדובר. יכול להיות שזה יוביל לבאגים נוספים, יכול להיות שישכחו לשים את הנקודותיים במקומות אחרים או יכתבו אותם גם איפה שלא צריך. בכל מקרה טוב לא יצא מזה.
בכתיבת קוד אנחנו רוצים לנסות לספר את סיפור הרקע בתוך הקוד במידת האפשר, ואם אי אפשר לצרף הערה. זאת הגירסה שלי לאותו קטע שכבר כן מספרת חלק מסיפור הרקע:
import requests
import base64
def basic_authorization_header(username: str, password: str):
return 'Basic '+str(base64.b64encode(bytes(username + ':'+pat, 'ascii')), 'ascii')
# Personal Access Token received from the web UI
pat = 'tcd******************************tnq'
headers = {
'Accept': 'application/json',
'Authorization': basic_authorization_header(username='', password=pat)
}
response = requests.get(
url="https://dev.azure.com/jack0503/_apis/projects?api-version=5.1", headers=headers)
print(response.text)
נ.ב. כמובן שלא כדאי לשמור את הטוקן בתוך הקוד ויש לקרוא אותו ממשתנה סביבה, אבל זה לא חשוב לסיפור שלנו אז השארתי כמו שמצאתי אותו.
נ.ב.ב. כמובן של requests יש תמיכה מובנית ב Basic Auth ולפעמים שווה להשתמש בה. היתרון של הכתיבה המפורשת של ה Headers הוא שכך יותר קל לתרגם את הקוד גם לשפות אחרות אם יהיה צורך.